From adfe5792c4cb465ba8409e2bbfc1754f51e9ee50 Mon Sep 17 00:00:00 2001 From: Xavier Date: Wed, 24 Aug 2022 07:59:16 +0200 Subject: [PATCH] first commit --- COPYING.md | 675 + LICENSE | 674 + README.md | 10 + bom.png | Bin 0 -> 95615 bytes config.txt | 63 + k3ng-keyer-schematic-2012052101.png | Bin 0 -> 22852 bytes k3ng_keyer/k3ng_keyer.ino | 22650 ++++++++++++++++ k3ng_keyer/keyer.h | 226 + k3ng_keyer/keyer_callsign_prefixes.h | 414 + k3ng_keyer/keyer_debug.h | 63 + k3ng_keyer/keyer_dependencies.h | 49 + k3ng_keyer/keyer_features_and_options.h | 134 + k3ng_keyer/keyer_features_and_options_fk_10.h | 148 + k3ng_keyer/keyer_features_and_options_fk_11.h | 135 + ..._features_and_options_generic_STM32F103C.h | 135 + .../keyer_features_and_options_iz3gme.h | 131 + k3ng_keyer/keyer_features_and_options_k5bcq.h | 130 + .../keyer_features_and_options_maple_mini.h | 143 + .../keyer_features_and_options_megakeyer.h | 135 + .../keyer_features_and_options_mortty.h | 122 + ...eyer_features_and_options_mortty_regular.h | 119 + ...ptions_mortty_regular_with_potentiometer.h | 119 + .../keyer_features_and_options_mortty_so2r.h | 119 + ...d_options_mortty_so2r_with_potentiometer.h | 120 + ...yer_features_and_options_nanokeyer_rev_b.h | 113 + ...yer_features_and_options_nanokeyer_rev_d.h | 113 + ...eyer_features_and_options_open_interface.h | 117 + ...yer_features_and_options_opencwkeyer_mk2.h | 131 + k3ng_keyer/keyer_features_and_options_test.h | 187 + ...yer_features_and_options_test_everything.h | 145 + .../keyer_features_and_options_tinykeyer.h | 95 + .../keyer_features_and_options_yaacwk.h | 126 + ...eyer_features_and_options_yccc_so2r_mini.h | 140 + k3ng_keyer/keyer_hardware.h | 97 + k3ng_keyer/keyer_pin_settings.h | 161 + k3ng_keyer/keyer_pin_settings_fk_10.h | 142 + k3ng_keyer/keyer_pin_settings_fk_11.h | 164 + .../keyer_pin_settings_generic_STM32F103C.h | 169 + k3ng_keyer/keyer_pin_settings_iz3gme.h | 161 + k3ng_keyer/keyer_pin_settings_k5bcq.h | 161 + k3ng_keyer/keyer_pin_settings_maple_mini.h | 176 + k3ng_keyer/keyer_pin_settings_megakeyer.h | 162 + k3ng_keyer/keyer_pin_settings_mortty.h | 162 + .../keyer_pin_settings_mortty_regular.h | 157 + ...ttings_mortty_regular_with_potentiometer.h | 155 + k3ng_keyer/keyer_pin_settings_mortty_so2r.h | 155 + ..._settings_mortty_so2r_with_potentiometer.h | 155 + .../keyer_pin_settings_nanokeyer_rev_b.h | 96 + .../keyer_pin_settings_nanokeyer_rev_d.h | 74 + .../keyer_pin_settings_open_interface.h | 143 + .../keyer_pin_settings_opencwkeyer_mk2.h | 163 + k3ng_keyer/keyer_pin_settings_test.h | 185 + .../keyer_pin_settings_test_everything.h | 199 + k3ng_keyer/keyer_pin_settings_tinykeyer.h | 94 + k3ng_keyer/keyer_pin_settings_yaacwk.h | 143 + .../keyer_pin_settings_yccc_so2r_mini.h | 176 + k3ng_keyer/keyer_settings.h | 316 + k3ng_keyer/keyer_settings_fk_10.h | 338 + k3ng_keyer/keyer_settings_fk_11.h | 318 + .../keyer_settings_generic_STM32F103C.h | 324 + k3ng_keyer/keyer_settings_iz3gme.h | 316 + k3ng_keyer/keyer_settings_k5bcq.h | 315 + k3ng_keyer/keyer_settings_maple_mini.h | 329 + k3ng_keyer/keyer_settings_megakeyer.h | 321 + k3ng_keyer/keyer_settings_mortty.h | 318 + k3ng_keyer/keyer_settings_mortty_regular.h | 318 + ...ttings_mortty_regular_with_potentiometer.h | 318 + k3ng_keyer/keyer_settings_mortty_so2r.h | 318 + ..._settings_mortty_so2r_with_potentiometer.h | 318 + k3ng_keyer/keyer_settings_nanokeyer_rev_b.h | 309 + k3ng_keyer/keyer_settings_nanokeyer_rev_d.h | 310 + k3ng_keyer/keyer_settings_open_interface.h | 312 + k3ng_keyer/keyer_settings_opencwkeyer_mk2.h | 316 + k3ng_keyer/keyer_settings_test.h | 652 + k3ng_keyer/keyer_settings_test_everything.h | 361 + k3ng_keyer/keyer_settings_tinykeyer.h | 310 + k3ng_keyer/keyer_settings_yaacwk.h | 317 + k3ng_keyer/keyer_settings_yccc_so2r_mini.h | 351 + k3ng_keyer/keyer_stm32duino.h | 39 + k3ng_keyer/keyer_training_text_czech.h | 266 + k3ng_keyer/keyer_training_text_english.h | 264 + k3ng_keyer/keyer_training_text_norsk.h | 248 + k3ng_keyer/src/buttonarray/buttonarray.cpp | 151 + k3ng_keyer/src/buttonarray/buttonarray.h | 59 + k3ng_keyer/src/buttonarray/buttonarray.ino | 50 + keyer.png | Bin 0 -> 153496 bytes .../Adafruit_LiquidCrystal.cpp | 453 + .../Adafruit_LiquidCrystal.h | 124 + libraries/Adafruit_LiquidCrystal/README.md | 37 + libraries/Adafruit_LiquidCrystal/keywords.txt | 38 + .../Adafruit_LiquidCrystal/library.properties | 9 + .../utility/Adafruit_MCP23008.cpp | 175 + .../utility/Adafruit_MCP23008.h | 50 + .../Adafruit_MCP23017/Adafruit_MCP23017.cpp | 230 + .../Adafruit_MCP23017/Adafruit_MCP23017.h | 63 + .../Adafruit_RGBLCDShield.cpp | 431 + .../Adafruit_RGBLCDShield.h | 132 + libraries/BasicTerm/BasicTerm.cpp | 137 + libraries/BasicTerm/BasicTerm.h | 60 + libraries/E24C1024/E24C1024.cpp | 84 + libraries/E24C1024/E24C1024.h | 68 + libraries/Goertzel/goertzel.cpp | 136 + libraries/Goertzel/goertzel.h | 81 + .../K3NG_PS2Keyboard/K3NG_PS2Keyboard.cpp | 538 + libraries/K3NG_PS2Keyboard/K3NG_PS2Keyboard.h | 538 + libraries/K3NG_PS2Keyboard/library.properties | 10 + libraries/Keypad/Keypad.cpp | 256 + libraries/Keypad/Keypad.h | 135 + libraries/NewTone/NewTone.cpp | 49 + libraries/NewTone/NewTone.h | 50 + libraries/NewTone/keywords.txt | 18 + libraries/README.md | 1 + libraries/USB_Host_Shield/.gitattributes | 23 + libraries/USB_Host_Shield/.gitignore | 4 + libraries/USB_Host_Shield/.gitmodules | 12 + libraries/USB_Host_Shield/BTD.cpp | 1363 + libraries/USB_Host_Shield/BTD.h | 618 + libraries/USB_Host_Shield/BTHID.cpp | 399 + libraries/USB_Host_Shield/BTHID.h | 155 + libraries/USB_Host_Shield/PS3BT.cpp | 634 + libraries/USB_Host_Shield/PS3BT.h | 240 + libraries/USB_Host_Shield/PS3Enums.h | 141 + libraries/USB_Host_Shield/PS3USB.cpp | 572 + libraries/USB_Host_Shield/PS3USB.h | 303 + libraries/USB_Host_Shield/PS4BT.h | 121 + libraries/USB_Host_Shield/PS4Parser.cpp | 109 + libraries/USB_Host_Shield/PS4Parser.h | 407 + libraries/USB_Host_Shield/PS4USB.h | 130 + libraries/USB_Host_Shield/PSBuzz.cpp | 82 + libraries/USB_Host_Shield/PSBuzz.h | 185 + libraries/USB_Host_Shield/README.md | 325 + libraries/USB_Host_Shield/SPP.cpp | 829 + libraries/USB_Host_Shield/SPP.h | 225 + libraries/USB_Host_Shield/Usb.cpp | 812 + libraries/USB_Host_Shield/Usb.h | 41 + libraries/USB_Host_Shield/UsbCore.h | 298 + libraries/USB_Host_Shield/Wii.cpp | 1199 + libraries/USB_Host_Shield/Wii.h | 478 + libraries/USB_Host_Shield/WiiCameraReadme.md | 13 + libraries/USB_Host_Shield/XBOXOLD.cpp | 337 + libraries/USB_Host_Shield/XBOXOLD.h | 185 + libraries/USB_Host_Shield/XBOXRECV.cpp | 583 + libraries/USB_Host_Shield/XBOXRECV.h | 276 + libraries/USB_Host_Shield/XBOXUSB.cpp | 361 + libraries/USB_Host_Shield/XBOXUSB.h | 225 + libraries/USB_Host_Shield/address.h | 282 + libraries/USB_Host_Shield/adk.cpp | 371 + libraries/USB_Host_Shield/adk.h | 140 + libraries/USB_Host_Shield/avrpins.h | 1067 + libraries/USB_Host_Shield/cdc_XR21B1411.cpp | 211 + libraries/USB_Host_Shield/cdc_XR21B1411.h | 272 + libraries/USB_Host_Shield/cdcacm.cpp | 331 + libraries/USB_Host_Shield/cdcacm.h | 252 + libraries/USB_Host_Shield/cdcftdi.cpp | 334 + libraries/USB_Host_Shield/cdcftdi.h | 143 + libraries/USB_Host_Shield/cdcprolific.cpp | 210 + libraries/USB_Host_Shield/cdcprolific.h | 135 + libraries/USB_Host_Shield/confdescparser.h | 213 + libraries/USB_Host_Shield/controllerEnums.h | 204 + .../examples/Bluetooth/BTHID/BTHID.ino | 54 + .../examples/Bluetooth/BTHID/KeyboardParser.h | 105 + .../examples/Bluetooth/BTHID/MouseParser.h | 46 + .../examples/Bluetooth/PS3BT/PS3BT.ino | 187 + .../examples/Bluetooth/PS3Multi/PS3Multi.ino | 148 + .../examples/Bluetooth/PS3SPP/PS3SPP.ino | 161 + .../examples/Bluetooth/PS4BT/PS4BT.ino | 146 + .../examples/Bluetooth/SPP/SPP.ino | 51 + .../examples/Bluetooth/SPPMulti/SPPMulti.ino | 66 + .../examples/Bluetooth/Wii/Wii.ino | 117 + .../Bluetooth/WiiIRCamera/WiiIRCamera.ino | 132 + .../examples/Bluetooth/WiiMulti/WiiMulti.ino | 131 + .../WiiUProController/WiiUProController.ino | 103 + .../HID/USBHIDBootKbd/USBHIDBootKbd.ino | 130 + .../USBHIDBootKbdAndMouse.ino | 177 + .../HID/USBHIDBootMouse/USBHIDBootMouse.ino | 82 + .../HID/USBHIDJoystick/USBHIDJoystick.ino | 37 + .../USBHIDJoystick/hidjoystickrptparser.cpp | 84 + .../HID/USBHIDJoystick/hidjoystickrptparser.h | 33 + .../examples/HID/USBHID_desc/USBHID_desc.ino | 76 + .../examples/HID/USBHID_desc/pgmstrings.h | 52 + .../examples/HID/le3dp/le3dp.ino | 41 + .../examples/HID/le3dp/le3dp_rptparser.cpp | 43 + .../examples/HID/le3dp/le3dp_rptparser.h | 42 + .../examples/HID/scale/scale.ino | 50 + .../examples/HID/scale/scale_rptparser.cpp | 150 + .../examples/HID/scale/scale_rptparser.h | 55 + .../examples/PS3USB/PS3USB.ino | 147 + .../examples/PS4USB/PS4USB.ino | 133 + .../examples/PSBuzz/PSBuzz.ino | 49 + .../examples/USB_desc/USB_desc.ino | 348 + .../examples/USB_desc/pgmstrings.h | 52 + .../examples/Xbox/XBOXOLD/XBOXOLD.ino | 109 + .../examples/Xbox/XBOXRECV/XBOXRECV.ino | 121 + .../examples/Xbox/XBOXUSB/XBOXUSB.ino | 112 + .../acm/acm_terminal/acm_terminal.ino | 100 + .../examples/acm/acm_terminal/pgmstrings.h | 52 + .../adk/ArduinoBlinkLED/ArduinoBlinkLED.ino | 89 + .../examples/adk/adk_barcode/adk_barcode.ino | 90 + .../examples/adk/demokit_20/demokit_20.ino | 102 + .../examples/adk/term_test/term_test.ino | 64 + .../examples/adk/term_time/term_time.ino | 49 + .../examples/board_qc/board_qc.ino | 258 + .../cdc_XR21B1411/XR_terminal/XR_terminal.ino | 83 + .../ftdi/USBFTDILoopback/USBFTDILoopback.ino | 97 + .../ftdi/USBFTDILoopback/pgmstrings.h | 52 + .../examples/hub_demo/hub_demo.ino | 344 + .../examples/hub_demo/pgmstrings.h | 52 + .../examples/max_LCD/max_LCD.ino | 28 + .../pl2303_gprs_terminal.ino | 100 + .../examples/pl2303/pl2303_gps/pl2303_gps.ino | 87 + .../pl2303/pl2303_tinygps/pl2303_tinygps.ino | 216 + .../pl2303_xbee_terminal.ino | 116 + .../examples/testusbhostFAT/Makefile | 58 + .../examples/testusbhostFAT/README.md | 29 + .../testusbhostFAT/testusbhostFAT.ino | 721 + libraries/USB_Host_Shield/gpl2.txt | 340 + libraries/USB_Host_Shield/hexdump.h | 61 + libraries/USB_Host_Shield/hid.cpp | 112 + libraries/USB_Host_Shield/hid.h | 184 + libraries/USB_Host_Shield/hidboot.cpp | 201 + libraries/USB_Host_Shield/hidboot.h | 618 + .../USB_Host_Shield/hidescriptorparser.cpp | 1588 ++ .../USB_Host_Shield/hidescriptorparser.h | 176 + libraries/USB_Host_Shield/hiduniversal.cpp | 425 + libraries/USB_Host_Shield/hiduniversal.h | 108 + libraries/USB_Host_Shield/hidusagestr.h | 977 + .../USB_Host_Shield/hidusagetitlearrays.cpp | 1048 + libraries/USB_Host_Shield/keywords.txt | 361 + libraries/USB_Host_Shield/library.json | 46 + libraries/USB_Host_Shield/macros.h | 82 + libraries/USB_Host_Shield/masstorage.cpp | 1266 + libraries/USB_Host_Shield/masstorage.h | 571 + libraries/USB_Host_Shield/max3421e.h | 228 + libraries/USB_Host_Shield/max_LCD.cpp | 255 + libraries/USB_Host_Shield/max_LCD.h | 106 + libraries/USB_Host_Shield/message.cpp | 116 + libraries/USB_Host_Shield/message.h | 78 + libraries/USB_Host_Shield/parsetools.cpp | 67 + libraries/USB_Host_Shield/parsetools.h | 140 + libraries/USB_Host_Shield/printhex.h | 84 + libraries/USB_Host_Shield/settings.h | 138 + libraries/USB_Host_Shield/sink_parser.h | 41 + libraries/USB_Host_Shield/usb_ch9.h | 166 + libraries/USB_Host_Shield/usbhost.h | 467 + libraries/USB_Host_Shield/usbhub.cpp | 425 + libraries/USB_Host_Shield/usbhub.h | 252 + libraries/USB_Host_Shield/version_helper.h | 222 + libraries/USB_Host_Shield/xboxEnums.h | 65 + pcb.png | Bin 0 -> 124206 bytes .../keyer_features_and_options_yaacwk.h | 102 + .../keyer_pin_settings_yaacwk.h | 143 + .../so2r/PS2_WINKEY_Atmega644P/default.txt | 1 + .../keyer_features_and_options_yaacwk.h | 102 + .../keyer_pin_settings_yaacwk.h | 143 + .../keyer_features_and_options_yaacwk.h | 103 + .../keyer_pin_settings_yaacwk.h | 143 + .../keyer_features_and_options_yaacwk.h | 102 + .../keyer_pin_settings_yaacwk.h | 143 + schema.png | Bin 0 -> 136999 bytes 259 files changed, 77719 insertions(+) create mode 100755 COPYING.md create mode 100755 LICENSE create mode 100755 README.md create mode 100755 bom.png create mode 100755 config.txt create mode 100755 k3ng-keyer-schematic-2012052101.png create mode 100755 k3ng_keyer/k3ng_keyer.ino create mode 100755 k3ng_keyer/keyer.h create mode 100755 k3ng_keyer/keyer_callsign_prefixes.h create mode 100755 k3ng_keyer/keyer_debug.h create mode 100755 k3ng_keyer/keyer_dependencies.h create mode 100755 k3ng_keyer/keyer_features_and_options.h create mode 100755 k3ng_keyer/keyer_features_and_options_fk_10.h create mode 100755 k3ng_keyer/keyer_features_and_options_fk_11.h create mode 100755 k3ng_keyer/keyer_features_and_options_generic_STM32F103C.h create mode 100755 k3ng_keyer/keyer_features_and_options_iz3gme.h create mode 100755 k3ng_keyer/keyer_features_and_options_k5bcq.h create mode 100755 k3ng_keyer/keyer_features_and_options_maple_mini.h create mode 100755 k3ng_keyer/keyer_features_and_options_megakeyer.h create mode 100755 k3ng_keyer/keyer_features_and_options_mortty.h create mode 100755 k3ng_keyer/keyer_features_and_options_mortty_regular.h create mode 100755 k3ng_keyer/keyer_features_and_options_mortty_regular_with_potentiometer.h create mode 100755 k3ng_keyer/keyer_features_and_options_mortty_so2r.h create mode 100755 k3ng_keyer/keyer_features_and_options_mortty_so2r_with_potentiometer.h create mode 100755 k3ng_keyer/keyer_features_and_options_nanokeyer_rev_b.h create mode 100755 k3ng_keyer/keyer_features_and_options_nanokeyer_rev_d.h create mode 100755 k3ng_keyer/keyer_features_and_options_open_interface.h create mode 100755 k3ng_keyer/keyer_features_and_options_opencwkeyer_mk2.h create mode 100755 k3ng_keyer/keyer_features_and_options_test.h create mode 100755 k3ng_keyer/keyer_features_and_options_test_everything.h create mode 100755 k3ng_keyer/keyer_features_and_options_tinykeyer.h create mode 100755 k3ng_keyer/keyer_features_and_options_yaacwk.h create mode 100755 k3ng_keyer/keyer_features_and_options_yccc_so2r_mini.h create mode 100755 k3ng_keyer/keyer_hardware.h create mode 100755 k3ng_keyer/keyer_pin_settings.h create mode 100755 k3ng_keyer/keyer_pin_settings_fk_10.h create mode 100755 k3ng_keyer/keyer_pin_settings_fk_11.h create mode 100755 k3ng_keyer/keyer_pin_settings_generic_STM32F103C.h create mode 100755 k3ng_keyer/keyer_pin_settings_iz3gme.h create mode 100755 k3ng_keyer/keyer_pin_settings_k5bcq.h create mode 100755 k3ng_keyer/keyer_pin_settings_maple_mini.h create mode 100755 k3ng_keyer/keyer_pin_settings_megakeyer.h create mode 100755 k3ng_keyer/keyer_pin_settings_mortty.h create mode 100755 k3ng_keyer/keyer_pin_settings_mortty_regular.h create mode 100755 k3ng_keyer/keyer_pin_settings_mortty_regular_with_potentiometer.h create mode 100755 k3ng_keyer/keyer_pin_settings_mortty_so2r.h create mode 100755 k3ng_keyer/keyer_pin_settings_mortty_so2r_with_potentiometer.h create mode 100755 k3ng_keyer/keyer_pin_settings_nanokeyer_rev_b.h create mode 100755 k3ng_keyer/keyer_pin_settings_nanokeyer_rev_d.h create mode 100755 k3ng_keyer/keyer_pin_settings_open_interface.h create mode 100755 k3ng_keyer/keyer_pin_settings_opencwkeyer_mk2.h create mode 100755 k3ng_keyer/keyer_pin_settings_test.h create mode 100755 k3ng_keyer/keyer_pin_settings_test_everything.h create mode 100755 k3ng_keyer/keyer_pin_settings_tinykeyer.h create mode 100755 k3ng_keyer/keyer_pin_settings_yaacwk.h create mode 100755 k3ng_keyer/keyer_pin_settings_yccc_so2r_mini.h create mode 100755 k3ng_keyer/keyer_settings.h create mode 100755 k3ng_keyer/keyer_settings_fk_10.h create mode 100755 k3ng_keyer/keyer_settings_fk_11.h create mode 100755 k3ng_keyer/keyer_settings_generic_STM32F103C.h create mode 100755 k3ng_keyer/keyer_settings_iz3gme.h create mode 100755 k3ng_keyer/keyer_settings_k5bcq.h create mode 100755 k3ng_keyer/keyer_settings_maple_mini.h create mode 100755 k3ng_keyer/keyer_settings_megakeyer.h create mode 100755 k3ng_keyer/keyer_settings_mortty.h create mode 100755 k3ng_keyer/keyer_settings_mortty_regular.h create mode 100755 k3ng_keyer/keyer_settings_mortty_regular_with_potentiometer.h create mode 100755 k3ng_keyer/keyer_settings_mortty_so2r.h create mode 100755 k3ng_keyer/keyer_settings_mortty_so2r_with_potentiometer.h create mode 100755 k3ng_keyer/keyer_settings_nanokeyer_rev_b.h create mode 100755 k3ng_keyer/keyer_settings_nanokeyer_rev_d.h create mode 100755 k3ng_keyer/keyer_settings_open_interface.h create mode 100755 k3ng_keyer/keyer_settings_opencwkeyer_mk2.h create mode 100755 k3ng_keyer/keyer_settings_test.h create mode 100755 k3ng_keyer/keyer_settings_test_everything.h create mode 100755 k3ng_keyer/keyer_settings_tinykeyer.h create mode 100755 k3ng_keyer/keyer_settings_yaacwk.h create mode 100755 k3ng_keyer/keyer_settings_yccc_so2r_mini.h create mode 100755 k3ng_keyer/keyer_stm32duino.h create mode 100755 k3ng_keyer/keyer_training_text_czech.h create mode 100755 k3ng_keyer/keyer_training_text_english.h create mode 100755 k3ng_keyer/keyer_training_text_norsk.h create mode 100755 k3ng_keyer/src/buttonarray/buttonarray.cpp create mode 100755 k3ng_keyer/src/buttonarray/buttonarray.h create mode 100755 k3ng_keyer/src/buttonarray/buttonarray.ino create mode 100755 keyer.png create mode 100755 libraries/Adafruit_LiquidCrystal/Adafruit_LiquidCrystal.cpp create mode 100755 libraries/Adafruit_LiquidCrystal/Adafruit_LiquidCrystal.h create mode 100755 libraries/Adafruit_LiquidCrystal/README.md create mode 100755 libraries/Adafruit_LiquidCrystal/keywords.txt create mode 100755 libraries/Adafruit_LiquidCrystal/library.properties create mode 100755 libraries/Adafruit_LiquidCrystal/utility/Adafruit_MCP23008.cpp create mode 100755 libraries/Adafruit_LiquidCrystal/utility/Adafruit_MCP23008.h create mode 100755 libraries/Adafruit_MCP23017/Adafruit_MCP23017.cpp create mode 100755 libraries/Adafruit_MCP23017/Adafruit_MCP23017.h create mode 100755 libraries/Adafruit_RGBLCDShield/Adafruit_RGBLCDShield.cpp create mode 100755 libraries/Adafruit_RGBLCDShield/Adafruit_RGBLCDShield.h create mode 100755 libraries/BasicTerm/BasicTerm.cpp create mode 100755 libraries/BasicTerm/BasicTerm.h create mode 100755 libraries/E24C1024/E24C1024.cpp create mode 100755 libraries/E24C1024/E24C1024.h create mode 100755 libraries/Goertzel/goertzel.cpp create mode 100755 libraries/Goertzel/goertzel.h create mode 100755 libraries/K3NG_PS2Keyboard/K3NG_PS2Keyboard.cpp create mode 100755 libraries/K3NG_PS2Keyboard/K3NG_PS2Keyboard.h create mode 100755 libraries/K3NG_PS2Keyboard/library.properties create mode 100755 libraries/Keypad/Keypad.cpp create mode 100755 libraries/Keypad/Keypad.h create mode 100755 libraries/NewTone/NewTone.cpp create mode 100755 libraries/NewTone/NewTone.h create mode 100755 libraries/NewTone/keywords.txt create mode 100755 libraries/README.md create mode 100755 libraries/USB_Host_Shield/.gitattributes create mode 100755 libraries/USB_Host_Shield/.gitignore create mode 100755 libraries/USB_Host_Shield/.gitmodules create mode 100755 libraries/USB_Host_Shield/BTD.cpp create mode 100755 libraries/USB_Host_Shield/BTD.h create mode 100755 libraries/USB_Host_Shield/BTHID.cpp create mode 100755 libraries/USB_Host_Shield/BTHID.h create mode 100755 libraries/USB_Host_Shield/PS3BT.cpp create mode 100755 libraries/USB_Host_Shield/PS3BT.h create mode 100755 libraries/USB_Host_Shield/PS3Enums.h create mode 100755 libraries/USB_Host_Shield/PS3USB.cpp create mode 100755 libraries/USB_Host_Shield/PS3USB.h create mode 100755 libraries/USB_Host_Shield/PS4BT.h create mode 100755 libraries/USB_Host_Shield/PS4Parser.cpp create mode 100755 libraries/USB_Host_Shield/PS4Parser.h create mode 100755 libraries/USB_Host_Shield/PS4USB.h create mode 100755 libraries/USB_Host_Shield/PSBuzz.cpp create mode 100755 libraries/USB_Host_Shield/PSBuzz.h create mode 100755 libraries/USB_Host_Shield/README.md create mode 100755 libraries/USB_Host_Shield/SPP.cpp create mode 100755 libraries/USB_Host_Shield/SPP.h create mode 100755 libraries/USB_Host_Shield/Usb.cpp create mode 100755 libraries/USB_Host_Shield/Usb.h create mode 100755 libraries/USB_Host_Shield/UsbCore.h create mode 100755 libraries/USB_Host_Shield/Wii.cpp create mode 100755 libraries/USB_Host_Shield/Wii.h create mode 100755 libraries/USB_Host_Shield/WiiCameraReadme.md create mode 100755 libraries/USB_Host_Shield/XBOXOLD.cpp create mode 100755 libraries/USB_Host_Shield/XBOXOLD.h create mode 100755 libraries/USB_Host_Shield/XBOXRECV.cpp create mode 100755 libraries/USB_Host_Shield/XBOXRECV.h create mode 100755 libraries/USB_Host_Shield/XBOXUSB.cpp create mode 100755 libraries/USB_Host_Shield/XBOXUSB.h create mode 100755 libraries/USB_Host_Shield/address.h create mode 100755 libraries/USB_Host_Shield/adk.cpp create mode 100755 libraries/USB_Host_Shield/adk.h create mode 100755 libraries/USB_Host_Shield/avrpins.h create mode 100755 libraries/USB_Host_Shield/cdc_XR21B1411.cpp create mode 100755 libraries/USB_Host_Shield/cdc_XR21B1411.h create mode 100755 libraries/USB_Host_Shield/cdcacm.cpp create mode 100755 libraries/USB_Host_Shield/cdcacm.h create mode 100755 libraries/USB_Host_Shield/cdcftdi.cpp create mode 100755 libraries/USB_Host_Shield/cdcftdi.h create mode 100755 libraries/USB_Host_Shield/cdcprolific.cpp create mode 100755 libraries/USB_Host_Shield/cdcprolific.h create mode 100755 libraries/USB_Host_Shield/confdescparser.h create mode 100755 libraries/USB_Host_Shield/controllerEnums.h create mode 100755 libraries/USB_Host_Shield/examples/Bluetooth/BTHID/BTHID.ino create mode 100755 libraries/USB_Host_Shield/examples/Bluetooth/BTHID/KeyboardParser.h create mode 100755 libraries/USB_Host_Shield/examples/Bluetooth/BTHID/MouseParser.h create mode 100755 libraries/USB_Host_Shield/examples/Bluetooth/PS3BT/PS3BT.ino create mode 100755 libraries/USB_Host_Shield/examples/Bluetooth/PS3Multi/PS3Multi.ino create mode 100755 libraries/USB_Host_Shield/examples/Bluetooth/PS3SPP/PS3SPP.ino create mode 100755 libraries/USB_Host_Shield/examples/Bluetooth/PS4BT/PS4BT.ino create mode 100755 libraries/USB_Host_Shield/examples/Bluetooth/SPP/SPP.ino create mode 100755 libraries/USB_Host_Shield/examples/Bluetooth/SPPMulti/SPPMulti.ino create mode 100755 libraries/USB_Host_Shield/examples/Bluetooth/Wii/Wii.ino create mode 100755 libraries/USB_Host_Shield/examples/Bluetooth/WiiIRCamera/WiiIRCamera.ino create mode 100755 libraries/USB_Host_Shield/examples/Bluetooth/WiiMulti/WiiMulti.ino create mode 100755 libraries/USB_Host_Shield/examples/Bluetooth/WiiUProController/WiiUProController.ino create mode 100755 libraries/USB_Host_Shield/examples/HID/USBHIDBootKbd/USBHIDBootKbd.ino create mode 100755 libraries/USB_Host_Shield/examples/HID/USBHIDBootKbdAndMouse/USBHIDBootKbdAndMouse.ino create mode 100755 libraries/USB_Host_Shield/examples/HID/USBHIDBootMouse/USBHIDBootMouse.ino create mode 100755 libraries/USB_Host_Shield/examples/HID/USBHIDJoystick/USBHIDJoystick.ino create mode 100755 libraries/USB_Host_Shield/examples/HID/USBHIDJoystick/hidjoystickrptparser.cpp create mode 100755 libraries/USB_Host_Shield/examples/HID/USBHIDJoystick/hidjoystickrptparser.h create mode 100755 libraries/USB_Host_Shield/examples/HID/USBHID_desc/USBHID_desc.ino create mode 100755 libraries/USB_Host_Shield/examples/HID/USBHID_desc/pgmstrings.h create mode 100755 libraries/USB_Host_Shield/examples/HID/le3dp/le3dp.ino create mode 100755 libraries/USB_Host_Shield/examples/HID/le3dp/le3dp_rptparser.cpp create mode 100755 libraries/USB_Host_Shield/examples/HID/le3dp/le3dp_rptparser.h create mode 100755 libraries/USB_Host_Shield/examples/HID/scale/scale.ino create mode 100755 libraries/USB_Host_Shield/examples/HID/scale/scale_rptparser.cpp create mode 100755 libraries/USB_Host_Shield/examples/HID/scale/scale_rptparser.h create mode 100755 libraries/USB_Host_Shield/examples/PS3USB/PS3USB.ino create mode 100755 libraries/USB_Host_Shield/examples/PS4USB/PS4USB.ino create mode 100755 libraries/USB_Host_Shield/examples/PSBuzz/PSBuzz.ino create mode 100755 libraries/USB_Host_Shield/examples/USB_desc/USB_desc.ino create mode 100755 libraries/USB_Host_Shield/examples/USB_desc/pgmstrings.h create mode 100755 libraries/USB_Host_Shield/examples/Xbox/XBOXOLD/XBOXOLD.ino create mode 100755 libraries/USB_Host_Shield/examples/Xbox/XBOXRECV/XBOXRECV.ino create mode 100755 libraries/USB_Host_Shield/examples/Xbox/XBOXUSB/XBOXUSB.ino create mode 100755 libraries/USB_Host_Shield/examples/acm/acm_terminal/acm_terminal.ino create mode 100755 libraries/USB_Host_Shield/examples/acm/acm_terminal/pgmstrings.h create mode 100755 libraries/USB_Host_Shield/examples/adk/ArduinoBlinkLED/ArduinoBlinkLED.ino create mode 100755 libraries/USB_Host_Shield/examples/adk/adk_barcode/adk_barcode.ino create mode 100755 libraries/USB_Host_Shield/examples/adk/demokit_20/demokit_20.ino create mode 100755 libraries/USB_Host_Shield/examples/adk/term_test/term_test.ino create mode 100755 libraries/USB_Host_Shield/examples/adk/term_time/term_time.ino create mode 100755 libraries/USB_Host_Shield/examples/board_qc/board_qc.ino create mode 100755 libraries/USB_Host_Shield/examples/cdc_XR21B1411/XR_terminal/XR_terminal.ino create mode 100755 libraries/USB_Host_Shield/examples/ftdi/USBFTDILoopback/USBFTDILoopback.ino create mode 100755 libraries/USB_Host_Shield/examples/ftdi/USBFTDILoopback/pgmstrings.h create mode 100755 libraries/USB_Host_Shield/examples/hub_demo/hub_demo.ino create mode 100755 libraries/USB_Host_Shield/examples/hub_demo/pgmstrings.h create mode 100755 libraries/USB_Host_Shield/examples/max_LCD/max_LCD.ino create mode 100755 libraries/USB_Host_Shield/examples/pl2303/pl2303_gprs_terminal/pl2303_gprs_terminal.ino create mode 100755 libraries/USB_Host_Shield/examples/pl2303/pl2303_gps/pl2303_gps.ino create mode 100755 libraries/USB_Host_Shield/examples/pl2303/pl2303_tinygps/pl2303_tinygps.ino create mode 100755 libraries/USB_Host_Shield/examples/pl2303/pl2303_xbee_terminal/pl2303_xbee_terminal.ino create mode 100755 libraries/USB_Host_Shield/examples/testusbhostFAT/Makefile create mode 100755 libraries/USB_Host_Shield/examples/testusbhostFAT/README.md create mode 100755 libraries/USB_Host_Shield/examples/testusbhostFAT/testusbhostFAT.ino create mode 100755 libraries/USB_Host_Shield/gpl2.txt create mode 100755 libraries/USB_Host_Shield/hexdump.h create mode 100755 libraries/USB_Host_Shield/hid.cpp create mode 100755 libraries/USB_Host_Shield/hid.h create mode 100755 libraries/USB_Host_Shield/hidboot.cpp create mode 100755 libraries/USB_Host_Shield/hidboot.h create mode 100755 libraries/USB_Host_Shield/hidescriptorparser.cpp create mode 100755 libraries/USB_Host_Shield/hidescriptorparser.h create mode 100755 libraries/USB_Host_Shield/hiduniversal.cpp create mode 100755 libraries/USB_Host_Shield/hiduniversal.h create mode 100755 libraries/USB_Host_Shield/hidusagestr.h create mode 100755 libraries/USB_Host_Shield/hidusagetitlearrays.cpp create mode 100755 libraries/USB_Host_Shield/keywords.txt create mode 100755 libraries/USB_Host_Shield/library.json create mode 100755 libraries/USB_Host_Shield/macros.h create mode 100755 libraries/USB_Host_Shield/masstorage.cpp create mode 100755 libraries/USB_Host_Shield/masstorage.h create mode 100755 libraries/USB_Host_Shield/max3421e.h create mode 100755 libraries/USB_Host_Shield/max_LCD.cpp create mode 100755 libraries/USB_Host_Shield/max_LCD.h create mode 100755 libraries/USB_Host_Shield/message.cpp create mode 100755 libraries/USB_Host_Shield/message.h create mode 100755 libraries/USB_Host_Shield/parsetools.cpp create mode 100755 libraries/USB_Host_Shield/parsetools.h create mode 100755 libraries/USB_Host_Shield/printhex.h create mode 100755 libraries/USB_Host_Shield/settings.h create mode 100755 libraries/USB_Host_Shield/sink_parser.h create mode 100755 libraries/USB_Host_Shield/usb_ch9.h create mode 100755 libraries/USB_Host_Shield/usbhost.h create mode 100755 libraries/USB_Host_Shield/usbhub.cpp create mode 100755 libraries/USB_Host_Shield/usbhub.h create mode 100755 libraries/USB_Host_Shield/version_helper.h create mode 100755 libraries/USB_Host_Shield/xboxEnums.h create mode 100755 pcb.png create mode 100755 preconfigurations/YaaCWk/so1r/PS2_WINKEY_Atmega644P/keyer_features_and_options_yaacwk.h create mode 100755 preconfigurations/YaaCWk/so1r/PS2_WINKEY_Atmega644P/keyer_pin_settings_yaacwk.h create mode 100755 preconfigurations/YaaCWk/so2r/PS2_WINKEY_Atmega644P/default.txt create mode 100755 preconfigurations/YaaCWk/so2r/PS2_WINKEY_Atmega644P/keyer_features_and_options_yaacwk.h create mode 100755 preconfigurations/YaaCWk/so2r/PS2_WINKEY_Atmega644P/keyer_pin_settings_yaacwk.h create mode 100755 preconfigurations/YaaCWk/so2r/USB_Atmega644P/keyer_features_and_options_yaacwk.h create mode 100755 preconfigurations/YaaCWk/so2r/USB_Atmega644P/keyer_pin_settings_yaacwk.h create mode 100755 preconfigurations/YaaCWk/so2r/USB_WINKEY_Atmega1284P/keyer_features_and_options_yaacwk.h create mode 100755 preconfigurations/YaaCWk/so2r/USB_WINKEY_Atmega1284P/keyer_pin_settings_yaacwk.h create mode 100755 schema.png diff --git a/COPYING.md b/COPYING.md new file mode 100755 index 0000000..2fb2e74 --- /dev/null +++ b/COPYING.md @@ -0,0 +1,675 @@ +### GNU GENERAL PUBLIC LICENSE + +Version 3, 29 June 2007 + +Copyright (C) 2007 Free Software Foundation, Inc. + + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + +### Preamble + +The GNU General Public License is a free, copyleft license for +software and other kinds of works. + +The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom +to share and change all versions of a program--to make sure it remains +free software for all its users. We, the Free Software Foundation, use +the GNU General Public License for most of our software; it applies +also to any other work released this way by its authors. You can apply +it to your programs, too. + +When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + +To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you +have certain responsibilities if you distribute copies of the +software, or if you modify it: responsibilities to respect the freedom +of others. + +For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + +Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + +For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + +Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the +manufacturer can do so. This is fundamentally incompatible with the +aim of protecting users' freedom to change the software. The +systematic pattern of such abuse occurs in the area of products for +individuals to use, which is precisely where it is most unacceptable. +Therefore, we have designed this version of the GPL to prohibit the +practice for those products. If such problems arise substantially in +other domains, we stand ready to extend this provision to those +domains in future versions of the GPL, as needed to protect the +freedom of users. + +Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish +to avoid the special danger that patents applied to a free program +could make it effectively proprietary. To prevent this, the GPL +assures that patents cannot be used to render the program non-free. + +The precise terms and conditions for copying, distribution and +modification follow. + +### TERMS AND CONDITIONS + +#### 0. Definitions. + +"This License" refers to version 3 of the GNU General Public License. + +"Copyright" also means copyright-like laws that apply to other kinds +of works, such as semiconductor masks. + +"The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + +To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of +an exact copy. The resulting work is called a "modified version" of +the earlier work or a work "based on" the earlier work. + +A "covered work" means either the unmodified Program or a work based +on the Program. + +To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + +To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user +through a computer network, with no transfer of a copy, is not +conveying. + +An interactive user interface displays "Appropriate Legal Notices" to +the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + +#### 1. Source Code. + +The "source code" for a work means the preferred form of the work for +making modifications to it. "Object code" means any non-source form of +a work. + +A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + +The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + +The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + +The Corresponding Source need not include anything that users can +regenerate automatically from other parts of the Corresponding Source. + +The Corresponding Source for a work in source code form is that same +work. + +#### 2. Basic Permissions. + +All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + +You may make, run and propagate covered works that you do not convey, +without conditions so long as your license otherwise remains in force. +You may convey covered works to others for the sole purpose of having +them make modifications exclusively for you, or provide you with +facilities for running those works, provided that you comply with the +terms of this License in conveying all material for which you do not +control copyright. Those thus making or running the covered works for +you must do so exclusively on your behalf, under your direction and +control, on terms that prohibit them from making any copies of your +copyrighted material outside their relationship with you. + +Conveying under any other circumstances is permitted solely under the +conditions stated below. Sublicensing is not allowed; section 10 makes +it unnecessary. + +#### 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + +No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + +When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such +circumvention is effected by exercising rights under this License with +respect to the covered work, and you disclaim any intention to limit +operation or modification of the work as a means of enforcing, against +the work's users, your or third parties' legal rights to forbid +circumvention of technological measures. + +#### 4. Conveying Verbatim Copies. + +You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + +You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + +#### 5. Conveying Modified Source Versions. + +You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these +conditions: + +- a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. +- b) The work must carry prominent notices stating that it is + released under this License and any conditions added under + section 7. This requirement modifies the requirement in section 4 + to "keep intact all notices". +- c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. +- d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + +A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + +#### 6. Conveying Non-Source Forms. + +You may convey a covered work in object code form under the terms of +sections 4 and 5, provided that you also convey the machine-readable +Corresponding Source under the terms of this License, in one of these +ways: + +- a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. +- b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the Corresponding + Source from a network server at no charge. +- c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. +- d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. +- e) Convey the object code using peer-to-peer transmission, + provided you inform other peers where the object code and + Corresponding Source of the work are being offered to the general + public at no charge under subsection 6d. + +A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + +A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, +family, or household purposes, or (2) anything designed or sold for +incorporation into a dwelling. In determining whether a product is a +consumer product, doubtful cases shall be resolved in favor of +coverage. For a particular product received by a particular user, +"normally used" refers to a typical or common use of that class of +product, regardless of the status of the particular user or of the way +in which the particular user actually uses, or expects or is expected +to use, the product. A product is a consumer product regardless of +whether the product has substantial commercial, industrial or +non-consumer uses, unless such uses represent the only significant +mode of use of the product. + +"Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to +install and execute modified versions of a covered work in that User +Product from a modified version of its Corresponding Source. The +information must suffice to ensure that the continued functioning of +the modified object code is in no case prevented or interfered with +solely because modification has been made. + +If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + +The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or +updates for a work that has been modified or installed by the +recipient, or for the User Product in which it has been modified or +installed. Access to a network may be denied when the modification +itself materially and adversely affects the operation of the network +or violates the rules and protocols for communication across the +network. + +Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + +#### 7. Additional Terms. + +"Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + +When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + +Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders +of that material) supplement the terms of this License with terms: + +- a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or +- b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or +- c) Prohibiting misrepresentation of the origin of that material, + or requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or +- d) Limiting the use for publicity purposes of names of licensors + or authors of the material; or +- e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or +- f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions + of it) with contractual assumptions of liability to the recipient, + for any liability that these contractual assumptions directly + impose on those licensors and authors. + +All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + +If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + +Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; the +above requirements apply either way. + +#### 8. Termination. + +You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + +However, if you cease all violation of this License, then your license +from a particular copyright holder is reinstated (a) provisionally, +unless and until the copyright holder explicitly and finally +terminates your license, and (b) permanently, if the copyright holder +fails to notify you of the violation by some reasonable means prior to +60 days after the cessation. + +Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + +Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + +#### 9. Acceptance Not Required for Having Copies. + +You are not required to accept this License in order to receive or run +a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + +#### 10. Automatic Licensing of Downstream Recipients. + +Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + +An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + +You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + +#### 11. Patents. + +A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + +A contributor's "essential patent claims" are all patent claims owned +or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + +In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + +If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + +If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + +A patent license is "discriminatory" if it does not include within the +scope of its coverage, prohibits the exercise of, or is conditioned on +the non-exercise of one or more of the rights that are specifically +granted under this License. You may not convey a covered work if you +are a party to an arrangement with a third party that is in the +business of distributing software, under which you make payment to the +third party based on the extent of your activity of conveying the +work, and under which the third party grants, to any of the parties +who would receive the covered work from you, a discriminatory patent +license (a) in connection with copies of the covered work conveyed by +you (or copies made from those copies), or (b) primarily for and in +connection with specific products or compilations that contain the +covered work, unless you entered into that arrangement, or that patent +license was granted, prior to 28 March 2007. + +Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + +#### 12. No Surrender of Others' Freedom. + +If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under +this License and any other pertinent obligations, then as a +consequence you may not convey it at all. For example, if you agree to +terms that obligate you to collect a royalty for further conveying +from those to whom you convey the Program, the only way you could +satisfy both those terms and this License would be to refrain entirely +from conveying the Program. + +#### 13. Use with the GNU Affero General Public License. + +Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + +#### 14. Revised Versions of this License. + +The Free Software Foundation may publish revised and/or new versions +of the GNU General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in +detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies that a certain numbered version of the GNU General Public +License "or any later version" applies to it, you have the option of +following the terms and conditions either of that numbered version or +of any later version published by the Free Software Foundation. If the +Program does not specify a version number of the GNU General Public +License, you may choose any version ever published by the Free +Software Foundation. + +If the Program specifies that a proxy can decide which future versions +of the GNU General Public License can be used, that proxy's public +statement of acceptance of a version permanently authorizes you to +choose that version for the Program. + +Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + +#### 15. Disclaimer of Warranty. + +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT +WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND +PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE +DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR +CORRECTION. + +#### 16. Limitation of Liability. + +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR +CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT +NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR +LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM +TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER +PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +#### 17. Interpretation of Sections 15 and 16. + +If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + +END OF TERMS AND CONDITIONS + +### How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these +terms. + +To do so, attach the following notices to the program. It is safest to +attach them to the start of each source file to most effectively state +the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper +mail. + +If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands \`show w' and \`show c' should show the +appropriate parts of the General Public License. Of course, your +program's commands might be different; for a GUI interface, you would +use an "about box". + +You should also get your employer (if you work as a programmer) or +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. For more information on this, and how to apply and follow +the GNU GPL, see . + +The GNU General Public License does not permit incorporating your +program into proprietary programs. If your program is a subroutine +library, you may consider it more useful to permit linking proprietary +applications with the library. If this is what you want to do, use the +GNU Lesser General Public License instead of this License. But first, +please read . diff --git a/LICENSE b/LICENSE new file mode 100755 index 0000000..f288702 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/README.md b/README.md new file mode 100755 index 0000000..a72cc32 --- /dev/null +++ b/README.md @@ -0,0 +1,10 @@ +# Arduino CW Keyer +Projet basé sur le travail de K3NG +https://github.com/k3ng/k3ng_cw_keyer/wiki + +Version 2.0 + + + + + diff --git a/bom.png b/bom.png new file mode 100755 index 0000000000000000000000000000000000000000..91c72b49164509767612a89577b1a3eca60c2569 GIT binary patch literal 95615 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

-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 Y;1%90IMFA*F|M&ppyMW@bu0_tuNp9t`!HDEUfL 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. + + +If you offer a hardware kit using this software, show your appreciation by sending the author a complimentary kit or a bottle of bourbon ;-) + +Full documentation can be found at https://github.com/k3ng/k3ng_cw_keyer/wiki . Please read it before requesting help. + +For help, please post on the Radio Artisan group: https://groups.io/g/radioartisan . Please do not email the developer directly for support. Thanks + +YouTube Channel: https://www.youtube.com/channel/UC5o8UM1-heT5kJbwnJRkUYg + +2020 Recipient of the Amateur Radio Software Award https://amateurradiosoftwareaward.github.io/ + +Wordsworth CW training method created by George Allison, K1IG +English code training word lists from gen_cw_words.pl by Andy Stewart, KB1OIQ + + Command Line Interface ("CLI") (USB Port) (Note: turn on carriage return if using Arduino Serial Monitor program) + + CW Keyboard: type what you want the keyer to send (all commands are preceded with a backslash ( \ ) + \? Help (requires FEATURE_SERIAL_HELP) + \/ Paged Help (requires FEATURE_SERIAL_HELP) + \# Play memory # (requires FEATURES_MEMORIES; play memories 1 - 10 (0 = memory 10) ) + \a Iambic A mode + \b Iambic B mode + \c Single Paddle mode + \d Ultimatic mode (if OPTION_NO_ULTIMATIC not set) + \e#### Set serial number to #### + \f#### Set sidetone frequency to #### hertz + \g Bug mode + \h Toggle between CW and Hell sending (requires FEATURE_HELL) + \i Transmit enable/disable + \j### Dah to dit ratio (300 = 3.00, do \j alone to set to default) + \k CW Training Module (requires FEATURE_TRAINING_COMMAND_LINE_INTERFACE) + \l## Set weighting (50 = normal, do \l alone to set to default) + \m### Set Farnsworth speed + \n Toggle paddle reverse + \o Toggle sidetone on/off + \p#(#) Program memory # + \q## Switch to QRSS mode, dit length ## seconds + \r Switch to regular speed mode + \s Status + \t Tune mode + \u Manual PTT toggle + \v Toggle potentiometer active / inactive (requires FEATURE_POTENTIOMETER) + \w### Set speed in WPM + \x# Switch to transmitter # + \y# Change wordspace to # elements (# = 1 to 9) + \z Autospace on/off + \+ Create prosign + \!## Repeat play memory + \|#### Set memory repeat (milliseconds) (backslash and pipe) + \* Toggle paddle echo + \` Toggle straight key echo + \^ Toggle wait for carriage return to send CW / send CW immediately + \& Toggle CMOS Super Keyer Timing on/off + \%## Set CMOS Super Keyer Timing % + \. Toggle dit buffer on/off + \- Toggle dah buffer on/off + \~ Reset unit + \; Toggle cw send echo + \{ QLF mode on/off + \> Send serial number, then increment + \< Send current serial number + \( Send current serial number in cut numbers + \) Send serial number with cut numbers, then increment + \[ Set Quiet Paddle Interruption + \= Toggle American Morse mode (requires FEATURE_AMERICAN_MORSE) + \@ Mill Mode + \}#### Set potentiometer range - low ## / high ## + \" Hold PTT active with buffered characters + \] PTT Enable / Disable + \_ Beacon Mode at Boot Up Enable / Disable (requires FEATURE_BEACON_SETTING) + \; CW Send Inhibit Enable / Disable + \\ Immediately clear the buffer, stop memory sending, etc. + \: Extended CLLI commands + eepromdump - do a byte dump of EEPROM for troubleshooting + saveeeprom - store EEPROM in a file + loadeeprom - load into EEPROM from a file + printlog - print the SD card log + clearlog - clear the SD card log + ls - list files in SD card directory + cat - print filename on SD card + pl - Set PTT lead time + pt - Set PTT tail time + af ### - Set autospace timing factor; 100 = 1.00 + pf ### - Set paddle echo timing factor; 100 = 1.00 + + + Buttons + button 0: command mode / command mode exit + button 0 + left paddle: increase cw speed + button 0 + right paddle: decrease cw speed + button 1 - 12 hold + left paddle: repeat memory + button 1 - 6 half second hold: switch to TX # 1 - 6 + + Command Mode (press button0 to enter command mode and press again to exit) + A Switch to Iambic A mode + B Switch to Iambic B mode + C Switch to Single Paddle Mode + D Switch to Ultimatic mode (if OPTION_NO_ULTIMATIC not set) + + E Announce speed + F Adjust sidetone frequency + G Switch to bug mode + H Set weighting and dah to dit ratio to defaults + I TX enable / disable + J Dah to dit ratio adjust + K Toggle Dit and Dah Buffers on and off (Ultimatic Mode) (if OPTION_NO_ULTIMATIC is not set) + L Adjust weighting + M Change command mode speed + N Toggle paddle reverse + O Toggle sidetone on / off + P#(#) Program a memory + Q Adjust keying compensation (left paddle = increase, right paddle = decrease) + R#### Set serial number to #### + S Alphabet code practice (FEATURE_ALPHABET_SEND_PRACTICE) + T Tune mode + U Receive / Send Echo Practice + V Toggle potentiometer active / inactive + W Change speed + X Exit command mode (you can also press the command button (button0) to exit) + Y#### Change memory repeat delay to #### mS + Z Autospace On/Off + # Play a memory without transmitting + = Enable / disable PTT Line (-...-) + ? Status + 1. Speed in WPM + 2. Keyer Mode (A = Iambic A, B = Iambic B, G = Bug, S = Single Paddle, U = Ultimatic) + 3. Weighting + 4. Dah to Dit Ratio + + Memory Macros + \# Jump to memory # + \c Play serial number with cut numbers, then increment + \d### Delay for ### seconds + \e Play serial number, then increment + \f#### Change sidetone to #### hertz (must be four digits - use leading zero below 1000 hz) + \h Switch to Hell sending + \i# Insert memory number + \l Switch to CW (from Hell mode) + \n Decrement serial number, do not send + \q## Switch to QRSS mode, dit length ## seconds + \r Switch to regular speed mode + \s Insert space + \t### Transmit for ### seconds (must be three digits, use leading zeros if necessary) + \u Activate PTT + \v Deactivate PTT + \w### Set regular mode speed to ### WPM (must be three digits, use leading zeros if necessary) + \x# Switch to transmitter # (1, 2, or 3) + \y# Increase speed # WPM + \z# Decrease speed # WPM + \+ Prosign the next two characters + + PS2 / USB Keyboard + + CTRL-A Iambic A + CTRL-B Iambic B + CTRL-C Single Paddle + CTRL-D Ultimatic (if OPTION_NO_ULTIMATIC not set) + CTRL-E Set Serial Number + CTRL-G Bug + CTRL-H Toggle Hell Mode On/Off (requires FEATURE_HELL) + CTRL-I TX enable / disable + CTRL-M Set Farnsworth Speed (0 = disabled) (requires FEATURE_FARNSWORTH) + CTRL-N Paddle Reverse + CTRL-O Toggle Sidetone On/Off + CTRL-S CMOS Superkeyer Timing On/Off + CTRL-T Tune + CTRL-U Manual PTT Toggle + CTRL-W Set WPM + CTRL-F1 Switch to TX #1 + CTRL-F2 Switch to TX #2 + CTRL-F3 Switch to TX #3 + CTRL-F4 Switch to TX #4 + CTRL-F5 Switch to TX #5 + CTRL-F6 Switch to TX #6 + END Send serial number no increment + ESC Stop sending and clear buffer + F1, F2, F3.. Play memory 1, 2, 3... + DOWN ARROW Decrease WPM + HOME Reset timing settings + INSERT Send serial number and increment + LEFT ARROW Decrease Dah to Dit Ratio + PGDN Decrease Sidetone Frequency + PGUP Increase Sidetone Frequency + RIGHT ARROW Increase Dah to Dit Ratio + SCROLL LOCK Prosign Next Two Characters + SHIFT-BACKSPACE Decrement serial number + SHIFT-F1, F2... Program Memory 1, 2... + ALT-F1, F2... Repeat Memory 1, 2... + TAB Pause Sending Immediately + UP ARROW Increase WPM + Keypad / Dit Paddle (USB Keyboard Only) + Keypad * Dah Paddle (USB Keyboard Only) + Keypad ENTER Tune / Straight Key (USB Keyboard Only) + + USB Mouse + + Left Button Dit + Right Button Dah + Middle Button Tune / Straight Key + + PS2 Keyboard Notes (FEATURE_PS2_KEYBOARD) + + To use FEATURE_PS2_KEYBOARD you need the K3NG_PS2Keyboard.h and K3NG_PS2Keyboard.cpp library files from https://github.com/k3ng/k3ng_cw_keyer/tree/master/libraries + + Some keyboards may require a reset sequence upon startup. This is activated with OPTION_PS2_KEYBOARD_RESET. + + USB Keyboard Notes (FEATURE_USB_KEYBOARD) + + To use a USB keyboard you need to download and install this library: https://github.com/felis/USB_Host_Shield_2.0 . You may use an Arduino Mega + ADK board (which has a built in USB host interface, get or Circuits@Home USB shield (http://www.circuitsathome.com/products-page/arduino-shields/usb-host-shield-2-0-for-arduino), + or built your own MAX3421 based USB port. + + If you are using an Arduino Mega ADK with Arduino IDE older than version 1.5.5, you must customize the USB Host Shield Library settings.h file! + + If you are using Arduino IDE older than version 1.5.5 and you experience a compiler error, you may need to add these lines to your keyer.h file: + + #define HID_PROTOCOL_KEYBOARD 1 + #define HID_PROTOCOL_MOUSE 2 + + (Thanks Raimo, DL1HTB, for the two notes above.) + + Option Usb Computer Keyboard Emulation FEATURE_CW_COMPUTER_KEYBOARD + (Arduino Due, Leonardo only) + + You can use your cw key as a computer keyboard. Your computer recognize the K3NG keyer as a normal keyboard. + Language available English and Italian (more languages to add) + Use following prosign to emulate Enter Key, Caps Lock, space and backspace: + Prosign AA "Enter" + Prosign DO "Caps Lock" (enable and disable) + "......" or more "Backspace" + "------" or more "Space" + + SIDETONE_SWITCH + Enabling this feature and an external toggle switch adds switch control for playing cw sidetone. + ST Switch status is displayed in the status command. This feature will override the software control of the sidetone (\o). + + +Useful Stuff + Reset to defaults: squeeze both paddles at power up (good to use if you dorked up the speed and don't have the CLI) + Press the right paddle to enter straight key mode at power up + Press the left paddle at power up to enter and stay forever in beacon mode + +Recent Update History + + 2.2.2015040402 More work on ARDUINO_SAM_DUE (documented) + + 2.2.2015040501 Fixed bug with O command not working when any display feature was compiled in + + 2.2.2015040801 FEATURE_EEPROM_E24C1024; working on FEATURE_CW_COMPUTER_KEYBOARD (documented) + + 2.2.2015040901 updated serial help text with recently added commands, consolidated the three paddle echo features into one subroutine + + 2.2.2015040902 Minor typos fixed + + 2.2.2015042002 Eliminated keyer.h declaration (upgrade Stino if you're still using keyer.h) + + 2.2.2015042301 + '#define PRIMARY_SERIAL_PORT &Serial' is now '#define PRIMARY_SERIAL_PORT &Serial' (documented on website 2015-04-25) + OPTION_SERIAL_PORT_DEFAULT_WINKEY_EMULATION is now OPTION_PRIMARY_SERIAL_PORT_DEFAULT_WINKEY_EMULATION (documented on website 2015-04-25) + '#define default_serial_baud_rate 115200' is now '#define PRIMARY_SERIAL_PORT_BAUD 115200' (documented on website 2015-04-25) + #define SECONDARY_SERIAL_PORT_BAUD 115200 (documented on website 2015-04-25) + FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT (documented on website 2015-04-25) + FEATURE_LCD1602_N07DH (Thanks Xigco for code!) (documented on website 2015-04-25) + + 2.2.2015042302 + OPTION_CW_KEYBOARD_ITALIAN (Thanks Giorgio IZ2XBZ) (documented on website 2015-04-25) + FEATURE_CW_COMPUTER_KEYBOARD repeating backspace, fixed caps lock sounds + + 2.2.2015042303 + Test of GitHub - no changes + + 2.2.2015042501 + FEATURE_CW_COMPUTER_KEYBOARD update from Giorgio IZ2XBZ + Website documentation up to date! Yeahhhhhh! :-) + + 2.2.2015042901 + HARDWARE_NANOKEYER_REV_D + + 2.2.2015043001 + Fixed compilation bug with FEATURE_COMMAND_LINE_INTERFACE when FEATURE_WINKEY_EMULATION not enabled + + 2.2.2015051201 + OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR (website updated 2015-05-12) + + 2.2.2015051301 + Improvements to FEATURE_CW_DECODER for better decoding and Goetzel settings for Arduino Due + + 2.2.2015061101 + lcd_columns and lcd_rows in keyer_settings*.h files renamed to LCD_COLUMNS and LCD_ROWS + OPTION_INVERT_PADDLE_PIN_LOGIC - paddle closed = HIGH, paddle open = LOW + + 2.2.2015082801 + Added E24C1024.h and E24C1024.cpp to git + Fixed compilation issue with Due involving E24C1024 library + + 2.2.2015082802 + FEATURE_STRAIGHT_KEY {documented on web page 2015-09-05} + + 2.2.2015090501 + Memories can now be programmed in commmand mode (FEATURE_BUTTONS) by pressing the memory button + FEATURE_CW_DECODER now has digital input pin (cw_decoder_pin) and if OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR is enable, cw_decoder_audio_input_pin will work in parallel + + 2.2.2015090801 + Fixed issue with FEATURE_CW_DECODER + OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR and wrong GOERTZ_SAMPLING_FREQ and GOERTZ_SAMPLES used in goertzel.h causing keyer lockups after startup + + 2.2.2015091301 + FEATURE_DYNAMIC_DAH_TO_DIT_RATIO (code contributed by Giorgio, IZ2XBZ) + #ifdef FEATURE_DYNAMIC_DAH_TO_DIT_RATIO (keyer_settings.h) + #define DYNAMIC_DAH_TO_DIT_RATIO_LOWER_LIMIT_WPM 30 + #define DYNAMIC_DAH_TO_DIT_RATIO_LOWER_LIMIT_RATIO 300 // 300 = 3:1 ratio + #define DYNAMIC_DAH_TO_DIT_RATIO_UPPER_LIMIT_WPM 70 + #define DYNAMIC_DAH_TO_DIT_RATIO_UPPER_LIMIT_RATIO 240 // 240 = 2.4:1 ratio + #endif //FEATURE_DYNAMIC_DAH_TO_DIT_RATIO + + 2.2.2015091302 + FEATURE_COMPETITION_COMPRESSION_DETECTION - Experimental + Fixed compiler error when only FEATURE_BUTTONS was enabled + + 2.2.2015091801 + OPTION_DIT_DAH_BUFFERS_OFF_BY_DEFAULT_FOR_FEATURE_DIT_DAH_BUFFER_CONTROL + OPTION_ADVANCED_SPEED_DISPLAY (code contributed by Giorgio, IZ2XBZ) + + 2.2.2015091802 + Improved handling of spaces in LCD display + + 2.2.2015092101 + Fixed bugs in OPTION_CW_KEYBOARD_ITALIAN and OPTION_UNKNOWN_CHARACTER_ERROR_TONE (courtesy of Giorgio, IZ2XBZ) + + 2.2.2015092301 + FEATURE_COMPETITION_COMPRESSION_DETECTION improvements + + 2.2.2015092401 + #define compression_detection_pin 0 + default potentiometer_change_threshold changed to 0.9 + + 2.2.2015101201 + Additional DEBUG_PS2_KEYBOARD code + + 2.2.2015101301 + OPTION_STRAIGHT_KEY_ECHO + + 2.2.2015101302 + OPTION_STRAIGHT_KEY_ECHO is now FEATURE_STRAIGHT_KEY_ECHO + CLI command: \` Toggle straight key echo + #define cli_paddle_echo_on_at_boot 1 + #define cli_straight_key_echo_on_at_boot 1 + FEATURE_STRAIGHT_KEY now works with FEATURE_CW_COMPUTER_KEYBOARD + Straight Key can now program memories + + 2.2.2015101401 + Fixed compile bug with FEATURE_DISPLAY and cli_straight_key_echo + + 2.2.2015101402 + K3NG_PS2Keyboard Library: Fixed issues with CTRL and ALT key combinations with German and French keyboards + + 2.2.2015101801 + OPTION_WINKEY_IGNORE_LOWERCASE + + 2.2.2015111501 + Fixed storage of KN prosign in memory (Thank Stefan, DL1SMF) + + 2.2.2015120401 + Fixed compiler warning: large integer implicitly truncated to unsigned type - jump_back_to_y = 99999; + + 2.2.2015121901 + OPTION_PROSIGN_SUPPORT - additional prosign support for memory storage + + 2.2.2015122001 + OPTION_PROSIGN_SUPPORT - updated; forgot to add functionality to paddle echo + + 2.2.2015122801 + void send_the_dits_and_dahs(char * cw_to_send) compile warning fix + + 2.2.2016010301 + Fixed compiler error when OPTION_SAVE_MEMORY_NANOKEYER and FEATURE_COMMAND_LINE_INTERFACE are enabled (Thanks, Gerd, DD4DA) + void play_memory (byte memory_number) near line 10049 - static String serial_number_string - removed static declration to fix compiler warning (Thanks, Gerd, DD4DA) + + 2.2.2016010302 + Winkey emulation pin config bug fix (Thanks, Gerd, DD4DA) + + 2.2.2016011801 + New and improved FEATURE_SLEEP code contributed by Graeme, ZL2APV + + 2.2.2016012001 + Fixed compile error involving serial_number, FEATURE_PS2_KEYBOARD, and HARDWARE_NANOKEYER_REV_D (Thanks, Kari, OH6FSG) + + 2.2.2016012002 + HARDWARE_TEST + Enhanced FEATURE_SLEEP to have pin that indicates sleep state: define keyer_awake 0 ; KEYER_AWAKE_PIN_AWAKE_STATE, KEYER_AWAKE_PIN_ASLEEP_STATE + + 2.2.2016012003 + Fixed compiler warning for void play_memory() and returns; (Thanks, Gerd, DD4DA) + + 2.2.2016012004 + Modified includes so library files can be put in \libraries\ folder rather than ino directory so Arduino 1.6.7 works (thanks Giorgio, IZ2XBZ)) + + 2.2.2016012101 + Beta testing FEATURE_INTERRUPT_PADDLE_READS + + 2.2.2016012301 + Fixed compilation error: 10306: error: return-statement with no value, in function returning byte (thanks Giorgio, IZ2XBZ)) + + 2.2.2016012302 + Merge of bug fix from JG2RZF: Winkey - CTESTWIN sends 0x00 as "HSCW Speed Change" to keyer (thanks JG2RZF) + + 2.2.2016012501 + loop_element_lengths - minor change to paddle reading that may have an effect at high speeds + + 2.2.2016012502 + tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state settings + OPTION_RUSSIAN_LANGUAGE_SEND_CLI contributed by Павел Бирюков, UA1AQC + + 2.2.2016012601 + Winkey emulation support for 0x1D HSCW overloaded command to switch transmitters (thanks JG2RZF) + Moved stuff from keyer_settings*.h to keyer.h (no need to tweak these or have different entries for different hardware) + + 2.2.2016012801 + Fixed issue with goertzel.h being required for compilation even when it wasn't needed + + 2.2.2016012901 + Removed experimental feature + + 2.2.2016012902 + FEATURE_LCD_ADAFRUIT_BACKPACK - support for Adafruit I2C LCD Backup using MCP23008 (courtesy Josiah Ritchie, KE0BLL) + + 2.2.2016020801 + PROSIGN_HH (courtesy of Vincenzo, IZ0RUS) + + 2.2.2016020802 + OPTION_DO_NOT_SEND_UNKNOWN_CHAR_QUESTION + + 2.2.2016030501 + FEATURE_LCD_SAINSMART_I2C + + 2.2.2016030701 + Fixed FEATURE_LCD_SAINSMART_I2C initialization + + 2.2.2016030801 + Fixed FEATURE_LCD_SAINSMART_I2C again + + 2.2.2016031801 + Ethernet, web server and Internet linking functionality in beta / development (DEFINEs are in HARDWARE_TEST files only right now) + #define FEATURE_WEB_SERVER + #define FEATURE_INTERNET_LINK + + #define OPTION_INTERNET_LINK_NO_UDP_SVC_DURING_KEY_DOWN + + #define FEATURE_ETHERNET_IP {192,168,1,179} // default IP address + #define FEATURE_ETHERNET_MAC {0xDE,0xAD,0xBE,0xEF,0xFE,0xEE} + + #define FEATURE_ETHERNET_GATEWAY {192,168,1,1} // default gateway + #define FEATURE_ETHERNET_SUBNET_MASK {255,255,255,0} // default subnet mask + #define FEATURE_ETHERNET_WEB_LISTENER_PORT 80 + #define FEATURE_UDP_SEND_BUFFER_SIZE 128 + #define FEATURE_UDP_RECEIVE_BUFFER_SIZE 128 + + #define FEATURE_INTERNET_LINK_MAX_LINKS 2 + #define FEATURE_INTERNET_LINK_DEFAULT_RCV_UDP_PORT 8888 + #define FEATURE_INTERNET_LINK_BUFFER_TIME_MS 500 + + 2.2.2016040501 + Fixed bug with OPTION_DO_NOT_SEND_UNKNOWN_CHAR_QUESTION and ? character not being sent with keyboard and Winkey operation + Still working on web server functionality + + 2.2.2016042601 + More web server functionality work + #define FEATURE_INTERNET_LINK_KEY_DOWN_TIMEOUT_SECS 8 + \P command now can program memories above #10 + + 2.2.2016053001 + Additional DEBUG_WINKEY messages for Winkeyer troubleshooting + #define WINKEY_DEFAULT_BAUD 1200 (added setting for UCXLog 9600 baud Winkey setting) + Fixed minor Winkey emulation bug with recognizing byte 0x7C as a half dit space when OPTION_WINKEY_IGNORE_LOWERCASE is enabled + + 2.2.2016062101 + New CLI commands: + \> Send serial number, then increment + \< Send current serial number + \( Send current serial number in cut numbers + \) Send serial number with cut numbers, then increment + + + 2.2.2016070701 + Corrected Nanokeyer Rev B and Rev D configurations + + 2.2.2016070702 + Setting for speed potentiometer check interval: #define potentiometer_check_interval_ms 150 + + 2.2.2016071001 + OPTION_WINKEY_UCXLOG_9600_BAUD for UCXLog 9600 baud support (I can't get UCXlog to work at 1200 baud) + + 2.2.2016071801 + Now have FEATURE_AUTOSPACE and FEATURE_DEAD_OP_WATCHDOG disabled by default for HARDWARE_NANOKEYER_REV_D + + 2.2.2016071802 + FEATURE_CAPACITIVE_PADDLE_PINS: capactive_paddle_pin_inhibit_pin + + 2.2.2016072301 + Added dependency check for FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + More Winkey emulation debugging; working on strange issues with UcxLog interoperability. UcxLog working with normal 1200 baud mode today. Hmmm. + + 2.2.2016080101 + Troubleshooting some UCXLog Winkey weirdness some users are experiencing. Created OPTION_WINKEY_UCXLOG_SUPRESS_C4_STATUS_BYTE + + 2.2.2016080301 + Disabled echoing of 7C (half space character) byte in Winkey emulation + + 2.2.2016080601 + More messing around with UCXlog... + OPTION_WINKEY_DO_NOT_ECHO_7C_BYTE // Might need for UCXlog? (7C = half space character) + OPTION_WINKEY_DO_NOT_SEND_7C_BYTE_HALF_SPACE + + 2.2.2016081201 + OPTION_WINKEY_DO_NOT_ECHO_7C_BYTE is changed to OPTION_WINKEY_ECHO_7C_BYTE and only in the test feature and options file for testing/debugging purposes + OPTION_WINKEY_DO_NOT_SEND_7C_BYTE_HALF_SPACE - not placing this into production. this was to troubleshoot issues with UCXLog + + 2.2.2016081601 + Updated paddle echo to work with bug mode + + 2.2.2016090701 + More efficient code suggestion from Paul, K1XM, implemented in loop_element_lengths() + + 2.2.2016090801 + Removed legacy option: OPTION_USE_ORIGINAL_VERSION_2_1_PS2KEYBOARD_LIB + + 2.2.2016090802 + Corrected error in FEATURE_ROTARY_ENCODER ttable (thanks, frye.dale) + + 2.2.2016091401 + More frequent PTT line tail time checking + + 2.2.2016091602 + Reversing munged GitHub merge + + 2.2.2016091901 + Manual merge of toyo pull request #22 + It is no longer necessary to specify HARDWARE_ARDUINO_DUE in keyer_hardware.h. It is automatically detected now. + + 2.2.2016092701 + Command Mode: command L - adjust weighting + + 2.2.2016092702 + Winkey Emulation - changed paddle interrupt behavior to send 0xC2 and then 0xC0 rather than just 0xC0 + + 2.2.2016092801 + Winkey Emulation - changed paddle interrupt behavior to send 0xC6,0xC0 rather than 0x64,0xC0 + + 2.2.2016092802 + Fixed issue with configuration in eeprom colliding with memory 0 (1) (Thanks, Ivan, IX1FJG) + + 2.2.2016092803 + Winkey Emulation - changed paddle interrupt behavior to also clear send buffer + + 2.2.2016092901 + Improved opposite paddle dit/dah insertion in Ultimatic mode + + 2.2.2016100601 + Improved paddle break in for memory playing and Winkey interruption + Fixed various compile bugs that have crept into the code + + 2.2.2016102401 + Updated \J (dah to dit ratio) and \L (weighting) CLI commands so that without arguments they set the parameters to defaults + + 2.2.2016102801 + Single Paddle mode, C command + + 2.2.2016103101 + Quiet Paddle Interruption feature - set with \[ command in CLI. Value is 0 to 20 element lengths; 0 = off + + 2.2.2016110801 + Integrated OK1RR Tiny Keyer hardware files - HARDWARE_TINYKEYER in keyer_hardware.h file + + 2.2.2016110802 + New command mode command H - set weighting and dah to dit ratio to defaults + New command mode command ? - Status + + 2.2.2016111701 + FEATURE_CW_COMPUTER_KEYBOARD enhancements from Giorgio IZ2XBZ + + 2.2.2016111702 + Eliminated FEATURE_DIT_DAH_BUFFER_CONTROL code; it's compiled in with core code now. Also depricated OPTION_DIT_DAH_BUFFERS_OFF_BY_DEFAULT_FOR_FEATURE_DIT_DAH_BUFFER_CONTROL + + 2.2.2016112301 + New command mode command K: toggle dit and dah buffer on and off + + 2.2.2016112302 + Updated keyer_hardware.h to accomodate Leonardo, Yun, Esplora, and other boards to compile with Serial related functionality. + + 2.2.2016112401 + Updated dit and dah buffer control to change automatically with Iambic A & B and Ultimatic + + 2.2.2016112501 + Code comment update + + 2.2.2016112502 + Merged in GitHub pull request 24 https://github.com/k3ng/k3ng_cw_keyer/pull/24 from Giorgio IZ2XBZ + + 2.2.2016112701 + Improved performance when sending large macros from logging and contest programs using Winkey emulation. Thanks, Martin OK1RR for discovery and testing + + 2.2.2016112702 + Updated command mode K command to work only when in Ultimatic mode + + 2.2.2016112901 + Fixed bug with command mode status command reporting wrong keyer mode. Also fixed CLI status query reporting wrong keyer mode while in command mode + + 2.2.2016120101 + Compilation of serial related functionality for TEENSYDUINO + + 2.2.2016120102 + Comilation issue fix for ARDUINO_MAPLE_MINI. Thanks, Edgar, KC2UEZ + + 2.2.2016120401 + Added keyer_stm32duino.h with function declarations to make ARDUINO_MAPLE_MINI compilation work. Thanks, Edgar, KC2UEZ + + 2.2.2016120901 + Merged pull request STM32duino compatibilty 30. Thanks, Edgar, KC2UEZ + + 2.2.2016120902 + Fixed bug in command mode when OPTION_WATCHDOG_TIMER is enabled. Thanks, disneysw. + + 2.2.2016121001 + Support for FUNtronics FK-10 contributed by disneysw. HARDWARE_FK_10 in keyer_hardware.h; files: keyer_pin_settings_fk_10.h, keyer_features_and_options_fk_10.h, keyer_settings_fk_10.h + + 2.2.2016121201 + Additional work on web interface + + 2.2.2016121202 + Additional work on web interface + Mainstreamed FEATURE_HI_PRECISION_LOOP_TIMING code. No longer an option. (Need to clean out of keyer_feature_and_options files) + + 2.2.2017010301 + FEATURE_AMERICAN_MORSE - American Morse Code sending mode. \= command in the CLI switches to American Morse Code https://en.wikipedia.org/wiki/American_Morse_code + + 2.2.2017011701 + FEATURE_LCD1602_N07DH - added include for Wire.h (Thanks, Hjalmar, OZ1JHM) + + 2.2.2017011702 + Pull request 32 https://github.com/k3ng/k3ng_cw_keyer/pull/32 merged which adds FEATURE_SIDETONE_SWITCH. Also fixed up additional features and pins files. (Thanks, dfannin) + + 2.2.2017011703 + Added OPTION_CW_KEYBOARD_GERMAN (Thanks, Raimo, DL1HTB) + + 2.2.2017012101 + New command mode command R: set serial number + + 2.2.2017020701 + WD9DMP contributed fixes and changes + Reconciled CLI Command/Memory Macro Help code with front comments and actual code so all commands now display with /? + Removed unimplemented Memory Macros from CLI Help + Updated descriptions of CLI Command/Memory Macro functions in help display (some missing serial number lack increment description where present in code) + Fixed issue where the TX ON/TX Off LCD display state in Command Mode could get out of sync with the actual key_tx state + Fixed serial numbers not displaying in LCD and CLI when playing back from Macro or CLI command (please check conditional compilations) + Fixed capialization in HELP display and Status output to be consistent + Changed "$" at end of non-empty memory contents in CLI status display to "_" to help determine if a trailing space is present. + + 2.2.2017020702 + Fixed typo + + 2.2.2017021001 + Fixed typo - 'include ' was commented out (thanks Raimo, DL1HTB) + + 2017.02.12.01 + WD9DMP contributed addition fixes + Changed version number scheme. The 2.2 really isn't significant anymore. + + 2017.02.12.02 + loop_element_lengths sending_mode code error fixed. (Thanks, WD9DMP) + + 2017.02.16.01 + Added note: Have a problem with Keyboard.h not found? See https://github.com/k3ng/k3ng_cw_keyer/issues/35 + + 2017.03.12.01 + WD9DMP contribution: Added checks to see that keyer is NOT in command mode before allowing keyboards or CLI to toggle key_tx flag state, otherwise key commands could key transmitter + Added library.properties file to K3NG_PS2Keyboard library to support the Arduino IDE eye candy bloatware Library Manager + + 2017.03.12.02 + Added CTRL-S keystroke to toggle CMOS Superkeyer Timing on and off in FEATURE_PS2_KEYBOARD and FEATURE_USB_KEYBOARD + + 2017.03.22.01 + Commented out include due to unexplained compilation error in Arduino 1.8.1 + + 2017.03.30.01 + FEATURE_4x4_KEYPAD and FEATURE_3x4_KEYPAD code contributed by Jack, W0XR + + 2017.04.19.01 + Minor change in keyer.h to prevent errors with some versions of Arduino IDE when compiling USB HID features + + 2017.04.19.02 + OPTION_CMOS_SUPER_KEYER_IAMBIC_B_TIMING_ON_BY_DEFAULT and two code fixes contributed by Raimo, DL1HTB, thanks! + + 2017.04.22.01 + Webserver About screen now handles millis() uptime rollover + Bug fix in loop_element_lengths and Internet Linking functionality UDP packet handling + + 2017.04.27.01 + Added bounds checking for void speed_set() + + 2017.05.03.01 + FEATURE_TRAINING_COMMAND_LINE_INTERFACE + First release of Wordsworth training functionality + + 2017.05.05.01 + keyer_training_text_czech.h contributed by Martin, OK1RR + Czech language support for Wordsworth training: OPTION_WORDSWORTH_CZECH + + 2017.05.06.01 + Lots of new functionality in FEATURE_TRAINING_COMMAND_LINE_INTERFACE + keyer_training_text_norsk.h content contributed by Karl, LA3FY + Norwegian language support for Wordsworth training: OPTION_WORDSWORTH_NORSK + + 2017.05.09.01 + FEATURE_TRAINING_COMMAND_LINE_INTERFACE - fixed issue with carriage returns and line feeds causing menus to reprint + + 2017.05.09.02 + Updated FEATURE_4x4_KEYPAD and FEATURE_3x4_KEYPAD to allow memory stacking + + 2017.05.12.01 + Fixed bug with \< and \> commands and carriage returns, and now handle serial number sending through the send buffer rather than direct sending + Fixed issue with non-English characters in Wordsworth by implementing OPTION_NON_ENGLISH_EXTENSIONS within Wordsworth + + 2017.05.12.02 + Added DEBUG_MEMORY_LOCATIONS + + 2017.05.13.01 + Improved reading of serial receive buffer in serial_program_memory to facilitate programming of large memories. Related parameter: serial_program_memory_buffer_size + + 2017.05.13.02 + Added random code group practice + + 2017.05.14.01 + Optimization of serial_program_memory() + + 2017.06.03.01 + Fixed a bug I introduced back in version 2017.05.12.01 or so with memory serial number macros not playing in right sequence (Thanks, Fred, VK2EFL) + + 2017.06.03.02 + Added OPTION_SIDETONE_DIGITAL_OUTPUT_NO_SQUARE_WAVE which changes the sidetone line to go high/low rather than output square wave, for driving an external audio amplifier + + 2017.06.14.02 + Fixed command line interface bug with /> and /< commands and carriage returns + + 2017.06.28.01 + Fixed bug with \T command when FEATURE_BUTTONS is not activated. (Thanks, Павел Бирюков) + + 2017.06.28.02 + Keyer now reports rotary encoder speed changes in K1EL Winkey emulation (Thanks, Marc-Andre, VE2EVN) + + 2017.07.24.01 + Fixed keypad asterisk and pound definitions (Thanks, Fred, VK2EFL) + + 2017.07.31.01 + Fixed bug with memory macro \X not switching to transmitters 4, 5, or 6 (Thanks, Larry, DL6YY) + + 2018.01.05.01 + When entering into program memory mode in command mode, a beep is now emitted rather than a dit + Implemented CLI Receive / Transmit Echo Practice (\K E) + + 2018.01.06.01 + Enhancements to CLI CW Training module + + 2018.01.13.01 + O command in command mode, keyboard input, and CLI enhanced to cycle through sidetone on / off / paddle only; code provided by Marc-Andre, VE2EVN + + 2018.01.13.02 + Improvements to LCD display in practice modes; code provided by Fred, VK2EFL + Minor tweaks to handle LCD displays with lesser number of columns + Bug fixes involving practice modes and garbage left in paddle_echo_buffer + + 2018.01.14.01 + Added \/ (backslash slash) CLI command for Paged Help + Added /@ CLI command for Mill Mode + ESC in CLI will now dump type ahead buffer and stop memory repeat, just like \\ + Added /} CLI command to set potentiometer range + + 2018.01.25.01 + Fixed bug in FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING + + 2018.01.28.01 + Added carriage return and newline to the beginning of several CLI command responses + Add command mode command M - set command mode WPM (command mode now has a speed setting independent of regular keyer speed) + + 2018.01.29.01 + Working on FEATURE_CLI_EXPERT_MENU and FEATURE_SD_CARD_SUPPORT + CLI status now shows speed potentiometer range + + 2018.02.01.01 + Changed Toggle cw send echo CLI command from \: to \; + Deprecated FEATURE_CLI_EXPERT_MENU + Working on Extended CLI Commands /: + Added OPTION_EXCLUDE_EXTENDED_CLI_COMMANDS + Extended CLI Commands + eepromdump + saveeeprom + loadeeprom + printlog + clearlog + ls + cat + Added serial support for ARDUINO_AVR_LEONARDO_ETH + + 2018.02.01.02 + Fixed bug with dit_buffer_off and dah_buffer_off not being initialized from eeprom settings at boot up (Thanks, YU7MW) + + 2018.02.05.01 + Typo fix: ifdef defined(__AVR__) (Thanks, Glen, N1XF https://github.com/k3ng/k3ng_cw_keyer/issues/19) + + 2018.02.05.02 + Fixed https://github.com/k3ng/k3ng_cw_keyer/issues/40 (Thanks, Glen, N1XF) + + 2018.02.07.01 + Added support for 8 column LCD displays + + 2018.02.25.01 + FEATURE_SIDETONE_SWITCH switch line is now set for internal pullup so it won't cause a problem if left floating + + 2018.03.04.01 + Changed wpm_command_mode from uint8_t to unsigned int + Fixed minor bug with junk left in paddle echo buffer after exiting command mode + + 2018.03.04.02 + Added OPTION_DFROBOT_LCD_COMMAND_BUTTONS to use this board https://www.dfrobot.com/wiki/index.php/Arduino_LCD_KeyPad_Shield_(SKU:_DFR0009) with FEATURE_BUTTONS + + 2018.03.08.01 + Additional OPTION_DFROBOT_LCD_COMMAND_BUTTONS code and corresponding DEBUG_BUTTONS code + + 2018.03.08.02 + Added OPTION_EXCLUDE_MILL_MODE + + 2018.03.11.01 + New feature! FEATURE_SEQUENCER Wiki: https://github.com/k3ng/k3ng_cw_keyer/wiki/383-Feature:-Sequencer + define sequencer_1_pin 0 + define sequencer_2_pin 0 + define sequencer_3_pin 0 + define sequencer_4_pin 0 + define sequencer_5_pin 0 + define ptt_input_pin 0 + define sequencer_pins_active_state HIGH + define sequencer_pins_inactive_state LOW + define ptt_line_active_state HIGH + define ptt_line_inactive_state LOW + define tx_key_line_active_state HIGH + define tx_key_line_inactive_state LOW + define ptt_input_pin_active_state LOW + define ptt_input_pin_inactive_state HIGH + New commands: + \:pl - set PTT lead time + \:pt - set PTT tail time + \:pa - PTT active to sequencer active time + \:pi - PTT inactive to sequencer inactive time + \:timing - show all current timing settings + PTT lead and tail times are now stored in EEPROM and setable at runtime with extended commands \:pl and \:pt + Additional documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/225-Sidetone,-PTT,-and-TX-Key-Lines + + 2018.03.14.01 + FEATURE_LCD_FABO_PCF8574 - Added support for FaBo LCD https://github.com/FaBoPlatform/FaBoLCD-PCF8574-Library + + 2018.03.16.01 + Fixed compile error involving lcd_string (Thanks, Jeff, N0MII) + + 2018.03.23.01 + Bug with automatic sending interruption fixed (Thanks, Larry, F6FVY) + + 2018.03.23.02 + Fixed compilation bug with FEATURE_PTT_INTERLOCK when FEATURE_WINKEY_EMULATION was disabled + + 2018.03.24.01 + Support for ARDUINO_MAPLE_MINI contributed by Marcin SP5IOU + HARDWARE_MAPLE_MINI hardware profile in keyer_hardware.h + + 2018.03.29.01 + Support for ARDUINO_GENERIC_STM32F103C (Blue pill boards) contributed by Marcin SP5IOU + HARDWARE_GENERIC_STM32F103C hardware profile in keyer_hardware.h + How to deal with those boards with Arduino: https://www.techshopbd.com/uploads/product_document/STM32bluepillarduinoguide(1).pdf + + 2018.03.30.01 + tx_inhibit and tx_pause pins implemented for use with contest station interlock controllers. Documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/225-Sidetone,-PTT,-and-TX-Key-Lines#tx-inhibit-and-pause + + 2018.03.31.01 + Now have OPTION_WINKEY_2_HOST_CLOSE_NO_SERIAL_PORT_RESET activated in feature files by default. + + 2018.04.07.01 + Improved tx_pause when buffer or memory sending is paused mid-character + + 2018.04.15.01 + Added HARDWARE_MORTTY + + 2018.04.16.01 + Added OPTION_WINKEY_BLINK_PTT_ON_HOST_OPEN - visual cue that Winkey HOST OPEN has occurred + + 2018.04.20.01 + FEATURE_WINKEY_EMULATION - Now clear manual ptt invoke upon host open, host close, and 0A commands + + 2018.04.22.01 + Added OPTION_BLINK_HI_ON_PTT - on units that lack a sidetone speaker, this will blink HI on the PTT line on boot up + Fixed issue in keyer_pin_settings_mortty.h + Added TX Inhibit and TX Pause status in Command Line Interface Status \S command + + 2018.04.23.01 + OPTION_KEEP_PTT_KEYED_WHEN_CHARS_BUFFERED - when Winkeyer Pause command is received, PTT is now de-asserted until Pause is cleared + + 2018.04.29.01 + Deprecated OPTION_KEEP_PTT_KEYED_WHEN_CHARS_BUFFERED. Winkey PINCONFIG PTT bit now sets / unsets ptt_buffer_hold_active + New CLI command \" to activate/deactivate PTT Hold Active When Characters Buffered functionality + + 2018.05.04.01 + Winkey Emulation - minor addition to filtering of values echoed from send_char() + + 2018.05.05.01 + Winkey Emulation - minor bug fix with handling of PTT tail time setting. Also added support in Admin Get Values command to report PTT lead and tail time + + 2018.05.08.01 + Fixed bug in CLI with multiple backspaces / backspaces exceeding number of characters in buffer locking up the keyer (Thanks, WF3T) + + 2018.05.10.01 + Removed OPTION_N1MM_WINKEY_TAB_BUG_WORKAROUND. The bug appears to be gone when testing a recent version of N1MM+. + + 2018.05.17.01 + Updated to better handle STM32 board compilation (Thanks, Marcin, SP5IOU) + + 2018.05.28.01 + Addressed potential issue with random pauses when using Winkey emulation with Writelog and Wintest: Changed check_for_dirty_configuration so it won't write to eeprom when there are characters buffered or PTT is active. Also, Winkey Pinconfig command no longer sets config_dirty. + + 2018.05.31.01 + Fixed design flaw with ptt_input_pin and manual PTT invoke commands not working independently (Thanks, Mek, SQ3RX) + + 2018.07.15.01 + Added FEATURE_LCD_8BIT for controlling standard LCD displays with 8 data lines + + 2018.08.03.01 + Fixed bug FEATURE_FARNSWORTH that was inadvertently introduced with command mode speed feature (Thanks, Jim, W5LA) + + 2018.08.04.01 + Added additional checking of serial port while sending automatic CW in order to better interrupt character sending (Thanks, Max, NG7M) + Added OPTION_DISABLE_SERIAL_PORT_CHECKING_WHILE_SENDING_CW to disable this additional checking if desired or for troubleshooting + + 2018.08.13.01 + More accurate Farnsworth timing; code contributed by Jim, W5LA + + 2018.08.21.01 + Merged pull request https://github.com/k3ng/k3ng_cw_keyer/pull/50 + HARDWARE_YAACWK contributed by Federico Pietro Briata, IZ1GLG + + 2018.08.21.02 + Different Farnsworth timing calculation. Introduced farnsworth_timing_calibration in settings files. + + 2018.08.23.01 + Fixed bug with Farnsworth timing not occurring during intercharacter time, however now overall WPM timing not right... + + 2018.08.25.01 + More work on Farnsworth timing. The timing appears correct now with PARIS testing, however using farnsworth_timing_calibration = 0.35 + Now allow /M0 command to disable Farnsworth + + 2018.08.30.01 + Think we got Farnsworth timing right now. Thanks, Jim, W5LA ! + + 2018.10.17.01 + PTT lead and tail times, and sequencer times can now be set up to 65,535 mS + Updated help text with extended commands + + 2018.10.17.02 + Fixed bug in K1EL Winkeyer Emulation paddle echo + + 2018.10.17.03 + Improved potentiometer noise immunity, added potentiometer_reading_threshold in settings (Thanks, Wolf, DK7OB) + Fixed non-optimal potentiometer speed change comparison (Thanks, Wolf, DK7OB) + + 2018.10.19.01 + Enabling OPTION_DISABLE_SERIAL_PORT_CHECKING_WHILE_SENDING_CW by default. There appear to be lock ups caused by the serial port checking while sending functionality. Investigating. + + 2018.10.21.01 + Fixed Funtronics FK-10 LCD pin definitions that were broken in 2018.07.15.01 (Thanks, Jeff, N0MII ) + + 2018.11.09.01 + Fixed bug with K1EL Winkey emulation with Admin Get Values PTT Hang Time value returned (Thanks, Dariusz, SP2MKI) + Improved reporting of K1EL Winkey emulation PTT tail time and also now have tail time change dynamically with WPM changes to better follow specification + Fixed bug in Beacon Mode where dit and dah paddle would interrupt beacon code + Fixed bug with K1EL Winkey emulation with dead op watchdog enabling / disabling, and reporting (Thanks, Dariusz, SP2MKI) + K1EL Winkey emulation PINCONFIG and Winkeyer Mode commands now write to eeprom + + 2018.11.09.02 + CLI Status now shows paddle and straight key echo state + + 2018.12.25.01 + Fixed potential bug in sleep functionality timing + + 2019.02.05.01 + Fixed bug in command mode K command when in ultimatic mode (Thanks, Rich) + Under Development: FEATURE_SINEWAVE_SIDETONE_USING_TIMER_1 and FEATURE_SINEWAVE_SIDETONE_USING_TIMER_3 in keyer_features_and_options_test.h + + 2019.02.05.02 + Improvement in how K1EL Winkey emulation buffered speed command speed changing and clearing is handled + + 2019.04.06.01 + OPTION_DISABLE_SERIAL_PORT_CHECKING_WHILE_SENDING_CW has been flipped and changed to OPTION_ENABLE_SERIAL_PORT_CHECKING_WHILE_SENDING_CW_MAY_CAUSE_PROBLEMS + Fixed some compiler warnings + + 2019.04.07.01 + Fixed additional compiler warnings + + 2019.04.27.01 + FEATURE_DISPLAY - fixed issue with cpm label and FunKeyer. (Thanks, Fred, VK2EFL) + Fixed bug introduced in version 2019.02.05.01 with not being able to switch between CLI and Winkey at startup using command button when FEATURE_COMMAND_LINE_INTERFACE and FEATURE_WINKEY_EMULATION both compiled in (Thanks, Dave, G8PGO) + + 2019.04.27.02 + Merge of pull request 59 https://github.com/k3ng/k3ng_cw_keyer/pull/59 - HARDWARE_K5BCQ added. (Thanks, woodjrx) + + 2019.04.27.03 + Merge of pull request 51 https://github.com/k3ng/k3ng_cw_keyer/pull/51 - Yaacwk dev (Thanks, federicobriata) + + 2019.04.27.04 + Merge of pull request 60 https://github.com/k3ng/k3ng_cw_keyer/pull/60 - Add support for generic PCF8574 based I2C display (Thanks, W6IPA) + + 2019.04.27.05 + Fixed bug with I2C displays and \+ memory macros with pauses in between prosigned characters (Thanks, Fred, VK2EFL) + + 2019.04.28.01 + Implemented asynchronous EEPROM writes + + 2019.04.29.01 + Fixed bug introduced in 2019.04.27.05 with display of second prosign character (Thanks, Fred, VK2EFL) + + 2019.05.03.01 + Merged pull request https://github.com/k3ng/k3ng_cw_keyer/pull/61 (Thanks, W6IPA) + Merged pull request https://github.com/k3ng/k3ng_cw_keyer/pull/62 (Thanks, W6IPA) + Merged pull request https://github.com/k3ng/k3ng_cw_keyer/pull/63 (Thanks, W6IPA) + New hardware profile: HARDWARE_MEGAKEYER https://github.com/w6ipa/megakeyer (Thanks, W6IPA) {needs documented} + + 2019.05.03.02 + Added potentiometer_enable_pin {needs documented} + Merged pull request https://github.com/k3ng/k3ng_cw_keyer/pull/64 (Thanks, W6IPA) + + 2019.05.15.01 + Merged pull request https://github.com/k3ng/k3ng_cw_keyer/pull/65 (Thanks, federicobriata); yaacwk FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + Merged pull request https://github.com/k3ng/k3ng_cw_keyer/pull/67 (Thanks, OK1CDJ); New hardware profile: HARDWARE_OPENCWKEYER_MK2 https://github.com/ok1cdj/OpenCWKeyerMK2 + Merged pull request https://github.com/k3ng/k3ng_cw_keyer/pull/66 (Thanks, woodjrx); Last update for K5BCQ + + 2019.05.16.01 + Fixed issue with factory reset functionality and asynchronous EEPROM write feature (Thanks, Fred, VK2EFL) + Relocated sidetone_hz_limit_low and sidetone_hz_limit_high setting from ino file to settings.h files (Thanks, Fred, VK2EFL) {needs documented} + + 2019.05.17.01 + service_async_eeprom_write(): Changed EEPROM.write to EEPROM.update to lessen wear and tear on EEPROM and also reduce writing time. (Each EEPROM.write = 3.3 mS) + + 2019.05.17.02 + OPTION_DIRECT_PADDLE_PIN_READS_UNO (Thanks, Fred, VK2EFL) {needs documented} + OPTION_SAVE_MEMORY_NANOKEYER now does direct pin reads rather than digitalRead (Thanks, Fred, VK2EFL) + FEATURE_SD_CARD_SUPPORT - Rolled out SD card support to main keyer_features_and_options.h files {needs documented} + + 2019.05.28.01 + FEATURE_WINKEY_EMULATION - fixed prosign lock up issue with Win-Test (Thanks, Bob, N6TV) + + 2019.05.29.01 + FEATURE_WINKEY_EMULATION - fixed issues with paddle echo (Thanks, Bob, N6TV) + Settings - winkey_paddle_echo_buffer_decode_time_factor changed to winkey_paddle_echo_buffer_decode_timing_factor + Fixed keyer_pin_settings_nanokeyer_rev_*.h to include potentiometer_enable_pin (https://github.com/k3ng/k3ng_cw_keyer/issues/68) (Thanks, rificity) + convert_cw_number_to_ascii() was returning exclamation and not comma (Thanks, W6IPA) + FEATURE_AMERICAN_MORSE - fixed errant submitted change in send_char() (Thanks, Sverre, LA3ZA) + + 2019.06.18.01 + Fixed bug with OPTION_SAVE_MEMORY_NANOKEYER and reading left (dit) paddle + + 2019.08.18.01 + Fixed logic issue with W INKEY_CANCEL_BUFFERED_SPEED_COMMAND that may arise in Logger32 + + 2019.08.18.02 + Fixed issue with OPTION_BLINK_HI_ON_PTT (Thanks, Bob, WO6W) + + 2019.08.18.03 + Merged pull request 75 https://github.com/k3ng/k3ng_cw_keyer/pull/75 - New HW addition - PIC32 Pinguino (Thanks, IZ3GME) + + 2019.08.18.04 + Merged pull request 73 https://github.com/k3ng/k3ng_cw_keyer/pull/73 - code change to allow the speed input device encoder or potentiometer to change the command mode speed when in Command Mode (Thanks VK2EFL and KG6HUM) + + 2019.10.23.01 + Added HARDWARE_MORTTY_REGULAR, HARDWARE_MORTTY_REGULAR_WITH_POTENTIOMETER, HARDWARE_MORTTY_SO2R, HARDWARE_MORTTY_SO2R_WITH_POTENTIOMETER + (Going to depricate Morrty preconfigurations directory shortly...) + + 2019.10.23.02 + https://github.com/k3ng/k3ng_cw_keyer/issues/70 Thanks, SP9RQA + + 2019.10.23.03 + Merged Pull 71 - re-factor analog button functions to support multiple button lines https://github.com/k3ng/k3ng_cw_keyer/pull/71 Thanks, W6IPA + + 2019.10.23.04 + Fixed bug with contest wordspace in K1EL Winkey emulation setmode command. Thanks, Paul K1XM + OPTION_WINKEY_SEND_VERSION_ON_HOST_CLOSE - Made this an option that is disabled by default. + + 2019.10.24.01 + Fixed issue in winkey_unbuffered_speed_command + Fixed some compiler warnings + Fixed compiler redefine errors with Mortty hardware profiles + + 2019.11.07.01 + Merged Pull Request 77 (https://github.com/k3ng/k3ng_cw_keyer/pull/77) Allow for 12 buttons + command - (Thanks, W6IPA) + Manually merged Pull Request 76 (https://github.com/k3ng/k3ng_cw_keyer/pull/76) Fix case sensitive include - (Thanks, Daniele, IU5HKX) + + 2019.11.07.02 + Manual merge of contributed S02R code in progress. Details later. No functionality added or big fixes. + + 2019.11.08.01 + Improved LCD screen refreshes; may improve performance with I2C LCD displays + + 2019.11.08.02 + Fixed bug with character displaying during memory playing; bug introduced with improved LCD screen refreshes + + 2019.11.08.03 + Completed manual merge of contributed code for HARDWARE_YCCC_SO2R_MINI . Testing in progress. (Thanks, Paul, K1XM) + Includes FEATURE_SO2R_BASE, FEATURE_SO2R_SWITCHES, FEATURE_SO2R_ANTENNA + + 2019.11.13.01 + Various code compilation warning cleanups. (Thanks, Paul, K1XM) + + 2019.12.07.01 + Updated version number for multiple merged pull requests + https://github.com/k3ng/k3ng_cw_keyer/pull/78 - Custom startup text - Thanks, Fred, VK2EFL + https://github.com/k3ng/k3ng_cw_keyer/pull/80 - FK-11 Support - Thanks, Ben git1k2 + https://github.com/k3ng/k3ng_cw_keyer/pull/82 - Additional display info - Thanks, Fred, VK2EFL + Fixed errant text at line 7293 from merge of pull request 82 + + 2019.12.07.02 + Added OPTION_PERSONALIZED_STARTUP_SCREEN and custom_startup_field to feature and settings files of all hardware profiles (from pull request 78) + + 2019.12.07.03 + Resolved conflict and merged pull request 83 - Paddle direction parameter change ( https://github.com/k3ng/k3ng_cw_keyer/pull/83/) Thanks, Fred, VK2EFL + Added OPTION_SWAP_PADDLE_PARAMETER_CHANGE_DIRECTION to feature and settings files of all hardware profiles (OPTION from pull request 83) + + 2019.12.07.04 + Resolved conflict and merged pull request 84 - Code to support the correct answer and wrong answer LEDs when progressive 5 character practice is invoked from the CLI ( https://github.com/k3ng/k3ng_cw_keyer/pull/84/) Thanks, Fred, VK2EFL + + 2019.12.07.05 + Resolved conflict and merged pull request 85 - Command mode display memory ( https://github.com/k3ng/k3ng_cw_keyer/pull/85/) Thanks, Fred, VK2EFL + Github conflict resolution tool nuked about 2000 lines at the end on ino file. Fixed that. GRRRRRRR + Added OPTION_DISPLAY_MEMORY_CONTENTS_COMMAND_MODE from pull request 85 to all hardware profile feature files + Updated all pin settings files so that correct_answer_led and wrong_answer_led are always defined + + 2019.12.16.01 + Fixed bug with K1EL Winkeyer emulation cancel buffered speed command (Thanks, Drew, N7DA) + + 2019.12.17.01 + Fixed bug with K1EL Winkeyer emulation with SO2R operation and errant CW being sent after switching radios + + 2020.02.04.01 + Fixed bug with handling of K1EL Winkeyer emulation and handling of PINCONFIG bit 0 and PTT lead time (Thanks, Bill, K1GQ) + + 2020.02.04.02 + Renabled serial port checking during CW sending specifically when automatic sending is happening + OPTION_ENABLE_SERIAL_PORT_CHECKING_WHILE_SENDING_CW_MAY_CAUSE_PROBLEMS has been depricated + OPTION_DISABLE_SERIAL_PORT_CHECKING_WHILE_SENDING_CW added + + 2020.02.10.01 + Added OPTION_WINKEY_PINCONFIG_PTT_CONTROLS_PTT_HOLD - Instead of normal K1EL Winkeyer PINCONFIG PTT bit 0 behavior (activating/deactivating PTT) have this bit control PTT hold when characters are buffered (Thanks, Bill, K1GQ) + + 2020.02.13.01 + Enabling OPTION_WINKEY_PINCONFIG_PTT_CONTROLS_PTT_HOLD by default with YCCC SO2R Mini hardware profile while we continue to troubleshoot issue involving PTT line, SO2R Mini footswitch, and K1EL Winkey emulation PINCONFIG PTT bit 0 + + 2020.02.18.01 + Fix for YCCC SO2R Mini issue involving PTT line, SO2R Mini footswitch, and K1EL Winkey emulation PINCONFIG PTT bit 0 (Thanks, K1GC and JH5GHM) + + 2020.02.18.02 + Corrected fix for YCCC SO2R Mini issue involving PTT line, SO2R Mini footswitch, and K1EL Winkey emulation PINCONFIG PTT bit 0 (Thanks, K1GC and JH5GHM) + In K1EL Winkey emulation character echo is now sent after character CW is sent + + 2020.03.06.01 + Merged pull request 92 - Fix button logic and add test https://github.com/k3ng/k3ng_cw_keyer/pull/92 (Thanks W6IPA) + + 2020.03.07.01 + Added OPTION_WINKEY_PINCONFIG_PTT_CONTROLS_PTT_LINE - K1EL Winkeyer PTT setting activates/deactivates PTT line rather than controls buffered character PTT hold + TinyKeyer - OPTION_DISABLE_SERIAL_PORT_CHECKING_WHILE_SENDING_CW enabled by default for TinyKeyer (keyer_features_and_options_tinykeyer.h) + + 2020.03.07.02 + Command Line Interface - Added \] to disable and enable PTT + + 2020.03.07.03 + Fixed bug with \] and \U interaction (Thanks SV5FRI) + Added \] to serial help + + 2020.03.08.01 + Fixed another bug with \] and \U interaction (Thanks SV5FRI) + + 2020.03.10.01 + Merged pull request 94 - HARDWARE_GENERIC_STM32F103C - Fixed error : call of overloaded 'noTone()' is ambiguous (Thanks 7m4mon) + Merged pull request 93 - Option to disable ultimatic to save space (Thanks, W6IPA) + + 2020.04.13.01 + Fixed compilation error when LCD display is enabled without FEATURE_MEMORIES (Thanks Nigel M0NDE) + + 2020.04.14.01 + Support for FlashAsEEPROM (Thanks Phil M0VSE) + + 2020.04.14.02 + Support for FlashAsEEPROM, take two; ARDUINO_SAMD_VARIANT_COMPLIANCE support (Thanks Phil M0VSE) + + 2020.04.14.03 + Support for FlashAsEEPROM, take three; ARDUINO_SAMD_VARIANT_COMPLIANCE support (Thanks Phil M0VSE) + Added DEBUG_EEPROM_READ_SETTINGS + + 2020.04.15.01 + Support for FlashAsEEPROM, take four; ARDUINO_SAMD_VARIANT_COMPLIANCE support (Thanks Phil M0VSE) + + 2020.04.21.01 + FEATURE_BEACON_SETTING + Command Line Interface: \_ Command - Beacon Mode at Boot Up Enable / Disable (requires FEATURE_BEACON_SETTING) + {Need to update wiki} + + 2020.04.24.01 + Added to settings files: command_mode_acknowledgement_character 'E' + + 2020.04.25.01 + -....- now echoes as dash "-" (-...- is double dash / equals =) + Added Command Mode command: - Enable / disable PTT Line + + 2020.04.26.01 + memory_area_end is now automagically calculated at runtime and is no longer in settings files + + 2020.05.27.01 + The Paddle Echo timing factor is now in eeprom and can be set with the \:pf ### extended CLI command ( https://github.com/k3ng/k3ng_cw_keyer/wiki/310-Feature:-Command-Line-Interface#cli-commands ) + Autospace now has a configurable timing factor which can be set with the \:af ### extended CLI command + cw_echo_timing_factor setting has been deprecated and replaced with default_cw_echo_timing_factor + default_autospace_timing_factor setting created + memory_area_start is now automagically calculated + FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT created which has customizable CW feedback messages for most command mode commands + New settings in settings files for FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT ( https://github.com/k3ng/k3ng_cw_keyer/wiki/320-Feature:-Command-Mode#feedback ) + + 2020.06.03.01 + Fixed issue (hopefully) with memory_area_start automatic cacluation + Fixed issue of command_mode_acknowledgement_character not used for command acknowledgement when FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT is disabled + + 2020.06.03.02 + In FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT added setting command_t_tune_mode for T (tune) command + Without FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT, setting command_mode_acknowledgement_character now is used for T (tune) command + Added setting FEATURE_ETHERNET_DNS {8,8,8,8} to FEATURE_WEB_SERVER and FEATURE_INTERNET_LINK (FEATURE_ETHERNET) + + 2020.06.03.03 + Fixed issue with paddle interruption of stacked memories not being consistent (Thanks, Marcin SP5IOU) + \S memory macro now prints space on CLI and LCD display + + 2020.06.13.01 + HARDWARE_GENERIC_STM32F103C - hard code EEPROM length to account for length() method not being available on this platform + + 2020.06.14.01 + Added [ character as prosign AS for K1EL Winkeyer / N1MM+ compatibility (Thanks, Mark WH7W) + + 2020.07.01.01 + Pull request 98 - Support for configuring the side tone line states - merged. (https://github.com/k3ng/k3ng_cw_keyer/pull/98) (Thanks, Costin Stroie) + New settings: sidetone_line_active_state, sidetone_line_inactive_state + + 2020.07.04.01 + Added OPTION_WINKEY_PROSIGN_COMPATIBILITY for additional character mappings to support K1EL Winkey emulation prosigns + + 2020.07.07.01 + Merge of pull request 99 - Add support for using the NewTone library (https://github.com/k3ng/k3ng_cw_keyer/pull/99) (Thanks, Costin Stroie) + Added support for configuring the sidetone line states HIGH and LOW. + Use the NewTone instead of the standard tone library (~1k smaller code). + Included the NewTone library by Tim Eckel + Added FEATURE_WEB_SERVER and FEATURE_INTERNET_LINK to all features and options files + + 2020.07.17.01 + Merge of pull request 100 - Command buttons 1-3 are not working with ARDUINO_GENERIC_STM32F103C (https://github.com/k3ng/k3ng_cw_keyer/pull/100) (Thanks, 7m4mon) + + 2020.07.26.01 + Extended CLI commands now work with linefeed line terminations in addition to carriage return; Issue 101 (https://github.com/k3ng/k3ng_cw_keyer/issues/101), (Thanks, devcpu) + + 2020.08.17.01 + FEATURE_WINKEY_EMULATION - fixed bug that adversly affected operation with Logger32 + + 2020.08.21.01 + FEATURE_COMMAND_BUTTONS is now called FEATURE_BUTTONS + Command mode is now broken out into its own feature, FEATURE_COMMAND_MODE + + 2020.08.22.01 + Minor tweak in check_buttons() + + 2020.08.23.01 + Added FEATURE_LCD_I2C_FDEBRABANDER + Added settings + lcd_i2c_address_mathertel_PCF8574 0x27 // I2C address of display for FEATURE_LCD_MATHERTEL_PCF8574 + lcd_i2c_address_fdebrander_lcd 0x27 // I2C address of display for FEATURE_LCD_I2C_FDEBRABANDER + lcd_i2c_address_ydv1_lcd 0x27 // I2C address of display for FEATURE_LCD_YDv1 + lcd_i2c_address_sainsmart_lcd 0x27 // I2C address of display for FEATURE_LCD_SAINSMART_I2C + + 2020.08.24.01 + In pin settings files clarified the function of pins for FEATURE_CW_DECODER & OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR + Updated CW Decoder Wiki Page https://github.com/k3ng/k3ng_cw_keyer/wiki/385-Feature:-CW-Decoder + + 2020.08.28.02 + Merged pull request 103 Change personalized startup operation ( https://github.com/k3ng/k3ng_cw_keyer/pull/103 ) (Thanks, VK2EFL) + + 2020.08.29.01 + Implemented fix for memories not halting after a paddle press ( https://groups.io/g/radioartisan/message/13500 ) (Thanks, Gary, AF8A, for code ) + + 2020.11.01.01 + Fixed issues with FEATURE_WEB_SERVER and FEATURE_INTERNET_LINK when compiled with main features and settings files. + + 2021.01.24.01 + Added the \:comp extended command to change keying compensation in the CLI + keying_compensation is now stored in eeprom + NOTE: increasing keying compensation above 35 mS at ~24 WPM causes wonkiness. Probably need to add code to limit this value based on current WPM + + 2021.01.25.01 + Keying compensation now displayed in \S status CLI command + Added Q command to FEATURE_COMMAND_MODE: Adjust keying compensation (left paddle = increase, right paddle = decrease) + FEATURE_COMMAND_LINE_INTERFACE \:comp command now displays warning if keying compensation setting is probably too high + + 2021.03.10.01 + Merged Pull Request 108 https://github.com/k3ng/k3ng_cw_keyer/pull/108 from VK2EFL + Adds to the \S CLI listing a display of the current tx's lead and tail times, plus the hang time (in wordspace units) plus the memory repeat time. + Merged Pull Request 109 https://github.com/k3ng/k3ng_cw_keyer/pull/109 from VK2EFL + Adds the option of a memory repeat time between repeated playing of memory 1 when in beacon mode. + Adds the option of having the PTT tail time added to the PTT at the end of each playing of memory 1 when in beacon mode. + + 2021.03.20.01 + Updated version number for merging of Pull Request 110 https://github.com/k3ng/k3ng_cw_keyer/pull/110 from FrugalGuy (Ron, KO4RON) + Adds FEATURE_LCD_BACKLIGHT_AUTO_DIM + + Documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki + + Support: https://groups.io/g/radioartisan ( Please do not email K3NG directly for support. Thanks ) + + YouTube Channel: https://www.youtube.com/channel/UC5o8UM1-heT5kJbwnJRkUYg + + + This code is currently maintained for and compiled with Arduino 1.8.x. Your mileage may vary with other versions. + + + ATTENTION: LIBRARY FILES MUST BE PUT IN LIBRARIES DIRECTORIES AND NOT THE INO SKETCH DIRECTORY !!!! + + FOR EXAMPLE: + + K3NG_PS2Keyboard.h, K3NG_PS2Keyboard.cpp -----> \Arduino\Sketchbook\libraries\K3NG_PS2Keyboard\ + Goertz.h, Goertz.cpp ------------------------> \Arduino\Sketchbook\libraries\Goertz\ + + + "Make good code and share it with friends." + + +If you offer a hardware kit using this software, show your appreciation by sending the author a complimentary kit or a bottle of bourbon ;-) + + + +*/ + +#define CODE_VERSION "2021.03.20.01" +#define eeprom_magic_number 41 // you can change this number to have the unit re-initialize EEPROM + +#include +#include "keyer_hardware.h" + +#if defined(ARDUINO_SAM_DUE) + #include + #include + #define tone toneDUE + #define noTone noToneDUE +#elif defined(ARDUINO_MAPLE_MINI)|| defined(ARDUINO_GENERIC_STM32F103C) || defined(__STM32F1__) + #include + #include + #include + #include "keyer_stm32duino.h" +#elif defined(_BOARD_PIC32_PINGUINO_) + #include +#elif defined(ARDUINO_SAMD_VARIANT_COMPLIANCE) + #include +#else + #include + #include + #include +#endif //ARDUINO_SAM_DUE + +#if defined(HARDWARE_OPENCWKEYER_MK2) + #include "keyer_features_and_options_opencwkeyer_mk2.h" +#elif defined(HARDWARE_NANOKEYER_REV_B) + #include "keyer_features_and_options_nanokeyer_rev_b.h" +#elif defined(HARDWARE_NANOKEYER_REV_D) + #include "keyer_features_and_options_nanokeyer_rev_d.h" +#elif defined(HARDWARE_OPEN_INTERFACE) + #include "keyer_features_and_options_open_interface.h" +#elif defined(HARDWARE_TINYKEYER) + #include "keyer_features_and_options_tinykeyer.h" +#elif defined(HARDWARE_FK_10) + #include "keyer_features_and_options_fk_10.h" +#elif defined(HARDWARE_FK_11) + #include "keyer_features_and_options_fk_11.h" +#elif defined(HARDWARE_MAPLE_MINI)//sp5iou 20180328 + #include "keyer_features_and_options_maple_mini.h" +#elif defined(HARDWARE_GENERIC_STM32F103C)//sp5iou 20180329 + #include "keyer_features_and_options_generic_STM32F103C.h" +#elif defined(HARDWARE_MORTTY) + #include "keyer_features_and_options_mortty.h" +#elif defined(HARDWARE_MORTTY_REGULAR) + #include "keyer_features_and_options_mortty_regular.h" +#elif defined(HARDWARE_MORTTY_REGULAR_WITH_POTENTIOMETER) + #include "keyer_features_and_options_mortty_regular_with_potentiometer.h" +#elif defined(HARDWARE_MORTTY_SO2R) + #include "keyer_features_and_options_mortty_so2r.h" +#elif defined(HARDWARE_MORTTY_SO2R_WITH_POTENTIOMETER) + #include "keyer_features_and_options_mortty_so2r_with_potentiometer.h" +#elif defined(HARDWARE_K5BCQ) + #include "keyer_features_and_options_k5bcq.h" +#elif defined(HARDWARE_MEGAKEYER) + #include "keyer_features_and_options_megakeyer.h" +#elif defined(HARDWARE_TEST_EVERYTHING) + #include "keyer_features_and_options_test_everything.h" +#elif defined(HARDWARE_YAACWK) + #include "keyer_features_and_options_yaacwk.h" +#elif defined(HARDWARE_TEST) + #include "keyer_features_and_options_test.h" +#elif defined(HARDWARE_IZ3GME) + #include "keyer_features_and_options_iz3gme.h" +#elif defined(HARDWARE_YCCC_SO2R_MINI) + #include "keyer_features_and_options_yccc_so2r_mini.h" +#else + #include "keyer_features_and_options.h" +#endif + +#include "keyer.h" + +#ifdef FEATURE_EEPROM_E24C1024 + #include + #define EEPROM EEPROM1024 +#endif + +#include "keyer_dependencies.h" +#include "keyer_debug.h" + +#if defined(HARDWARE_OPENCWKEYER_MK2) + #include "keyer_pin_settings_opencwkeyer_mk2.h" + #include "keyer_settings_opencwkeyer_mk2.h" +#elif defined(HARDWARE_NANOKEYER_REV_B) + #include "keyer_pin_settings_nanokeyer_rev_b.h" + #include "keyer_settings_nanokeyer_rev_b.h" +#elif defined(HARDWARE_NANOKEYER_REV_D) + #include "keyer_pin_settings_nanokeyer_rev_d.h" + #include "keyer_settings_nanokeyer_rev_d.h" +#elif defined(HARDWARE_OPEN_INTERFACE) + #include "keyer_pin_settings_open_interface.h" + #include "keyer_settings_open_interface.h" +#elif defined(HARDWARE_TINYKEYER) + #include "keyer_pin_settings_tinykeyer.h" + #include "keyer_settings_tinykeyer.h" +#elif defined(HARDWARE_FK_10) + #include "keyer_pin_settings_fk_10.h" + #include "keyer_settings_fk_10.h" +#elif defined(HARDWARE_FK_11) + #include "keyer_pin_settings_fk_11.h" + #include "keyer_settings_fk_11.h" +#elif defined(HARDWARE_MAPLE_MINI) + #include "keyer_pin_settings_maple_mini.h" + #include "keyer_settings_maple_mini.h" +#elif defined(HARDWARE_GENERIC_STM32F103C) + #include "keyer_pin_settings_generic_STM32F103C.h" + #include "keyer_settings_generic_STM32F103C.h" +#elif defined(HARDWARE_MORTTY) + #include "keyer_pin_settings_mortty.h" + #include "keyer_settings_mortty.h" +#elif defined(HARDWARE_MORTTY_REGULAR) + #include "keyer_pin_settings_mortty_regular.h" + #include "keyer_settings_mortty_regular.h" +#elif defined(HARDWARE_MORTTY_REGULAR_WITH_POTENTIOMETER) + #include "keyer_pin_settings_mortty_regular_with_potentiometer.h" + #include "keyer_settings_mortty_regular_with_potentiometer.h" +#elif defined(HARDWARE_MORTTY_SO2R) + #include "keyer_pin_settings_mortty_so2r.h" + #include "keyer_settings_mortty_so2r.h" +#elif defined(HARDWARE_MORTTY_SO2R_WITH_POTENTIOMETER) + #include "keyer_pin_settings_mortty_so2r_with_potentiometer.h" + #include "keyer_settings_mortty_so2r_with_potentiometer.h" +#elif defined(HARDWARE_K5BCQ) + #include "keyer_pin_settings_k5bcq.h" + #include "keyer_settings_k5bcq.h" +#elif defined(HARDWARE_MEGAKEYER) + #include "keyer_pin_settings_megakeyer.h" + #include "keyer_settings_megakeyer.h" +#elif defined(HARDWARE_TEST_EVERYTHING) + #include "keyer_pin_settings_test_everything.h" + #include "keyer_settings_test_everything.h" +#elif defined(HARDWARE_YAACWK) + #include "keyer_pin_settings_yaacwk.h" + #include "keyer_settings_yaacwk.h" +#elif defined(HARDWARE_TEST) + #include "keyer_pin_settings_test.h" + #include "keyer_settings_test.h" +#elif defined(HARDWARE_IZ3GME) + #include "keyer_pin_settings_iz3gme.h" + #include "keyer_settings_iz3gme.h" +#elif defined(HARDWARE_YCCC_SO2R_MINI) + #include "keyer_pin_settings_yccc_so2r_mini.h" + #include "keyer_settings_yccc_so2r_mini.h" +#else + #include "keyer_pin_settings.h" + #include "keyer_settings.h" +#endif + +#if defined(FEATURE_BUTTONS) + #include "src/buttonarray/buttonarray.h" +#endif + +#if defined(FEATURE_SIDETONE_NEWTONE) && !defined(OPTION_SIDETONE_DIGITAL_OUTPUT_NO_SQUARE_WAVE) + #include + #define tone NewTone + #define noTone noNewTone +#endif //FEATURE_SIDETONE_NEWTONE + +#if defined(FEATURE_SLEEP) + #include // It should be different library for ARM sp5iou +#endif + +#if defined(FEATURE_PS2_KEYBOARD) + #include +#endif + +#if defined(FEATURE_LCD_4BIT) || defined(FEATURE_LCD1602_N07DH) || defined (FEATURE_LCD_8BIT) // works on 3.2V supply and logic, but do not work on every pins (SP5IOU) + #include + #include +#endif + +#if defined(FEATURE_LCD_ADAFRUIT_I2C) || defined(FEATURE_LCD_ADAFRUIT_BACKPACK) || defined(FEATURE_LCD_YDv1) || defined(FEATURE_LCD_SAINSMART_I2C) || defined(FEATURE_LCD_FABO_PCF8574) || defined(FEATURE_LCD_MATHERTEL_PCF8574) + #include +#endif + +#if defined(FEATURE_LCD_YDv1) + #include +#endif + +#if defined(FEATURE_LCD_ADAFRUIT_I2C) + #include + #include +#endif + +#if defined(FEATURE_LCD_ADAFRUIT_BACKPACK) + #include +#endif + +#if defined(FEATURE_LCD_SAINSMART_I2C) + #include +#endif + +#if defined(FEATURE_LCD_FABO_PCF8574) + #include +#endif + +#if defined(FEATURE_LCD_MATHERTEL_PCF8574) + #include +#endif + +#if defined(FEATURE_LCD_I2C_FDEBRABANDER) + #include +#endif + +#if defined(FEATURE_LCD_HD44780) + #include + #include + #include + #define WIRECLOCK 400000L +#endif + +#if defined(FEATURE_TRAINING_COMMAND_LINE_INTERFACE) + // #include +#endif + +#if defined(FEATURE_CW_DECODER) && defined(OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR) + #include +#endif + +#if defined(FEATURE_ETHERNET) +#if !defined(ARDUINO_MAPLE_MINI) && !defined(ARDUINO_GENERIC_STM32F103C) //sp5iou 20180329 + #include // if this is not included, compilation fails even though all ethernet code is #ifdef'ed out + #if defined(FEATURE_INTERNET_LINK) + #include + #endif //FEATURE_INTERNET_LINK +#endif //!defined(ARDUINO_MAPLE_MINI) && !defined(ARDUINO_GENERIC_STM32F103C) //sp5iou 20180329 +#endif //FEATURE_ETHERNET + + +#if defined(FEATURE_USB_KEYBOARD) || defined(FEATURE_USB_MOUSE) // note_usb_uncomment_lines + // #include // Arduino 1.6.x (and maybe 1.5.x) has issues with these three lines, moreover we noted that Arduino 1.8.6 it's not afected by an issue during USB Shield SPI init see https://github.com/felis/USB_Host_Shield_2.0/issues/390 + // #include // Uncomment the three lines if you are using FEATURE_USB_KEYBOARD or FEATURE_USB_MOUSE + // #include // Note: the most updated USB Library can be downloaded at https://github.com/felis/USB_Host_Shield_2.0 +#endif + +#if defined(FEATURE_CW_COMPUTER_KEYBOARD) + #include // Have a problem with Keyboard.h not found? See https://github.com/k3ng/k3ng_cw_keyer/issues/35 + // For some unknown reason this line uncommented in Arduino 1.8.1 causes compilation error (sigh) +#endif //defined(FEATURE_CW_COMPUTER_KEYBOARD) + +#if defined(FEATURE_4x4_KEYPAD)|| defined (FEATURE_3x4_KEYPAD) + #include +#endif + +#if defined(FEATURE_SD_CARD_SUPPORT) + #include + #include +#endif //FEATURE_SD_CARD_SUPPORT + +#if defined(ARDUINO_SAMD_VARIANT_COMPLIANCE) + extern uint32_t __get_MSP(void); + #define SP __get_MSP() +#endif + + +#define memory_area_start (sizeof(configuration)+5) + +// Variables and stuff +struct config_t { // 120 bytes total + + uint8_t paddle_mode; + uint8_t keyer_mode; + uint8_t sidetone_mode; + uint8_t pot_activated; + uint8_t length_wordspace; + uint8_t autospace_active; + uint8_t current_ptt_line; + uint8_t current_tx; + uint8_t weighting; + uint8_t dit_buffer_off; + uint8_t dah_buffer_off; + uint8_t cmos_super_keyer_iambic_b_timing_percent; + uint8_t cmos_super_keyer_iambic_b_timing_on; + uint8_t link_receive_enabled; + uint8_t paddle_interruption_quiet_time_element_lengths; + uint8_t wordsworth_wordspace; + uint8_t wordsworth_repetition; + uint8_t cli_mode; + uint8_t ptt_buffer_hold_active; + uint8_t ptt_disabled; + uint8_t beacon_mode_on_boot_up; + uint8_t keying_compensation; + // 22 bytes + + unsigned int wpm; + unsigned int hz_sidetone; + unsigned int dah_to_dit_ratio; + unsigned int wpm_farnsworth; + unsigned int memory_repeat_time; + unsigned int wpm_command_mode; + unsigned int link_receive_udp_port; + unsigned int wpm_ps2_usb_keyboard; + unsigned int wpm_cli; + unsigned int wpm_winkey; + unsigned int cw_echo_timing_factor; + unsigned int autospace_timing_factor; + // 24 bytes + + uint8_t ip[4]; + uint8_t gateway[4]; + uint8_t subnet[4]; + uint8_t dns_server[4]; + // 16 bytes + + uint8_t link_send_ip[4][FEATURE_INTERNET_LINK_MAX_LINKS]; // FEATURE_INTERNET_LINK_MAX_LINKS = 2 + uint8_t link_send_enabled[FEATURE_INTERNET_LINK_MAX_LINKS]; + unsigned int link_send_udp_port[FEATURE_INTERNET_LINK_MAX_LINKS]; + // 14 bytes + + unsigned int ptt_lead_time[6]; + unsigned int ptt_tail_time[6]; + unsigned int ptt_active_to_sequencer_active_time[5]; + unsigned int ptt_inactive_to_sequencer_inactive_time[5]; + // 44 bytes + + int sidetone_volume; + // 2 bytes + +} configuration; + + + +byte sending_mode = UNDEFINED_SENDING; +byte command_mode_disable_tx = 0; +byte current_tx_key_line = tx_key_line_1; +#ifdef FEATURE_SO2R_BASE + byte current_tx_ptt_line = ptt_tx_1; +#endif +byte manual_ptt_invoke = 0; +byte manual_ptt_invoke_ptt_input_pin = 0; +byte qrss_dit_length = initial_qrss_dit_length; +byte keyer_machine_mode = KEYER_NORMAL; // KEYER_NORMAL, BEACON, KEYER_COMMAND_MODE +byte char_send_mode = 0; // CW, HELL, AMERICAN_MORSE +byte key_tx = 0; // 0 = tx_key_line control suppressed +byte dit_buffer = 0; // used for buffering paddle hits in iambic operation +byte dah_buffer = 0; // used for buffering paddle hits in iambic operation +byte button0_buffer = 0; +byte being_sent = 0; // SENDING_NOTHING, SENDING_DIT, SENDING_DAH +byte key_state = 0; // 0 = key up, 1 = key down +byte config_dirty = 0; +unsigned long ptt_time = 0; +byte ptt_line_activated = 0; +byte speed_mode = SPEED_NORMAL; +#if defined(FEATURE_COMMAND_LINE_INTERFACE) || defined(FEATURE_PS2_KEYBOARD) || defined(FEATURE_MEMORY_MACROS) || defined(FEATURE_MEMORIES) || defined(FEATURE_COMMAND_MODE) + unsigned int serial_number = 1; +#endif +byte pause_sending_buffer = 0; +byte length_letterspace = default_length_letterspace; +//byte keying_compensation = default_keying_compensation; +byte first_extension_time = default_first_extension_time; +byte ultimatic_mode = ULTIMATIC_NORMAL; +float ptt_hang_time_wordspace_units = default_ptt_hang_time_wordspace_units; +byte last_sending_mode = MANUAL_SENDING; +byte zero = 0; +byte iambic_flag = 0; +unsigned long last_config_write = 0; +uint16_t memory_area_end = 0; + +#ifdef FEATURE_SLEEP + unsigned long last_activity_time = 0; +#endif + +#ifdef FEATURE_LCD_BACKLIGHT_AUTO_DIM + unsigned long last_active_time = 0; +#endif + + +#ifdef FEATURE_DISPLAY + enum lcd_statuses {LCD_CLEAR, LCD_REVERT, LCD_TIMED_MESSAGE, LCD_SCROLL_MSG}; + #define default_display_msg_delay 1000 +#endif //FEATURE_DISPLAY + +#ifdef FEATURE_LCD_ADAFRUIT_I2C + #define RED 0x1 + #define YELLOW 0x3 + #define GREEN 0x2 + #define TEAL 0x6 + #define BLUE 0x4 + #define VIOLET 0x5 + #define WHITE 0x7 + byte lcdcolor = GREEN; // default color for RGB LCD display +#endif //FEATURE_LCD_ADAFRUIT_I2C + +#if defined(OPTION_WINKEY_2_SUPPORT) && defined(FEATURE_WINKEY_EMULATION) + byte wk2_mode = 1; + #ifndef FEATURE_SO2R_BASE + byte wk2_both_tx_activated = 0; + #endif //FEATURE_SO2R_BASE + byte wk2_paddle_only_sidetone = 0; +#endif //defined(OPTION_WINKEY_2_SUPPORT) && defined(FEATURE_WINKEY_EMULATION) + +#ifdef FEATURE_DISPLAY + byte lcd_status = LCD_CLEAR; + unsigned long lcd_timed_message_clear_time = 0; + byte lcd_previous_status = LCD_CLEAR; + byte lcd_scroll_buffer_dirty = 0; + String lcd_scroll_buffer[LCD_ROWS]; + byte lcd_scroll_flag = 0; + byte lcd_paddle_echo = 1; + byte lcd_send_echo = 1; +#endif //FEATURE_DISPLAY + +#ifdef DEBUG_VARIABLE_DUMP + long dit_start_time; + long dit_end_time; + long dah_start_time; + long dah_end_time; +#endif //DEBUG_VARIABLE_DUMP + +#ifdef FEATURE_BUTTONS + int button_array_high_limit[analog_buttons_number_of_buttons]; + int button_array_low_limit[analog_buttons_number_of_buttons]; + long button_last_add_to_send_buffer_time = 0; +#endif //FEATURE_BUTTONS + +byte pot_wpm_low_value; + +#ifdef FEATURE_POTENTIOMETER + byte pot_wpm_high_value; + byte last_pot_wpm_read; + int pot_full_scale_reading = default_pot_full_scale_reading; +#endif //FEATURE_POTENTIOMETER + +#if defined(FEATURE_SERIAL) + #if !defined(OPTION_DISABLE_SERIAL_PORT_CHECKING_WHILE_SENDING_CW) + byte loop_element_lengths_breakout_flag; + byte dump_current_character_flag; + #endif + byte incoming_serial_byte; + long primary_serial_port_baud_rate; + byte cw_send_echo_inhibit = 0; + #ifdef FEATURE_COMMAND_LINE_INTERFACE + byte serial_backslash_command; + byte cli_paddle_echo = cli_paddle_echo_on_at_boot; + byte cli_prosign_flag = 0; + byte cli_wait_for_cr_to_send_cw = 0; + #if defined(FEATURE_STRAIGHT_KEY_ECHO) + byte cli_straight_key_echo = cli_straight_key_echo_on_at_boot; + #endif + #endif //FEATURE_COMMAND_LINE_INTERFACE +#endif //FEATURE_SERIAL + +byte send_buffer_array[send_buffer_size]; +byte send_buffer_bytes = 0; +byte send_buffer_status = SERIAL_SEND_BUFFER_NORMAL; + +#ifdef FEATURE_MEMORIES + byte play_memory_prempt = 0; + long last_memory_button_buffer_insert = 0; + byte repeat_memory = 255; + unsigned long last_memory_repeat_time = 0; +#endif //FEATURE_MEMORIES + +#if defined(FEATURE_SERIAL) + byte primary_serial_port_mode = SERIAL_CLI; +#endif //FEATURE_SERIAL + +#ifdef FEATURE_WINKEY_EMULATION + byte winkey_serial_echo = 1; + byte winkey_host_open = 0; + unsigned int winkey_last_unbuffered_speed_wpm = 0; + byte winkey_speed_state = WINKEY_UNBUFFERED_SPEED; + byte winkey_buffer_counter = 0; + byte winkey_buffer_pointer = 0; + byte winkey_dit_invoke = 0; + byte winkey_dah_invoke = 0; + long winkey_paddle_echo_buffer = 0; + byte winkey_paddle_echo_activated = 0; + unsigned long winkey_paddle_echo_buffer_decode_time = 0; + byte winkey_sending = 0; + byte winkey_interrupted = 0; + byte winkey_xoff = 0; + byte winkey_session_ptt_tail = 0; + byte winkey_pinconfig_ptt_bit = 1; + #ifdef OPTION_WINKEY_SEND_BREAKIN_STATUS_BYTE + byte winkey_breakin_status_byte_inhibit = 0; + #endif //OPTION_WINKEY_SEND_BREAKIN_STATUS_BYTE +#endif //FEATURE_WINKEY_EMULATION + +#ifdef FEATURE_PS2_KEYBOARD + byte ps2_keyboard_mode = PS2_KEYBOARD_NORMAL; + byte ps2_keyboard_command_buffer[25]; + byte ps2_keyboard_command_buffer_pointer = 0; +#endif //FEATURE_PS2_KEYBOARD + + +#ifdef FEATURE_HELL + PROGMEM const char hell_font1[] = {B00111111, B11100000, B00011001, B11000000, B01100011, B00000001, B10011100, B00111111, B11100000, // A + B00110000, B00110000, B11111111, B11000011, B00110011, B00001100, B11001100, B00011100, B11100000, // B + B00111111, B11110000, B11000000, B11000011, B00000011, B00001100, B00001100, B00110000, B00110000, // C + B00110000, B00110000, B11111111, B11000011, B00000011, B00001100, B00001100, B00011111, B11100000, // D + B00111111, B11110000, B11001100, B11000011, B00110011, B00001100, B00001100, B00110000, B00110000, + B00111111, B11110000, B00001100, B11000000, B00110011, B00000000, B00001100, B00000000, B00110000, + B00111111, B11110000, B11000000, B11000011, B00000011, B00001100, B11001100, B00111111, B00110000, + B00111111, B11110000, B00001100, B00000000, B00110000, B00000000, B11000000, B00111111, B11110000, + B00000000, B00000000, B00000000, B00000011, B11111111, B00000000, B00000000, B00000000, B00000000, + B00111100, B00000000, B11000000, B00000011, B00000000, B00001100, B00000000, B00111111, B11110000, + B00111111, B11110000, B00001100, B00000000, B01110000, B00000011, B00110000, B00111000, B11100000, + B00111111, B11110000, B11000000, B00000011, B00000000, B00001100, B00000000, B00110000, B00000000, + B00111111, B11110000, B00000001, B10000000, B00001100, B00000000, B00011000, B00111111, B11110000, + B00111111, B11110000, B00000011, B10000000, B00111000, B00000011, B10000000, B00111111, B11110000, + B00111111, B11110000, B11000000, B11000011, B00000011, B00001100, B00001100, B00111111, B11110000, + B00110000, B00110000, B11111111, B11000011, B00110011, B00000000, B11001100, B00000011, B11110000, + B00111111, B11110000, B11000000, B11000011, B11000011, B00001111, B11111100, B11110000, B00000000, + B00111111, B11110000, B00001100, B11000000, B00110011, B00000011, B11001100, B00111001, B11100000, + B00110001, B11100000, B11001100, B11000011, B00110011, B00001100, B11001100, B00011110, B00110000, + B00000000, B00110000, B00000000, B11000011, B11111111, B00000000, B00001100, B00000000, B00110000, + B00111111, B11110000, B11000000, B00000011, B00000000, B00001100, B00000000, B00111111, B11110000, + B00111111, B11110000, B01110000, B00000000, B01110000, B00000000, B01110000, B00000000, B01110000, + B00011111, B11110000, B11000000, B00000001, B11110000, B00001100, B00000000, B00011111, B11110000, + B00111000, B01110000, B00110011, B00000000, B01111000, B00000011, B00110000, B00111000, B01110000, + B00000000, B01110000, B00000111, B00000011, B11110000, B00000000, B01110000, B00000000, B01110000, + B00111000, B00110000, B11111000, B11000011, B00110011, B00001100, B01111100, B00110000, B01110000}; // Z + + PROGMEM const char hell_font2[] = {B00011111, B11100000, B11000000, B11000011, B00000011, B00001100, B00001100, B00011111, B11100000, // 0 + B00000000, B00000000, B00000011, B00000000, B00000110, B00001111, B11111100, B00000000, B00000000, + B00111000, B01100000, B11110000, B11000011, B00110011, B00001100, B01111000, B00110000, B00000000, + B11000000, B00000011, B00000000, B11000110, B00110011, B00001100, B11111100, B00011110, B00000000, + B00000111, B11111000, B00011000, B00000000, B01100000, B00001111, B11111100, B00000110, B00000000, + B00110000, B00000000, B11000000, B00000011, B00011111, B10000110, B01100110, B00001111, B00011000, + B00011111, B11110000, B11001100, B01100011, B00011000, B11001100, B01100000, B00011111, B00000000, + B01110000, B00110000, B01110000, B11000000, B01110011, B00000000, B01111100, B00000000, B01110000, + B00111100, B11110001, B10011110, B01100110, B00110001, B10011001, B11100110, B00111100, B11110000, + B00000011, B11100011, B00011000, B11000110, B01100011, B00001100, B00001100, B00011111, B11100000}; // 9 + + PROGMEM const char hell_font3[] = {B00000011, B00000000, B00001100, B00000001, B11111110, B00000000, B11000000, B00000011, B00000000, + B00000011, B00000000, B00001100, B00000000, B00110000, B00000000, B11000000, B00000011, B00000000, + B00000000, B00110000, B00000000, B11001110, B01110011, B00000000, B01111100, B00000000, B00000000, + B01110000, B00000000, B01110000, B00000000, B01110000, B00000000, B01110000, B00000000, B01110000, + B00111000, B00000000, B11100000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, + B00001100, B00000001, B11110000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, + B00000000, B00111000, B00000011, B10000000, B00000000, B00000000, B00000000, B00000000, B00000000, + B00001100, B11000000, B00110011, B00000000, B11001100, B00000011, B00110000, B00001100, B11000000, + B01110000, B00111000, B01110011, B10000000, B01111000, B00000000, B00000000, B00000000, B00000000, + B00000000, B00000000, B00000000, B00000000, B01111000, B00000111, B00111000, B01110000, B00111000, + B00000000, B00000000, B01110011, B10000001, B11001110, B00000000, B00000000, B00000000, B00000000, + 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + +#endif //FEATURE_HELL + +#ifdef FEATURE_DEAD_OP_WATCHDOG + byte dead_op_watchdog_active = 1; + byte dit_counter = 0; + byte dah_counter = 0; +#endif //FEATURE_DEAD_OP_WATCHDOG + +#ifdef FEATURE_BUTTONS + #ifdef OPTION_REVERSE_BUTTON_ORDER + ButtonArray button_array(analog_buttons_pin, analog_buttons_number_of_buttons, true); + #else + ButtonArray button_array(analog_buttons_pin, analog_buttons_number_of_buttons, false); + #endif +#endif //FEATURE_BUTTONS + +#ifdef FEATURE_ROTARY_ENCODER // Rotary Encoder State Tables + #ifdef OPTION_ENCODER_HALF_STEP_MODE // Use the half-step state table (emits a code at 00 and 11) + const unsigned char ttable[6][4] = { + {0x3 , 0x2, 0x1, 0x0}, {0x23, 0x0, 0x1, 0x0}, + {0x13, 0x2, 0x0, 0x0}, {0x3 , 0x5, 0x4, 0x0}, + {0x3 , 0x3, 0x4, 0x10}, {0x3 , 0x5, 0x3, 0x20} + }; + #else // Use the full-step state table (emits a code at 00 only) + const unsigned char ttable[7][4] = { + {0x0, 0x2, 0x4, 0x0}, {0x3, 0x0, 0x1, 0x10}, + {0x3, 0x2, 0x0, 0x0}, {0x3, 0x2, 0x1, 0x0}, + {0x6, 0x0, 0x4, 0x0}, {0x6, 0x5, 0x0, 0x20}, + {0x6, 0x5, 0x4, 0x0}, + }; + + #endif //OPTION_ENCODER_HALF_STEP_MODE + unsigned char state = 0; + #define DIR_CCW 0x10 // CW Encoder Code (do not change) + #define DIR_CW 0x20 // CCW Encoder Code (do not change) +#endif //FEATURE_ENCODER_SUPPORT + +#ifdef FEATURE_USB_KEYBOARD + unsigned long usb_keyboard_special_mode_start_time = 0; + String keyboard_string; +#endif //FEATURE_USB_KEYBOARD + +#if defined(FEATURE_USB_MOUSE) || defined(FEATURE_USB_KEYBOARD) + byte usb_dit = 0; + byte usb_dah = 0; +#endif + +#if defined(FEATURE_PS2_KEYBOARD) + #ifdef OPTION_USE_ORIGINAL_VERSION_2_1_PS2KEYBOARD_LIB + PS2Keyboard keyboard; + #else //OPTION_USE_ORIGINAL_VERSION_2_1_PS2KEYBOARD_LIB + K3NG_PS2Keyboard keyboard; + #endif //OPTION_USE_ORIGINAL_VERSION_2_1_PS2KEYBOARD_LIB +#endif + +#if defined(FEATURE_LCD_4BIT) || defined(FEATURE_LCD1602_N07DH) + LiquidCrystal lcd(lcd_rs, lcd_enable, lcd_d4, lcd_d5, lcd_d6, lcd_d7); +#endif + +#if defined(FEATURE_LCD_8BIT) + LiquidCrystal lcd(lcd_rs, lcd_enable, lcd_d0, lcd_d1, lcd_d2, lcd_d3, lcd_d4, lcd_d5, lcd_d6, lcd_d7); +#endif + +#if defined(FEATURE_LCD_ADAFRUIT_I2C) + Adafruit_RGBLCDShield lcd = Adafruit_RGBLCDShield(); +#endif + +#if defined(FEATURE_LCD_ADAFRUIT_BACKPACK) + Adafruit_LiquidCrystal lcd(0); +#endif + +#if defined(FEATURE_LCD_SAINSMART_I2C) + // #define I2C_ADDR 0x27 + // #define BACKLIGHT_PIN 3 + // #define En_pin 2 + // #define Rw_pin 1 + // #define Rs_pin 0 + // #define D4_pin 4 + // #define D5_pin 5 + // #define D6_pin 6 + // #define D7_pin 7 + // LiquidCrystal_I2C lcd(I2C_ADDR,En_pin,Rw_pin,Rs_pin,D4_pin,D5_pin,D6_pin,D7_pin, BACKLIGHT_PIN, POSITIVE); + LiquidCrystal_I2C lcd(lcd_i2c_address_sainsmart_lcd,LCD_COLUMNS,LCD_ROWS); +#endif //FEATURE_SAINSMART_I2C_LCD + +#if defined(FEATURE_LCD_YDv1) + //LiquidCrystal_I2C lcd(0x38); + LiquidCrystal_I2C lcd(lcd_i2c_address_ydv1_lcd, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // for FEATURE_LCD_YDv1; set the LCD I2C address needed for LCM1602 IC V1 +#endif + +#if defined(FEATURE_LCD_FABO_PCF8574) + FaBoLCD_PCF8574 lcd; +#endif + +#if defined(FEATURE_LCD_MATHERTEL_PCF8574) + LiquidCrystal_PCF8574 lcd(lcd_i2c_address_mathertel_PCF8574); +#endif + +#if defined(FEATURE_LCD_I2C_FDEBRABANDER) + LiquidCrystal_I2C lcd(lcd_i2c_address_fdebrander_lcd, LCD_COLUMNS, LCD_ROWS, /*charsize*/ LCD_5x8DOTS); +#endif + +#if defined(FEATURE_LCD_HD44780) + hd44780_I2Cexp lcd; +#endif + +#if defined(FEATURE_USB_KEYBOARD) || defined(FEATURE_USB_MOUSE) + USB Usb; + uint32_t next_time; +#endif + +#if defined(FEATURE_USB_KEYBOARD) + class KbdRptParser : public KeyboardReportParser + { + protected: + virtual void OnKeyDown (uint8_t mod, uint8_t key); + virtual void OnKeyUp (uint8_t mod, uint8_t key); + }; + HIDBoot HidKeyboard(&Usb); + KbdRptParser KeyboardPrs; +#endif + +#if defined(FEATURE_USB_MOUSE) + class MouseRptParser : public MouseReportParser + { + protected: + virtual void OnMouseMove(MOUSEINFO *mi); + virtual void OnLeftButtonUp(MOUSEINFO *mi); + virtual void OnLeftButtonDown(MOUSEINFO *mi); + virtual void OnRightButtonUp(MOUSEINFO *mi); + virtual void OnRightButtonDown(MOUSEINFO *mi); + virtual void OnMiddleButtonUp(MOUSEINFO *mi); + virtual void OnMiddleButtonDown(MOUSEINFO *mi); + }; + HIDBoot HidMouse(&Usb); + MouseRptParser MousePrs; +#endif //FEATURE_USB_MOUSE + +#if defined(FEATURE_TRAINING_COMMAND_LINE_INTERFACE) + //BasicTerm term(&Serial); +#endif + +PRIMARY_SERIAL_CLS * primary_serial_port; + +#if defined(FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT) + SECONDARY_SERIAL_CLS * secondary_serial_port; +#endif + +PRIMARY_SERIAL_CLS * debug_serial_port; + +#ifdef FEATURE_PTT_INTERLOCK + byte ptt_interlock_active = 0; +#endif //FEATURE_PTT_INTERLOCK + +#ifdef FEATURE_QLF + byte qlf_active = qlf_on_by_default; +#endif //FEATURE_QLF + +#if defined(FEATURE_PADDLE_ECHO) + byte paddle_echo = 0; + long paddle_echo_buffer = 0; + unsigned long paddle_echo_buffer_decode_time = 0; +#endif //FEATURE_PADDLE_ECHO + +#ifndef FEATURE_PADDLE_ECHO + long paddle_echo_buffer = 0; +#endif // FEATURE_PADDLE_ECHO + +#if defined(FEATURE_CW_DECODER) && defined(OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR) + Goertzdetector cwtonedetector; +#endif + +#if defined(FEATURE_COMPETITION_COMPRESSION_DETECTION) + unsigned long compression_detection_key_down_time = 0; + unsigned long compression_detection_key_up_time = 0; + int time_array[COMPETITION_COMPRESSION_DETECTION_ARRAY_SIZE]; + byte time_array_index = 0; +#endif //FEATURE_COMPETITION_COMPRESSION_DETECTION + +#if defined(FEATURE_CW_COMPUTER_KEYBOARD) + byte cw_keyboard_capslock_on = 0; +#endif //defined(FEATURE_CW_COMPUTER_KEYBOARD) + + +#if defined(OPTION_WINKEY_SEND_BREAKIN_STATUS_BYTE) && defined(FEATURE_WINKEY_EMULATION) + byte send_winkey_breakin_byte_flag = 0; +#endif //defined(OPTION_WINKEY_SEND_BREAKIN_STATUS_BYTE) && defined(FEATURE_WINKEY_EMULATION) + +#if defined(FEATURE_ETHERNET) + uint8_t default_ip[] = FEATURE_ETHERNET_IP; // default IP address ("192.168.1.178") + uint8_t default_gateway[] = FEATURE_ETHERNET_GATEWAY; // default gateway + uint8_t default_subnet[] = FEATURE_ETHERNET_SUBNET_MASK; // default subnet mask + uint8_t dns_server[] = FEATURE_ETHERNET_DNS; + uint8_t mac[] = FEATURE_ETHERNET_MAC; // default physical mac address + uint8_t restart_networking = 0; + + #if defined(FEATURE_WEB_SERVER) + #define MAX_WEB_REQUEST 512 + String web_server_incoming_string; + uint8_t valid_request = 0; + EthernetServer server(FEATURE_ETHERNET_WEB_LISTENER_PORT); // default server port + #define MAX_PARSE_RESULTS 32 + struct parse_get_result_t{ + String parameter; + String value_string; + long value_long; + }; + struct parse_get_result_t parse_get_results[MAX_PARSE_RESULTS]; + int parse_get_results_index = 0; + unsigned long web_control_tx_key_time = 0; + #endif //FEATURE_WEB_SERVER + + #if defined(FEATURE_UDP) + unsigned int udp_listener_port = FEATURE_INTERNET_LINK_DEFAULT_RCV_UDP_PORT; + EthernetUDP Udp; + #if defined(FEATURE_INTERNET_LINK) + uint8_t udp_send_buffer[FEATURE_UDP_SEND_BUFFER_SIZE]; + uint8_t udp_send_buffer_bytes = 0; + uint8_t udp_receive_packet_buffer[FEATURE_UDP_RECEIVE_BUFFER_SIZE]; + uint8_t udp_receive_packet_buffer_bytes = 0; + #endif //FEATURE_INTERNET_LINK + #endif +#endif //FEATURE_ETHERNET + +unsigned long automatic_sending_interruption_time = 0; + +#ifdef FEATURE_4x4_KEYPAD + // Define the Keymap for 4x4 matrix keypad + char keys[KEYPAD_ROWS][KEYPAD_COLS] = { + {'1','2','3','A'}, + {'4','5','6','B'}, + {'7','8','9','C'}, + {'*','0','#','D'} + }; +#endif + +#ifdef FEATURE_3x4_KEYPAD + // Define the Keymap for 3x4 matrix keypad + char keys[KEYPAD_ROWS][KEYPAD_COLS] = { + {'1','2','3'}, + {'4','5','6'}, + {'7','8','9'}, + {'*','0','#'} + }; +#endif + +// Setup for 4x4 matrix keypad +#ifdef FEATURE_4x4_KEYPAD + byte rowPins[KEYPAD_ROWS] = {Row3,Row2,Row1,Row0}; //Arduino Mega Pins: 30,31,32,33---Keypad Pins 5,6,7,8 + byte colPins[KEYPAD_COLS] = {Col3,Col2,Col1,Col0}; //Arduino Mega Pins: 34,35,36,37---Keypad Pins 1,2,3,4 +#endif + +#ifdef FEATURE_3x4_KEYPAD + byte rowPins [KEYPAD_ROWS] = {Row3,Row2,Row1,Row0}; //Arduino Mega Pins: 30,31,32,33--Keypad Pins 4,6,7,2 + byte colPins [KEYPAD_COLS] = {Col2,Col1,Col0}; //Arduino Mega Pins: 34,35,36-----Keypad Pins 5,1,3 +#endif + +#if defined(FEATURE_4x4_KEYPAD) || defined(FEATURE_3x4_KEYPAD) + Keypad kpd = Keypad(makeKeymap(keys), rowPins, colPins, KEYPAD_ROWS, KEYPAD_COLS); +#endif + +unsigned long millis_rollover = 0; + +#if defined(FEATURE_TRAINING_COMMAND_LINE_INTERFACE) + byte check_serial_override = 0; + #if defined(OPTION_WORDSWORTH_CZECH) + #include "keyer_training_text_czech.h" + #elif defined(OPTION_WORDSWORTH_DEUTCSH) + #include "keyer_training_text_deutsch.h" + #elif defined(OPTION_WORDSWORTH_NORSK) + #include "keyer_training_text_norsk.h" + #else + #include "keyer_training_text_english.h" + #endif + + #include "keyer_callsign_prefixes.h" +#endif + + +#ifdef FEATURE_CLOCK + unsigned long clock_years = 0; + unsigned long clock_months = 0; + unsigned long clock_days = 0; + unsigned long clock_hours = 0; + unsigned long clock_minutes = 0; + unsigned long clock_seconds = 0; + long local_clock_years = 0; + long local_clock_months = 0; + long local_clock_days = 0; + long local_clock_hours = 0; + long local_clock_minutes = 0; + long local_clock_seconds = 0; + int clock_year_set = 2017; + byte clock_month_set = 1; + byte clock_day_set = 1; + byte clock_sec_set = 0; + unsigned long clock_hour_set = 0; + unsigned long clock_min_set = 0; + unsigned long millis_at_last_calibration = 0; +#endif // FEATURE_CLOCK + +#if defined(FEATURE_SD_CARD_SUPPORT) + uint8_t sd_card_state = SD_CARD_UNINITIALIZED; + File sdfile; + File sdlogfile; + uint8_t sd_card_log_state = SD_CARD_LOG_NOT_OPEN; +#endif //FEATURE_SD_CARD_SUPPORT + +#ifdef FEATURE_SEQUENCER + unsigned long sequencer_ptt_inactive_time = 0; + byte sequencer_1_active = 0; + byte sequencer_2_active = 0; + byte sequencer_3_active = 0; + byte sequencer_4_active = 0; + byte sequencer_5_active = 0; +#endif + +#ifdef FEATURE_SO2R_BASE + uint8_t so2r_rx = 1; + uint8_t so2r_tx = 1; + uint8_t so2r_pending_tx = 0; + uint8_t so2r_ptt = 0; + uint8_t so2r_latch = 0; + + #ifdef FEATURE_SO2R_SWITCHES + uint8_t so2r_open = 0; + uint8_t so2r_switch_rx = 0; + uint8_t so2r_debounce = 0; + unsigned long so2r_debounce_time; + #endif //FEATURE_SO2R_SWITCHES + + #ifdef FEATURE_SO2R_ANTENNA + uint8_t so2r_antenna_1 = 0; + uint8_t so2r_antenna_2 = 0; + #endif //FEATURE_SO2R_ANTENNA +#endif //FEATURE_SO2R_BASE + +byte async_eeprom_write = 0; + +/*--------------------------------------------------------------------------------------------------------- + + + “What we do for ourselves dies with us. What we do for others and the world remains and is immortal.†+ +― Albert Pike + + +---------------------------------------------------------------------------------------------------------*/ + +void setup() +{ + + initialize_pins(); + // initialize_serial_ports(); // Goody - this is available for testing startup issues + // initialize_debug_startup(); // Goody - this is available for testing startup issues + // debug_blink(); // Goody - this is available for testing startup issues + initialize_keyer_state(); + initialize_potentiometer(); + //initialize_rotary_encoder(); + initialize_default_modes(); + initialize_watchdog(); + //initialize_ethernet_variables(); + #if defined(DEBUG_EEPROM_READ_SETTINGS) + initialize_serial_ports(); + #endif + check_eeprom_for_initialization(); + check_for_beacon_mode(); + check_for_debug_modes(); + initialize_analog_button_array(); + #if !defined(DEBUG_EEPROM_READ_SETTINGS) + initialize_serial_ports(); + #endif + initialize_ps2_keyboard(); + initialize_usb(); + initialize_cw_keyboard(); + initialize_display(); + //initialize_ethernet(); + //initialize_udp(); + //initialize_web_server(); + //initialize_sd_card(); + initialize_debug_startup(); + +} + +// -------------------------------------------------------------------------------------------- + +void loop() +{ + + // this is where the magic happens + + + #ifdef OPTION_WATCHDOG_TIMER + wdt_reset(); + #endif //OPTION_WATCHDOG_TIMER + + #if defined(FEATURE_BEACON) && defined(FEATURE_MEMORIES) + if (keyer_machine_mode == BEACON) { + delay(201); // an odd duration delay before we enter BEACON mode + #ifdef OPTION_BEACON_MODE_MEMORY_REPEAT_TIME + unsigned int time_to_delay = configuration.memory_repeat_time - configuration.ptt_tail_time[configuration.current_tx - 1]; + #endif // OPTION_BEACON_MODE_MEMORY_REPEAT_TIME + + while (keyer_machine_mode == BEACON) { // if we're in beacon mode, just keep playing memory 1 + if (!send_buffer_bytes) { + add_to_send_buffer(SERIAL_SEND_BUFFER_MEMORY_NUMBER); + add_to_send_buffer(0); + } + service_send_buffer(PRINTCHAR); + #ifdef OPTION_BEACON_MODE_PTT_TAIL_TIME + delay(configuration.ptt_tail_time[configuration.current_tx - 1]); // after memory 1 has played, this holds the PTT line active for the ptt tail time of the current tx + check_ptt_tail(); // this resets things so that the ptt line will go high during the next playout + digitalWrite (configuration.current_ptt_line, ptt_line_inactive_state); // forces the ptt line of the current tx to be inactive + #endif // OPTION_BEACON_MODE_PTT_TAIL_TIME + + #ifdef FEATURE_SERIAL + check_serial(); + #endif + + #ifdef OPTION_WATCHDOG_TIMER + wdt_reset(); + #endif // OPTION_WATCHDOG_TIMER + + #ifdef OPTION_BEACON_MODE_MEMORY_REPEAT_TIME + if (time_to_delay > 0) delay(time_to_delay); // this provdes a delay between succesive playouts of the memory contents + #endif // OPTION_BEACON_MODE_MEMORY_REPEAT_TIME + } // end while (keyer_machine_mode == BEACON) + } // end if (keyer_machine_mode == BEACON) + #endif // defined(FEATURE_BEACON) && defined(FEATURE_MEMORIES) + + #if defined(FEATURE_BEACON_SETTING) + service_beacon(); + #endif + + if (keyer_machine_mode == KEYER_NORMAL) { + #ifdef FEATURE_BUTTONS + check_buttons(); + #endif + check_paddles(); + service_dit_dah_buffers(); + + #if defined(FEATURE_SERIAL) + check_serial(); + check_paddles(); + service_dit_dah_buffers(); + #endif + + service_send_buffer(PRINTCHAR); + check_ptt_tail(); + + #ifdef FEATURE_POTENTIOMETER + check_potentiometer(); + #endif + + #ifdef FEATURE_ROTARY_ENCODER + check_rotary_encoder(); + #endif + + #ifdef FEATURE_PS2_KEYBOARD + check_ps2_keyboard(); + #endif + + #if defined(FEATURE_USB_KEYBOARD) || defined(FEATURE_USB_MOUSE) + service_usb(); + #endif + + check_for_dirty_configuration(); + + #ifdef FEATURE_DEAD_OP_WATCHDOG + check_for_dead_op(); + #endif + + #ifdef FEATURE_MEMORIES + check_memory_repeat(); + #endif + + #ifdef FEATURE_DISPLAY + check_paddles(); + service_send_buffer(PRINTCHAR); + service_display(); + #endif + + #ifdef FEATURE_CW_DECODER + service_cw_decoder(); + #endif + + #ifdef FEATURE_LED_RING + update_led_ring(); + #endif + + #ifdef FEATURE_SLEEP + check_sleep(); + #endif + + #ifdef FEATURE_LCD_BACKLIGHT_AUTO_DIM + check_backlight(); + #endif + + #ifdef FEATURE_PTT_INTERLOCK + service_ptt_interlock(); + #endif + + #ifdef FEATURE_PADDLE_ECHO + service_paddle_echo(); + #endif + + #ifdef FEATURE_STRAIGHT_KEY + service_straight_key(); + #endif + + #if defined(FEATURE_COMPETITION_COMPRESSION_DETECTION) + service_competition_compression_detection(); + #endif + + #if defined(OPTION_WINKEY_SEND_BREAKIN_STATUS_BYTE) && defined(FEATURE_WINKEY_EMULATION) + service_winkey_breakin(); + #endif + + #if defined(FEATURE_ETHERNET) + check_for_network_restart(); + #if defined(FEATURE_WEB_SERVER) + service_web_server(); + #endif + #if defined(FEATURE_INTERNET_LINK) + service_udp_send_buffer(); + service_udp_receive(); + service_internet_link_udp_receive_buffer(); + #endif + #endif + + #ifdef FEATURE_SIDETONE_SWITCH + check_sidetone_switch(); + #endif + + #if defined(FEATURE_4x4_KEYPAD) || defined(FEATURE_3x4_KEYPAD) + service_keypad(); + #endif + + #ifdef FEATURE_SD_CARD_SUPPORT + service_sd_card(); + #endif + + #ifdef FEATURE_SEQUENCER + check_sequencer_tail_time(); + #endif + + #ifdef FEATURE_SO2R_SWITCHES + so2r_switches(); + #endif + + service_async_eeprom_write(); + + } + + service_millis_rollover(); + + +} + +// Subroutines -------------------------------------------------------------------------------------------- + + +// Are you a radio artisan ? + + +byte service_tx_inhibit_and_pause(){ + + byte return_code = 0; + static byte pause_sending_buffer_active = 0; + + if (tx_inhibit_pin){ + if ((digitalRead(tx_inhibit_pin) == tx_inhibit_pin_active_state)){ + dit_buffer = 0; + dah_buffer = 0; + return_code = 1; + if (send_buffer_bytes){ + clear_send_buffer(); + send_buffer_status = SERIAL_SEND_BUFFER_NORMAL; + #ifdef FEATURE_MEMORIES + play_memory_prempt = 1; + repeat_memory = 255; + #endif + #ifdef FEATURE_WINKEY_EMULATION + if (winkey_sending && winkey_host_open) { + winkey_port_write(0xc2|winkey_sending|winkey_xoff,0); // 0xc2 - BREAKIN bit set high + winkey_interrupted = 1; + } + #endif + } + } + } + + if (tx_pause_pin){ + if ((digitalRead(tx_pause_pin) == tx_pause_pin_active_state)){ + dit_buffer = 0; + dah_buffer = 0; + return_code = 1; + if (!pause_sending_buffer_active){ + pause_sending_buffer = 1; + pause_sending_buffer_active = 1; + delay(10); + } + } else { + if (pause_sending_buffer_active){ + pause_sending_buffer = 0; + pause_sending_buffer_active = 0; + delay(10); + } + } + + + } + + return return_code; + +} + +//------------------------------------------------------------------------------------------------------- + +#if defined(FEATURE_BEACON_SETTING) + void service_beacon(){ + if ((configuration.beacon_mode_on_boot_up) && (!send_buffer_bytes)){ + serial_play_memory(0); + } + } +#endif //FEATURE_BEACON_SETTING + +//------------------------------------------------------------------------------------------------------- + +#if defined(FEATURE_COMPETITION_COMPRESSION_DETECTION) + void service_competition_compression_detection(){ + + + static byte compression_detection_indicator_on = 0; + static unsigned long last_compression_check_time = 0; + + + + + if ((millis() - last_compression_check_time) > 1000){ + float time_average = 0; + if (time_array_index == COMPETITION_COMPRESSION_DETECTION_ARRAY_SIZE){ + for (int i = 0;i < COMPETITION_COMPRESSION_DETECTION_ARRAY_SIZE;i++){ + time_average = time_average + time_array[i]; + } + time_average = time_average / COMPETITION_COMPRESSION_DETECTION_ARRAY_SIZE; + if (time_average < ((1200/configuration.wpm)*COMPETITION_COMPRESSION_DETECTION_AVERAGE_ALARM_THRESHOLD)){ + if (!compression_detection_indicator_on){ + compression_detection_indicator_on = 1; + digitalWrite(compression_detection_pin,HIGH); + #if defined(DEBUG_FEATURE_COMPETITION_COMPRESSION_DETECTION) + debug_serial_port->print("service_competition_compression_detection: time_array: "); + for (int i = 0;i < COMPETITION_COMPRESSION_DETECTION_ARRAY_SIZE;i++){ + debug_serial_port->print(time_array[i]); + debug_serial_port->print(" "); + } + debug_serial_port->print("\n\rservice_competition_compression_detection: COMPRESSION DETECTION ON average: "); + debug_serial_port->println(time_average); + #endif + } + + } else { + if (compression_detection_indicator_on){ + compression_detection_indicator_on = 0; + digitalWrite(compression_detection_pin,LOW); + #if defined(DEBUG_FEATURE_COMPETITION_COMPRESSION_DETECTION) + debug_serial_port->print("service_competition_compression_detection: time_array: "); + for (int i = 0;i < COMPETITION_COMPRESSION_DETECTION_ARRAY_SIZE;i++){ + debug_serial_port->print(time_array[i]); + debug_serial_port->print(" "); + } + debug_serial_port->print("\n\rservice_competition_compression_detection: COMPRESSION DETECTION OFF average: "); + debug_serial_port->println(time_average); + #endif + } + } + } + last_compression_check_time = millis(); + } + + } +#endif //FEATURE_COMPETITION_COMPRESSION_DETECTION + +//------------------------------------------------------------------------------------------------------- + +#if defined(FEATURE_4x4_KEYPAD) || defined(FEATURE_3x4_KEYPAD) +void service_keypad(){ + + // Code contributed by Jack, W0XR + + char key = kpd.getKey(); + + if(key){ // Check for a valid key. + + #if defined(DEBUG_KEYPAD_SERIAL) + debug_serial_port->print("service_keypad: key:"); + debug_serial_port->println(key); + #endif + + switch(key){ + case '1': + add_to_send_buffer(SERIAL_SEND_BUFFER_MEMORY_NUMBER); + add_to_send_buffer(mem1); + //play_memory(mem1); //MEMORY 1 + break; + case '2': + add_to_send_buffer(SERIAL_SEND_BUFFER_MEMORY_NUMBER); + add_to_send_buffer(mem2); + //play_memory(mem2); //MEMORY 2 + break; + case '3': + add_to_send_buffer(SERIAL_SEND_BUFFER_MEMORY_NUMBER); + add_to_send_buffer(mem3); + //play_memory(mem3); //MEMORY 3 + break; + case '4': + add_to_send_buffer(SERIAL_SEND_BUFFER_MEMORY_NUMBER); + add_to_send_buffer(mem4); + //play_memory(mem4); //MEMORY 4 + break; + case '5': + add_to_send_buffer(SERIAL_SEND_BUFFER_MEMORY_NUMBER); + add_to_send_buffer(mem5); + //play_memory(mem5); //MEMORY 5 + break; + case '6': + add_to_send_buffer(SERIAL_SEND_BUFFER_MEMORY_NUMBER); + add_to_send_buffer(mem6); + //play_memory(mem6); //MEMORY 6 + break; + case '7': + add_to_send_buffer(SERIAL_SEND_BUFFER_MEMORY_NUMBER); + add_to_send_buffer(mem7); + //play_memory(mem7); //MEMORY 7 + break; + case '8': + add_to_send_buffer(SERIAL_SEND_BUFFER_MEMORY_NUMBER); + add_to_send_buffer(mem8); + //play_memory(mem8); //MEMORY 8 + break; + case '9': + add_to_send_buffer(SERIAL_SEND_BUFFER_MEMORY_NUMBER); + add_to_send_buffer(mem9); + //play_memory(mem9); //MEMORY 9 + break; + case '0': + add_to_send_buffer(SERIAL_SEND_BUFFER_MEMORY_NUMBER); + add_to_send_buffer(mem10); + //play_memory(mem10); //MEMORY 10 + break; + case '#': + beep_boop(); + break; + case '*': + beep_boop(); + break; + case 'A': + beep_boop(); + break; + case 'B': + beep_boop(); + break; + #ifdef FEATURE_COMMAND_MODE + case 'C': + command_mode(); + break; + #endif + case 'D': + beep_boop(); + break; + } + + } //if(key) + +} // service_keypad() +#endif //defined(FEATURE_4x4_KEYPAD) || defined(FEATURE_3x4_KEYPAD) + +//------------------------------------------------------------------------------------------------------- + +#ifdef FEATURE_STRAIGHT_KEY + long service_straight_key(){ + + static byte last_straight_key_state = 0; + + if (digitalRead(pin_straight_key) == STRAIGHT_KEY_ACTIVE_STATE){ + if (!last_straight_key_state){ + sending_mode = MANUAL_SENDING; + tx_and_sidetone_key(1); + last_straight_key_state = 1; + + + #ifdef FEATURE_MEMORIES + clear_send_buffer(); + repeat_memory = 255; + #endif + + } + } else { + if (last_straight_key_state){ + sending_mode = MANUAL_SENDING; + tx_and_sidetone_key(0); + last_straight_key_state = 0; + } + } + + + #if defined(FEATURE_STRAIGHT_KEY_DECODE) + + static unsigned long last_transition_time = 0; + static unsigned long last_decode_time = 0; + static byte last_state = 0; + static int decode_elements[16]; // this stores received element lengths in mS (positive = tone, minus = no tone) + static byte decode_element_pointer = 0; + static float decode_element_tone_average = 0; + static float decode_element_no_tone_average = 0; + static int no_tone_count = 0; + static int tone_count = 0; + byte decode_it_flag = 0; + + int element_duration = 0; + static float decoder_wpm = configuration.wpm; + long decode_character = 0; + static byte space_sent = 0; + #if defined(FEATURE_COMMAND_LINE_INTERFACE) && defined(FEATURE_STRAIGHT_KEY_ECHO) + static byte screen_column = 0; + static int last_printed_decoder_wpm = 0; + #endif + + #if defined(FEATURE_CW_COMPUTER_KEYBOARD) + static byte cw_keyboard_no_space = 0; + char cw_keyboard_character_to_send; + static byte cw_keyboard_backspace_flag = 0; + #endif //defined(FEATURE_CW_COMPUTER_KEYBOARD) + + + if (last_transition_time == 0) { + if (last_straight_key_state == 1) { // is this our first tone? + last_transition_time = millis(); + last_state = 1; + + #ifdef FEATURE_SLEEP + last_activity_time = millis(); + #endif //FEATURE_SLEEP + #ifdef FEATURE_LCD_BACKLIGHT_AUTO_DIM + last_active_time = millis(); + #endif //FEATURE_LCD_BACKLIGHT_AUTO_DIM + + } else { + + if ((last_decode_time > 0) && (!space_sent) && ((millis() - last_decode_time) > ((1200/decoder_wpm)*CW_DECODER_SPACE_PRINT_THRESH))) { // should we send a space? + #if defined(FEATURE_SERIAL) && defined(FEATURE_STRAIGHT_KEY_ECHO) + #ifdef FEATURE_COMMAND_LINE_INTERFACE + primary_serial_port->write(32); + screen_column++; + #endif //FEATURE_COMMAND_LINE_INTERFACE + #endif //FEATURE_SERIAL + #ifdef FEATURE_DISPLAY + display_scroll_print_char(' '); + #endif //FEATURE_DISPLAY + space_sent = 1; + + #if defined(FEATURE_CW_COMPUTER_KEYBOARD) + if (!cw_keyboard_no_space){ + Keyboard.write(' '); + #ifdef DEBUG_CW_COMPUTER_KEYBOARD + debug_serial_port->println("service_straight_key: Keyboard.write: "); + #endif //DEBUG_CW_COMPUTER_KEYBOARD + } + cw_keyboard_no_space = 0; + #endif //defined(FEATURE_CW_COMPUTER_KEYBOARD) + + }// should we send a space? + } + } else { + if (last_straight_key_state != last_state) { + // we have a transition + element_duration = millis() - last_transition_time; + if (element_duration > CW_DECODER_NOISE_FILTER) { // filter out noise + if (last_straight_key_state == 1) { // we have a tone + decode_elements[decode_element_pointer] = (-1 * element_duration); // the last element was a space, so make it negative + no_tone_count++; + if (decode_element_no_tone_average == 0) { + decode_element_no_tone_average = element_duration; + } else { + decode_element_no_tone_average = (element_duration + decode_element_no_tone_average) / 2; + } + decode_element_pointer++; + last_state = 1; + } else { // we have no tone + decode_elements[decode_element_pointer] = element_duration; // the last element was a tone, so make it positive + tone_count++; + if (decode_element_tone_average == 0) { + decode_element_tone_average = element_duration; + } else { + decode_element_tone_average = (element_duration + decode_element_tone_average) / 2; + } + last_state = 0; + decode_element_pointer++; + } + last_transition_time = millis(); + if (decode_element_pointer == 16) { decode_it_flag = 1; } // if we've filled up the array, go ahead and decode it + } + + + } else { + // no transition + element_duration = millis() - last_transition_time; + if (last_state == 0) { + // we're still high (no tone) - have we reached character space yet? + //if ((element_duration > (decode_element_no_tone_average * 2.5)) || (element_duration > (decode_element_tone_average * 2.5))) { + if (element_duration > (float(1200/decoder_wpm)*CW_DECODER_SPACE_DECODE_THRESH)) { + decode_it_flag = 1; + } + } else { + // have we had tone for an outrageous amount of time? + } + } + } + + + + if (decode_it_flag) { // are we ready to decode the element array? + + // adjust the decoder wpm based on what we got + + if ((no_tone_count > 0) && (tone_count > 1)){ // NEW + + if (decode_element_no_tone_average > 0) { + if (abs((1200/decode_element_no_tone_average) - decoder_wpm) < 5) { + decoder_wpm = (decoder_wpm + (1200/decode_element_no_tone_average))/2; + } else { + if (abs((1200/decode_element_no_tone_average) - decoder_wpm) < 10) { + decoder_wpm = (decoder_wpm + decoder_wpm + (1200/decode_element_no_tone_average))/3; + } else { + if (abs((1200/decode_element_no_tone_average) - decoder_wpm) < 20) { + decoder_wpm = (decoder_wpm + decoder_wpm + decoder_wpm + (1200/decode_element_no_tone_average))/4; + } + } + } + } + + + } // NEW + + #ifdef DEBUG_FEATURE_STRAIGHT_KEY_ECHO + if (abs(decoder_wpm - last_printed_decoder_wpm) > 0.9) { + debug_serial_port->print("<"); + debug_serial_port->print(int(decoder_wpm)); + debug_serial_port->print(">"); + last_printed_decoder_wpm = decoder_wpm; + } + #endif //DEBUG_FEATURE_STRAIGHT_KEY_ECHO + + for (byte x = 0;x < decode_element_pointer; x++) { + if (decode_elements[x] > 0) { // is this a tone element? + // we have no spaces to time from, use the current wpm + if ((decode_elements[x]/(1200/decoder_wpm)) < 2.1 ) { // changed from 1.3 to 2.1 2015-05-12 + decode_character = (decode_character * 10) + 1; // we have a dit + } else { + decode_character = (decode_character * 10) + 2; // we have a dah + } + } + #ifdef DEBUG_FEATURE_STRAIGHT_KEY_ECHO + debug_serial_port->print(F("service_straight_key: decode_elements[")); + debug_serial_port->print(x); + debug_serial_port->print(F("]: ")); + debug_serial_port->println(decode_elements[x]); + #endif //DEBUG_FEATURE_STRAIGHT_KEY_ECHO + } + + #ifdef DEBUG_FEATURE_STRAIGHT_KEY_ECHO + debug_serial_port->print(F("service_straight_key: decode_element_tone_average: ")); + debug_serial_port->println(decode_element_tone_average); + debug_serial_port->print(F("service_straight_key: decode_element_no_tone_average: ")); + debug_serial_port->println(decode_element_no_tone_average); + debug_serial_port->print(F("service_straight_key: decode_element_no_tone_average wpm: ")); + debug_serial_port->println(1200/decode_element_no_tone_average); + debug_serial_port->print(F("service_straight_key: decoder_wpm: ")); + debug_serial_port->println(decoder_wpm); + debug_serial_port->print(F("service_straight_key: decode_character: ")); + debug_serial_port->println(decode_character); + #endif //DEBUG_FEATURE_STRAIGHT_KEY_ECHO + + + #if defined(OPTION_PROSIGN_SUPPORT) + byte cw_ascii_temp = convert_cw_number_to_ascii(decode_character); + static char * prosign_char = (char*)""; + + if ((cw_ascii_temp > PROSIGN_START) && (cw_ascii_temp < PROSIGN_END)){ // if we have a prosign code, convert it to chars + prosign_char = convert_prosign(cw_ascii_temp); + cw_ascii_temp = 0; + } + + #if defined(FEATURE_SERIAL) && defined(FEATURE_COMMAND_LINE_INTERFACE) && defined(FEATURE_STRAIGHT_KEY_ECHO) + if (cli_straight_key_echo){ + if (cw_ascii_temp){ + primary_serial_port->write(cw_ascii_temp); + } else { + primary_serial_port->write(prosign_char[0]); + primary_serial_port->write(prosign_char[1]); + } + #ifdef FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + if (cw_ascii_temp){ + secondary_serial_port->write(cw_ascii_temp); + } else { + secondary_serial_port->write(prosign_char[0]); + secondary_serial_port->write(prosign_char[1]); + } + #endif + screen_column++; + } + #endif //defined(FEATURE_SERIAL) && defined(FEATURE_COMMAND_LINE_INTERFACE) + + #if defined(FEATURE_DISPLAY) && defined(FEATURE_STRAIGHT_KEY_ECHO) + if (cli_straight_key_echo){ + if (cw_ascii_temp){ + display_scroll_print_char(cw_ascii_temp); + } else { + display_scroll_print_char(prosign_char[0]); + display_scroll_print_char(prosign_char[1]); + } + } + #endif //FEATURE_DISPLAY + + + #else //OPTION_PROSIGN_SUPPORT + + #if defined(FEATURE_SERIAL) && defined(FEATURE_COMMAND_LINE_INTERFACE) && defined(FEATURE_STRAIGHT_KEY_ECHO) + if (cli_straight_key_echo){ + primary_serial_port->write(convert_cw_number_to_ascii(decode_character)); + #ifdef FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + secondary_serial_port->write(convert_cw_number_to_ascii(decode_character)); + #endif + screen_column++; + } + #endif //defined(FEATURE_SERIAL) && defined(FEATURE_COMMAND_LINE_INTERFACE) + + #if defined(FEATURE_DISPLAY) && defined(FEATURE_STRAIGHT_KEY_ECHO) + if (cli_straight_key_echo){display_scroll_print_char(convert_cw_number_to_ascii(decode_character));} + #endif //FEATURE_DISPLAY + + #endif //OPTION_PROSIGN_SUPPORT + + + + + #if defined(FEATURE_CW_COMPUTER_KEYBOARD) + switch (decode_character){ + case 111111: + case 1111111: + case 11111111: + case 111111111: + Keyboard.write(KEY_BACKSPACE); // backspace + cw_keyboard_no_space = 1; + break; + case 222222: + case 2222222: + case 22222222: + case 222222222: + Keyboard.write(32); // space + no_space = 1; + break; + case 1212: // prosign AA + Keyboard.write(KEY_RETURN); + cw_keyboard_no_space = 1; + break; + case 211222: // prosign DO + Keyboard.write(KEY_CAPS_LOCK); + #ifdef OPTION_CW_KEYBOARD_CAPSLOCK_BEEP + if (cw_keyboard_capslock_on){ + beep();delay(100); + boop(); + cw_keyboard_capslock_on = 0; + } else { + boop(); + beep(); + cw_keyboard_capslock_on = 1; + } + #endif //OPTION_CW_KEYBOARD_CAPSLOCK_BEEP + cw_keyboard_no_space = 1; + break; + + #ifdef OPTION_CW_KEYBOARD_ITALIAN // courtesy of Giorgio IZ2XBZ + case 122121: // "@" + Keyboard.press(KEY_LEFT_ALT); + Keyboard.write(59); + Keyboard.releaseAll(); + break; + case 112211:// "?" + Keyboard.write(95); + break; + case 11221: // "!" + Keyboard.write(33); + break; + case 21121: // "/" + Keyboard.write(38); + break; + case 21112: // "=" or "BT" + Keyboard.write(41); + break; + case 12212: //à + Keyboard.write(39); + break; + case 11211: //è + Keyboard.write(91); + break; + case 12221: //ì + Keyboard.write(61); + break; + case 2221: //ò + Keyboard.write(59); + break; + case 1122: //ù + Keyboard.write(92); + break; + case 21221: // ( + Keyboard.write(42); + break; + case 212212: // ) + Keyboard.write(40); + break; + case 12111: // & + Keyboard.write(94); + break; + case 222111: //: + Keyboard.write(62); + break; + case 212121: //; + Keyboard.write(60); + break; + case 12121: //+ + Keyboard.write(93); + break; + case 211112: // - + Keyboard.write(47); + break; + #endif //OPTION_CW_KEYBOARD_ITALIAN + + default: + cw_keyboard_character_to_send = convert_cw_number_to_ascii(decode_character); + if ((cw_keyboard_character_to_send > 64) && (cw_keyboard_character_to_send < 91)) {cw_keyboard_character_to_send = cw_keyboard_character_to_send + 32;} + if (cw_keyboard_character_to_send=='*'){ + cw_keyboard_no_space = 1; + #ifdef OPTION_UNKNOWN_CHARACTER_ERROR_TONE + boop(); + #endif //OPTION_UNKNOWN_CHARACTER_ERROR_TONE + } else { + if (!((cw_keyboard_backspace_flag) && ((decode_character == 1) || (decode_character == 11) || (decode_character == 111) || (decode_character == 1111) || (decode_character == 11111)))){ + Keyboard.write(char(cw_keyboard_character_to_send)); + } + cw_keyboard_backspace_flag = 0; + } + break; + + } //switch (decode_character) + + #ifdef DEBUG_CW_COMPUTER_KEYBOARD + debug_serial_port->print("service_straight_key: Keyboard.write: "); + debug_serial_port->write(character_to_send); + debug_serial_port->println(); + #endif //DEBUG_CW_COMPUTER_KEYBOARD + + + #endif //defined(FEATURE_CW_COMPUTER_KEYBOARD) + + + // reinitialize everything + last_transition_time = 0; + last_decode_time = millis(); + decode_element_pointer = 0; + decode_element_tone_average = 0; + decode_element_no_tone_average = 0; + space_sent = 0; + no_tone_count = 0; + tone_count = 0; + + } //if (decode_it_flag) + + #if defined(FEATURE_SERIAL) && defined(FEATURE_STRAIGHT_KEY_ECHO) + #ifdef FEATURE_COMMAND_LINE_INTERFACE + if ((screen_column > CW_DECODER_SCREEN_COLUMNS) && (cli_straight_key_echo)) { + #ifdef FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + secondary_serial_port->println(); + #else + primary_serial_port->println(); + #endif + screen_column = 0; + } + #endif //FEATURE_COMMAND_LINE_INTERFACE + #endif //FEATURE_SERIAL + + return(decode_character); + + #endif //FEATURE_STRAIGHT_KEY_DECODE + + + + + + + + } +#endif //FEATURE_STRAIGHT_KEY + +//------------------------------------------------------------------------------------------------------- + +void initialize_cw_keyboard(){ + + #ifdef FEATURE_CW_COMPUTER_KEYBOARD + Keyboard.begin(); + #endif //FEATURE_CW_COMPUTER_KEYBOARD + +} + +//------------------------------------------------------------------------------------------------------- + +#ifdef ARDUINO_SAM_DUE + +/* + +This code from http://forum.arduino.cc/index.php?topic=136500.0 + +*/ + + +// timers TC0 TC1 TC2 channels 0-2 ids 0-2 3-5 6-8 AB 0 1 +// use TC1 channel 0 + +#define TONE_TIMER TC1 +#define TONE_CHNL 0 +#define TONE_IRQ TC3_IRQn + +// TIMER_CLOCK4 84MHz/128 with 16 bit counter give 10 Hz to 656KHz + +static uint8_t pinEnabled[PINS_COUNT]; +static uint8_t TCChanEnabled = 0; +static boolean pin_state = false ; +static Tc *chTC = TONE_TIMER; +static uint32_t chNo = TONE_CHNL; + +volatile static int32_t toggle_count; +static uint32_t tone_pin; + +void toneDUE(uint32_t ulPin, uint32_t frequency, int32_t duration = 0){ + + // frequency (in hertz) and duration (in milliseconds) + + const uint32_t rc = VARIANT_MCK / 256 / frequency; + tone_pin = ulPin; + toggle_count = 0; // strange wipe out previous duration + if (duration > 0 ){ + toggle_count = 2 * frequency * duration / 1000; + } else { + toggle_count = -1; + } + if (!TCChanEnabled) { + pmc_set_writeprotect(false); + pmc_enable_periph_clk((uint32_t)TONE_IRQ); + TC_Configure(chTC, chNo, TC_CMR_TCCLKS_TIMER_CLOCK4 | + TC_CMR_WAVE | // Waveform mode + TC_CMR_WAVSEL_UP_RC ); // Counter running up and reset when equals to RC + chTC->TC_CHANNEL[chNo].TC_IER=TC_IER_CPCS; // RC compare interrupt + chTC->TC_CHANNEL[chNo].TC_IDR=~TC_IER_CPCS; + NVIC_EnableIRQ(TONE_IRQ); + TCChanEnabled = 1; + } + if (!pinEnabled[ulPin]) { + pinMode(ulPin, OUTPUT); + pinEnabled[ulPin] = 1; + } + TC_Stop(chTC, chNo); + TC_SetRC(chTC, chNo, rc); // set frequency + TC_Start(chTC, chNo); +} + +void noToneDUE(uint32_t ulPin){ + + TC_Stop(chTC, chNo); // stop timer + digitalWrite(ulPin,LOW); // no signal on pin + +} + +// timer ISR TC1 ch 0 +void TC3_Handler ( void ) { + + TC_GetStatus(TC1, 0); + if (toggle_count != 0){ + // toggle pin TODO better + digitalWrite(tone_pin,pin_state= !pin_state); + if (toggle_count > 0) toggle_count--; + } else { + noTone(tone_pin); + } + +} +#endif +/* +//sp5iou 2018/0329 This is already in stm32 SDK standard library +#elif defined(ARDUINO_MAPLE_MINI) || defined(ARDUINO_GENERIC_STM32F103C) //HARDWARE_ARDUINO_DUE +//This code from http://www.stm32duino.com/viewtopic.php?t=496 + +/////////////////////////////////////////////////////////////////////// +// +// tone(pin,frequency[,duration]) generate a tone on a given pin +// +// noTone(pin) switch of the tone on the pin +// +/////////////////////////////////////////////////////////////////////// + +//#include "Arduino.h" +//#include + +#ifndef TONE_TIMER + #define TONE_TIMER 2 +#endif + +HardwareTimer tone_timer(TONE_TIMER); + +bool tone_state = true; // last pin state for toggling +short tone_pin = -1; // pin for outputting sound +short tone_freq = 444; // tone frequency (0=pause) +unsigned tone_micros = 500000/444; // tone have wave time in usec +int tone_counts = 0; // tone duration in units of half waves + +// timer hander for tone with no duration specified, +// will keep going until noTone() is called +void tone_handler_1(void) { + tone_state = !tone_state; + digitalWrite(tone_pin,tone_state); +} + +// timer hander for tone with a specified duration, +// will stop automatically when duration time is up. +void tone_handler_2(void) { // check duration + if(tone_freq>0){ + tone_state = !tone_state; + digitalWrite(tone_pin,tone_state); + } + if(!--tone_counts){ + tone_timer.pause(); + pinMode(tone_pin, INPUT); + } +} + +// play a tone on given pin with given frequency and optional duration in msec +void tone(uint8_t pin, unsigned short freq, unsigned duration = 0) { + tone_pin = pin; + tone_freq = freq; + tone_micros = 500000/(freq>0?freq:1000); + tone_counts = 0; + + tone_timer.pause(); + + if(freq >= 0){ + if(duration > 0)tone_counts = ((long)duration)*1000/tone_micros; + pinMode(tone_pin, OUTPUT); + + // set timer to half period in microseconds + tone_timer.setPeriod(tone_micros); + + // Set up an interrupt on channel 1 + tone_timer.setChannel1Mode(TIMER_OUTPUT_COMPARE); + tone_timer.setCompare(TIMER_CH1, 1); // Interrupt 1 count after each update + tone_timer.attachCompare1Interrupt(tone_counts?tone_handler_2:tone_handler_1); + + // Refresh the tone timer + tone_timer.refresh(); + + // Start the timer counting + tone_timer.resume(); + } else { + pinMode(tone_pin, INPUT); + } +} + +// disable tone on specified pin, if any +void noTone(uint8_t pin){ + tone(pin,-1); +} + + +#endif //ARDUINO_MAPLE_MINI / ARDUINO_SAM_DUE +*/ + +//------------------------------------------------------------------------------------------------------- + +/* Sleep code prior to 2016-01-18 +#ifdef FEATURE_SLEEP +void wakeup() { + detachInterrupt(0); +} +#endif //FEATURE_SLEEP +*/ + +#ifdef FEATURE_SLEEP // Code contributed by Graeme, ZL2APV 2016-01-18 +void wakeup() { + sleep_disable(); + detachInterrupt (0); +} // end of wakeup + +ISR (PCINT1_vect) + { + PCICR = 0; // cancel pin change interrupts + sleep_disable(); + } // end of ISR (PCINT1_vect) + +ISR (PCINT2_vect) + { + PCICR = 0; // turn off all pin change interrupt ports + sleep_disable(); + } // end of ISR (PCINT2_vect) +#endif //FEATURE_SLEEP + +//------------------------------------------------------------------------------------------------------- +/* Sleep code prior to 2016-01-18 +#ifdef FEATURE_SLEEP +void check_sleep(){ + + if ((millis() - last_activity_time) > (go_to_sleep_inactivity_time*60000)){ + + if (config_dirty) { // force a configuration write to EEPROM if the config is dirty + last_config_write = 0; + check_for_dirty_configuration(); + } + + attachInterrupt(0, wakeup, LOW); + set_sleep_mode(SLEEP_MODE_PWR_DOWN); + sleep_enable(); + #ifdef DEBUG_SLEEP + debug_serial_port->println(F("check_sleep: entering sleep")); + delay(1000); + #endif //DEBUG_SLEEP + + sleep_mode(); + + // shhhhh! we are asleep here !! + + sleep_disable(); + last_activity_time = millis(); + + #ifdef DEBUG_SLEEP + debug_serial_port->println(F("check_sleep: I'm awake!")); + #endif //DEBUG_SLEEP + } + + +} +#endif //FEATURE_SLEEP +*/ + + +#ifdef FEATURE_SLEEP // Code contributed by Graeme, ZL2APV 2016-01-18 +void check_sleep(){ + + if ((millis() - last_activity_time) > ((unsigned long)go_to_sleep_inactivity_time*60000)){ + + if (config_dirty) { // force a configuration write to EEPROM if the config is dirty + last_config_write = 0; + check_for_dirty_configuration(); + } + + byte old_ADCSRA = ADCSRA; + // disable ADC to save power + ADCSRA = 0; + + set_sleep_mode (SLEEP_MODE_PWR_DOWN); + sleep_enable(); + + // Do not interrupt before we go to sleep, or the ISR will detach interrupts and we won't wake. + noInterrupts (); + + // will be called when pin D2, D5 or A1 goes low + attachInterrupt(0, wakeup, FALLING); + EIFR = bit(INTF0); // clear flag for interrupt 0 + PCIFR = 0; // Clear all pin change flags + PCICR = 0b00000110; //Turn on ports C and D only + PCMSK2 = bit(PCINT21); //Turn on pin D5 + PCMSK1 = bit(PCINT9); //Turn on pin A1 + + // turn off brown-out enable in software + // BODS must be set to one and BODSE must be set to zero within four clock cycles + #if !defined(__AVR_ATmega2560__) + MCUCR = bit (BODS) | bit (BODSE); + // The BODS bit is automatically cleared after three clock cycles + MCUCR = bit (BODS); + #endif + + #ifdef DEBUG_SLEEP + debug_serial_port->println(F("check_sleep: entering sleep")); + delay(1000); + #endif //DEBUG_SLEEP + + if (keyer_awake){ + digitalWrite(keyer_awake,KEYER_AWAKE_PIN_ASLEEP_STATE); + } + + interrupts(); + sleep_cpu(); + + // shhhhh! we are asleep here !! + + // An interrupt on digital 2 will call the wake() interrupt service routine + // and then return us to here while a change on D5 or A1 will vector to their + // interrupt handler and also return to here. + + detachInterrupt (0); + PCICR = 0; //Turn off all ports + PCMSK2 = 0; //Turn off pin D5 + PCMSK1 = 0; //Turn off pin A1 + + ADCSRA = old_ADCSRA; // re-enable ADC conversion + + if (keyer_awake){ + digitalWrite(keyer_awake,KEYER_AWAKE_PIN_AWAKE_STATE); + } + + last_activity_time = millis(); + #ifdef FEATURE_LCD_BACKLIGHT_AUTO_DIM + last_active_time = millis(); + #endif //FEATURE_LCD_BACKLIGHT_AUTO_DIM + + #ifdef DEBUG_SLEEP + debug_serial_port->println(F("check_sleep: I'm awake!")); + #endif //DEBUG_SLEEP + } + + +} +#endif //FEATURE_SLEEP + +//------------------------------------------------------------------------------------------------------- + +#ifdef FEATURE_LCD_BACKLIGHT_AUTO_DIM +void check_backlight() { + + static unsigned long last_bl_check = 0 ; + + if (millis() - last_bl_check < 1000) return; // not time-critical + + last_bl_check = millis(); + + if ((last_bl_check - last_active_time) > ((unsigned long)dim_backlight_inactive_time*60000)){ + + #ifdef DEBUG_BACKLIGHT + debug_serial_port->println(F("check_backlight: I'm asleep!")); + #endif //DEBUG_BACKLIGHT + + if (keyer_power_led){ + analogWrite(keyer_power_led,keyer_power_led_asleep_duty); + } + lcd.noBacklight(); + + } else { + + #ifdef DEBUG_BACKLIGHT + debug_serial_port->println(F("check_backlight: I'm awake!")); + #endif //DEBUG_BACKLIGHT + + if (keyer_power_led){ + analogWrite(keyer_power_led,keyer_power_led_awake_duty); + } + lcd.backlight(); + } + + +} +#endif //FEATURE_LCD_BACKLIGHT_AUTO_DIM + +//------------------------------------------------------------------------------------------------------- + +#ifdef FEATURE_DISPLAY +void service_display() { + + #ifdef DEBUG_LOOP + debug_serial_port->println(F("loop: entering service_display")); + #endif + + #define SCREEN_REFRESH_IDLE 0 + #define SCREEN_REFRESH_INIT 1 + #define SCREEN_REFRESH_IN_PROGRESS 2 + + static byte x = 0; + static byte y = 0; + + static byte screen_refresh_status = SCREEN_REFRESH_IDLE; + + if (screen_refresh_status == SCREEN_REFRESH_INIT){ + lcd.setCursor(0,0); + y = 0; + x = 0; + screen_refresh_status = SCREEN_REFRESH_IN_PROGRESS; + return; + } + + if (screen_refresh_status == SCREEN_REFRESH_IN_PROGRESS){ + if (x > lcd_scroll_buffer[y].length()){ + y++; + if (y >= LCD_ROWS){ + screen_refresh_status = SCREEN_REFRESH_IDLE; + lcd_scroll_buffer_dirty = 0; + return; + } else { + x = 0; + lcd.setCursor(0,y); + } + } else { + if (lcd_scroll_buffer[y].charAt(x) > 0){ + #ifdef FEATURE_LCD_BACKLIGHT_AUTO_DIM + lcd.backlight(); + #endif // FEATURE_LCD_BACKLIGHT_AUTO_DIM + lcd.print(lcd_scroll_buffer[y].charAt(x)); + } + x++; + } + } + + if (screen_refresh_status == SCREEN_REFRESH_IDLE){ + if (lcd_status == LCD_REVERT) { + lcd_status = lcd_previous_status; + switch (lcd_status) { + case LCD_CLEAR: lcd_clear(); break; + case LCD_SCROLL_MSG: + lcd.clear(); + // for (x = 0;x < LCD_ROWS;x++){ + // //clear_display_row(x); + // lcd.setCursor(0,x); + // lcd.print(lcd_scroll_buffer[x]); + // } + screen_refresh_status = SCREEN_REFRESH_INIT; + lcd_scroll_flag = 0; + //lcd_scroll_buffer_dirty = 0; + break; + } + } else { + switch (lcd_status) { + case LCD_CLEAR : break; + case LCD_TIMED_MESSAGE: + if (millis() > lcd_timed_message_clear_time) { + lcd_status = LCD_REVERT; + } + case LCD_SCROLL_MSG: + if (lcd_scroll_buffer_dirty) { + if (lcd_scroll_flag) { + lcd.clear(); + lcd_scroll_flag = 0; + } + // for (x = 0;x < LCD_ROWS;x++){ + // //clear_display_row(x); + // lcd.setCursor(0,x); + // lcd.print(lcd_scroll_buffer[x]); + // } + //lcd_scroll_buffer_dirty = 0; + screen_refresh_status = SCREEN_REFRESH_INIT; + } + break; + } + } + } + +} +#endif + + + +//------------------------------------------------------------------------------------------------------- + +#ifdef FEATURE_DISPLAY +void display_scroll_print_char(char charin){ + + static byte column_pointer = 0; + static byte row_pointer = 0; + static byte holding_space = 0; + byte x = 0; + + #ifdef DEBUG_DISPLAY_SCROLL_PRINT_CHAR + debug_serial_port->print(F("display_scroll_print_char: ")); + debug_serial_port->write(charin); + debug_serial_port->print(" "); + debug_serial_port->println(charin); + #endif //DEBUG_DISPLAY_SCROLL_PRINT_CHAR + + #ifdef OPTION_DISPLAY_NON_ENGLISH_EXTENSIONS + switch (byte(charin)){ + case 220: charin = 0;break; // U_umlaut (D, ...) + case 214: charin = 1;break; // O_umlaut (D, SM, OH, ...) + case 196: charin = 2;break; // A_umlaut (D, SM, OH, ...) + case 198: charin = 3;break; // AE_capital (OZ, LA) + case 216: charin = 4;break; // OE_capital (OZ, LA) + case 197: charin = 6;break; // AA_capital (OZ, LA, SM) + case 209: charin = 7;break; // N-tilde (EA) + } + #endif //OPTION_DISPLAY_NON_ENGLISH_EXTENSIONS + + if (lcd_status != LCD_SCROLL_MSG) { + lcd_status = LCD_SCROLL_MSG; + lcd.clear(); + } + + if (charin == ' '){ + holding_space = 1; + return; + } + + if (holding_space){ // ok, I admit this is a hack. Hold on to spaces and don't scroll until the next char comes in... + if (column_pointer > (LCD_COLUMNS-1)) { + row_pointer++; + column_pointer = 0; + if (row_pointer > (LCD_ROWS-1)) { + for (x = 0; x < (LCD_ROWS-1); x++) { + lcd_scroll_buffer[x] = lcd_scroll_buffer[x+1]; + } + lcd_scroll_buffer[x] = ""; + row_pointer--; + lcd_scroll_flag = 1; + } + } + if (column_pointer > 0){ // don't put a space in the first column + lcd_scroll_buffer[row_pointer].concat(' '); + column_pointer++; + } + holding_space = 0; + } + + + + if (column_pointer > (LCD_COLUMNS-1)) { + row_pointer++; + column_pointer = 0; + if (row_pointer > (LCD_ROWS-1)) { + for (x = 0; x < (LCD_ROWS-1); x++) { + lcd_scroll_buffer[x] = lcd_scroll_buffer[x+1]; + } + lcd_scroll_buffer[x] = ""; + row_pointer--; + lcd_scroll_flag = 1; + } + } + lcd_scroll_buffer[row_pointer].concat(charin); + column_pointer++; + + + lcd_scroll_buffer_dirty = 1; +} + +#endif //FEATURE_DISPLAY + + +//------------------------------------------------------------------------------------------------------- +#ifdef FEATURE_DISPLAY +void lcd_clear() { + lcd.clear(); + lcd.noCursor();//sp5iou 20180328 + lcd_status = LCD_CLEAR; + +} +#endif +//------------------------------------------------------------------------------------------------------- +#ifdef FEATURE_DISPLAY +void lcd_center_print_timed(String lcd_print_string, byte row_number, unsigned int duration) +{ + #ifdef FEATURE_LCD_BACKLIGHT_AUTO_DIM + lcd.backlight(); + #endif //FEATURE_LCD_BACKLIGHT_AUTO_DIM + lcd.noCursor();//sp5iou 20180328 + if (lcd_status != LCD_TIMED_MESSAGE) { + lcd_previous_status = lcd_status; + lcd_status = LCD_TIMED_MESSAGE; + lcd.clear(); + } else { + clear_display_row(row_number); + } + lcd.setCursor(((LCD_COLUMNS - lcd_print_string.length())/2),row_number); + lcd.print(lcd_print_string); + lcd_timed_message_clear_time = millis() + duration; +} +#endif + +//------------------------------------------------------------------------------------------------------- + +#ifdef FEATURE_DISPLAY +void clear_display_row(byte row_number) +{ + #ifdef FEATURE_LCD_BACKLIGHT_AUTO_DIM + lcd.backlight(); + #endif //FEATURE_LCD_BACKLIGHT_AUTO_DIM + lcd.noCursor();//sp5iou 20180328 + for (byte x = 0; x < LCD_COLUMNS; x++) { + lcd.setCursor(x,row_number); + lcd.print(" "); + } +} +#endif + +//------------------------------------------------------------------------------------------------------- + +void check_for_dirty_configuration() +{ + #ifdef DEBUG_LOOP + debug_serial_port->println(F("loop: entering check_for_dirty_configuration")); + #endif + + //if ((config_dirty) && ((millis()-last_config_write)>30000) && (!send_buffer_bytes) && (!ptt_line_activated)) { + if ((config_dirty) && ((millis()-last_config_write)>eeprom_write_time_ms) && (!send_buffer_bytes) && (!ptt_line_activated) && (!dit_buffer) && (!dah_buffer) && (!async_eeprom_write) && (paddle_pin_read(paddle_left) == HIGH) && (paddle_pin_read(paddle_right) == HIGH) ) { + write_settings_to_eeprom(0); + last_config_write = millis(); + #ifdef DEBUG_EEPROM + debug_serial_port->println(F("check_for_dirty_configuration: wrote config\n")); + #endif + } + +} + +//------------------------------------------------------------------------------------------------------- +#ifdef FEATURE_MEMORIES +void check_memory_repeat() { + + #ifdef DEBUG_LOOP + debug_serial_port->println(F("loop: entering check_memory_repeat")); + #endif + + if ((repeat_memory < number_of_memories) && ((millis() - last_memory_repeat_time) > configuration.memory_repeat_time)) { + add_to_send_buffer(SERIAL_SEND_BUFFER_MEMORY_NUMBER); + add_to_send_buffer(repeat_memory); + last_memory_repeat_time = millis(); + #ifdef DEBUG_MEMORIES + debug_serial_port->print(F("check_memory_repeat: added repeat_memory to send buffer\n\r")); + #endif + } + + if (repeat_memory == 255){last_memory_repeat_time = 0;} + +} +#endif + +//------------------------------------------------------------------------------------------------------- + +#ifdef FEATURE_DEAD_OP_WATCHDOG +void check_for_dead_op() + + // if the dit or dah paddle is stuck, disable the transmitter line after 100 straight dits or dahs + // go in and out of command mode to clear or just reset the unit + +{ + + #ifdef DEBUG_LOOP + debug_serial_port->println(F("loop: entering check_for_dead_op")); + #endif + + if (dead_op_watchdog_active && ((dit_counter > 100) || (dah_counter > 100))) { + key_tx = 0; + } +} +#endif +//------------------------------------------------------------------------------------------------------- + +#if (defined(FEATURE_PS2_KEYBOARD) || defined(FEATURE_USB_KEYBOARD)) && defined(FEATURE_MEMORIES) + +void repeat_memory_msg(byte memory_number){ + + #ifdef FEATURE_MEMORIES + repeat_memory = memory_number; + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("RptMem" + String(memory_number+1), 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Repeat Memory " + String(memory_number+1), 0, default_display_msg_delay); + } + service_display(); + #endif //FEATURE_DISPLAY + #endif //FEATURE_MEMORIES +} + +#endif //defined(FEATURE_PS2_KEYBOARD) || defined(FEATURE_USB_KEYBOARD) + +//------------------------------------------------------------------------------------------------------- + +#ifdef FEATURE_PS2_KEYBOARD +void check_ps2_keyboard() +{ + + #ifdef DEBUG_LOOP + debug_serial_port->println(F("loop: entering check_ps2_keyboard")); + #endif + + static byte keyboard_tune_on = 0; + static byte ps2_prosign_flag = 0; + int work_int = 0; + uint8_t keystroke = 0; + + /* NOTE!!! This entire block of code is repeated again below the #else. This was done to fix a bug with Notepad++ not + collapsing code correctly when while() statements are encapsulated in #ifdef/#endifs. */ + + #ifdef FEATURE_MEMORIES + while ((keyboard.available()) && (play_memory_prempt == 0)) { + + // read the next key + keystroke = keyboard.read(); + + #if defined(DEBUG_PS2_KEYBOARD) + debug_serial_port->print("check_ps2_keyboard: keystroke: "); + debug_serial_port->println(keystroke,DEC); + #endif //DEBUG_PS2_KEYBOARD + + #ifdef FEATURE_SLEEP + last_activity_time = millis(); + #endif //FEATURE_SLEEP + #ifdef FEATURE_LCD_BACKLIGHT_AUTO_DIM + last_active_time = millis(); + #endif //FEATURE_LCD_BACKLIGHT_AUTO_DIM + + if (ps2_keyboard_mode == PS2_KEYBOARD_NORMAL) { + switch (keystroke) { + case PS2_PAGEUP : sidetone_adj(20); break; + case PS2_PAGEDOWN : sidetone_adj(-20); break; + case PS2_RIGHTARROW : adjust_dah_to_dit_ratio(int(configuration.dah_to_dit_ratio/10)); break; + case PS2_LEFTARROW : adjust_dah_to_dit_ratio(-1*int(configuration.dah_to_dit_ratio/10)); break; + case PS2_UPARROW : speed_set(configuration.wpm+1); break; + case PS2_DOWNARROW : speed_set(configuration.wpm-1); break; + case PS2_HOME : + configuration.dah_to_dit_ratio = initial_dah_to_dit_ratio; + key_tx = 1; + config_dirty = 1; + #ifdef FEATURE_DISPLAY + #ifdef OPTION_MORE_DISPLAY_MSGS + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("DfltRtio", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Default ratio", 0, default_display_msg_delay); + } + service_display(); + #endif + #endif + break; + case PS2_TAB : + if (pause_sending_buffer) { + pause_sending_buffer = 0; + #ifdef FEATURE_DISPLAY + #ifdef OPTION_MORE_DISPLAY_MSGS + lcd_center_print_timed("Resume", 0, default_display_msg_delay); + #endif + #endif + } else { + pause_sending_buffer = 1; + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("Pause", 0, default_display_msg_delay); + #endif + } + break; // pause + + case PS2_SCROLL : // Prosign next two characters + ps2_prosign_flag = 1; + #ifdef FEATURE_DISPLAY + #ifdef OPTION_MORE_DISPLAY_MSGS + lcd_center_print_timed("Prosign", 0, default_display_msg_delay); + #endif + #endif + break; + + #ifdef FEATURE_MEMORIES + case PS2_F1 : ps2_usb_keyboard_play_memory(0); break; + case PS2_F2 : ps2_usb_keyboard_play_memory(1); break; + case PS2_F3 : ps2_usb_keyboard_play_memory(2); break; + #ifndef OPTION_SAVE_MEMORY_NANOKEYER + case PS2_F4 : ps2_usb_keyboard_play_memory(3); break; + case PS2_F5 : ps2_usb_keyboard_play_memory(4); break; + case PS2_F6 : ps2_usb_keyboard_play_memory(5); break; + case PS2_F7 : ps2_usb_keyboard_play_memory(6); break; + case PS2_F8 : ps2_usb_keyboard_play_memory(7); break; + case PS2_F9 : ps2_usb_keyboard_play_memory(8); break; + case PS2_F10 : ps2_usb_keyboard_play_memory(9); break; + case PS2_F11 : ps2_usb_keyboard_play_memory(10); break; + case PS2_F12 : ps2_usb_keyboard_play_memory(11); break; + #endif //OPTION_SAVE_MEMORY_NANOKEYER + case PS2_F1_ALT : if (number_of_memories > 0) {repeat_memory_msg(0);} break; + case PS2_F2_ALT : if (number_of_memories > 1) {repeat_memory_msg(1);} break; + case PS2_F3_ALT : if (number_of_memories > 2) {repeat_memory_msg(2);} break; + #ifndef OPTION_SAVE_MEMORY_NANOKEYER + case PS2_F4_ALT : if (number_of_memories > 3) {repeat_memory_msg(3);} break; + case PS2_F5_ALT : if (number_of_memories > 4) {repeat_memory_msg(4);} break; + case PS2_F6_ALT : if (number_of_memories > 5) {repeat_memory_msg(5);} break; + case PS2_F7_ALT : if (number_of_memories > 6) {repeat_memory_msg(6);} break; + case PS2_F8_ALT : if (number_of_memories > 7) {repeat_memory_msg(7);} break; + case PS2_F9_ALT : if (number_of_memories > 8) {repeat_memory_msg(8);} break; + case PS2_F10_ALT : if (number_of_memories > 9) {repeat_memory_msg(9);} break; + case PS2_F11_ALT : if (number_of_memories > 10) {repeat_memory_msg(10);} break; + case PS2_F12_ALT : if (number_of_memories > 11) {repeat_memory_msg(11);} break; + #endif //OPTION_SAVE_MEMORY_NANOKEYER + #endif //FEATURE_MEMORIES + + case PS2_DELETE : if (send_buffer_bytes) { send_buffer_bytes--; } break; + case PS2_ESC : // clear the serial send buffer and a bunch of other stuff + if (manual_ptt_invoke) { + manual_ptt_invoke = 0; + ptt_unkey(); + } + if (keyboard_tune_on) { + sending_mode = MANUAL_SENDING; + tx_and_sidetone_key(0); + keyboard_tune_on = 0; + } + if (pause_sending_buffer) { + pause_sending_buffer = 0; + } + clear_send_buffer(); + #ifdef FEATURE_MEMORIES + //clear_memory_button_buffer(); + play_memory_prempt = 1; + repeat_memory = 255; + #endif + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("Abort", 0, default_display_msg_delay); + #endif + break; + + #ifdef FEATURE_MEMORIES + case PS2_F1_SHIFT : + ps2_keyboard_program_memory(0); + break; + + case PS2_F2_SHIFT : + ps2_keyboard_program_memory(1); + break; + + case PS2_F3_SHIFT : + ps2_keyboard_program_memory(2); + break; + + + #ifndef OPTION_SAVE_MEMORY_NANOKEYER + case PS2_F4_SHIFT : + ps2_keyboard_program_memory(3); + break; + + case PS2_F5_SHIFT : + ps2_keyboard_program_memory(4); + break; + + case PS2_F6_SHIFT : + ps2_keyboard_program_memory(5); + break; + + case PS2_F7_SHIFT : + ps2_keyboard_program_memory(6); + break; + + case PS2_F8_SHIFT : + ps2_keyboard_program_memory(7); + break; + + case PS2_F9_SHIFT : + ps2_keyboard_program_memory(8); + break; + + case PS2_F10_SHIFT : + ps2_keyboard_program_memory(9); + break; + + case PS2_F11_SHIFT : + ps2_keyboard_program_memory(10); + break; + + case PS2_F12_SHIFT : + ps2_keyboard_program_memory(11); + break; + #endif //OPTION_SAVE_MEMORY_NANOKEYER + #endif //FEATURE_MEMORIES + + #ifndef OPTION_SAVE_MEMORY_NANOKEYER + case PS2_INSERT : // send serial number and increment + put_serial_number_in_send_buffer(); + serial_number++; + break; + + case PS2_END : // send serial number no increment + put_serial_number_in_send_buffer(); + break; + + case PS2_BACKSPACE_SHIFT : // decrement serial number + serial_number--; + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("SN " + String(serial_number), 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Serial: " + String(serial_number), 0, default_display_msg_delay); + } + #endif + break; + + #endif //OPTION_SAVE_MEMORY_NANOKEYER + + case PS2_LEFT_ALT : + #ifdef DEBUG_PS2_KEYBOARD + debug_serial_port->println("PS2_LEFT_ALT\n"); + #endif + break; + + + case PS2_A_CTRL : + configuration.keyer_mode = IAMBIC_A; + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("Iambic A", 0, default_display_msg_delay); + #endif + + config_dirty = 1; + break; + + case PS2_B_CTRL : + configuration.keyer_mode = IAMBIC_B; + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("Iambic B", 0, default_display_msg_delay); + #endif + config_dirty = 1; + break; + + case PS2_C_CTRL : + configuration.keyer_mode = SINGLE_PADDLE; + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("SnglePdl", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Single Paddle", 0, default_display_msg_delay); + } + #endif + config_dirty = 1; + break; + + #ifndef OPTION_NO_ULTIMATIC + case PS2_D_CTRL : + configuration.keyer_mode = ULTIMATIC; + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("Ultimatc", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Ultimatic", 0, default_display_msg_delay); + } + #endif + config_dirty = 1; + break; + #endif // OPTION_NO_ULTIMATIC + #ifndef OPTION_SAVE_MEMORY_NANOKEYER + case PS2_E_CTRL : + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("EnterSN", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Enter Serial #", 0, default_display_msg_delay); + } + #else + boop_beep(); + #endif + work_int = ps2_keyboard_get_number_input(4,0,10000); + if (work_int > 0) { + serial_number = work_int; + #ifdef FEATURE_DISPLAY + lcd_status = LCD_REVERT; + #else + beep(); + #endif + } + break; + #endif //OPTION_SAVE_MEMORY_NANOKEYER + + case PS2_G_CTRL : + configuration.keyer_mode = BUG; + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("Bug", 0, default_display_msg_delay); + #endif + config_dirty = 1; + break; + + #ifdef FEATURE_HELL + case PS2_H_CTRL : + if (char_send_mode == CW) { + char_send_mode = HELL; + beep(); + } else { + char_send_mode = CW; + beep(); + } + break; + #endif //FEATURE_HELL + + case PS2_I_CTRL : + if (key_tx && keyer_machine_mode != KEYER_COMMAND_MODE) { //Added check that keyer is NOT in command mode or keyer might be enabled for paddle commands (WD9DMP) + key_tx = 0; + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("TX off", 0, default_display_msg_delay); + #endif + + } else if (!key_tx && keyer_machine_mode != KEYER_COMMAND_MODE) { //Added check that keyer is NOT in command mode or keyer might be enabled for paddle commands (WD9DMP) + key_tx = 1; + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("TX on", 0, default_display_msg_delay); + #endif + } + break; + + #ifdef FEATURE_FARNSWORTH + case PS2_M_CTRL: + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("Frnswrth", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Farnsworth WPM", 0, default_display_msg_delay); + } + #else + boop_beep(); + #endif + work_int = ps2_keyboard_get_number_input(3,-1,1000); + if (work_int > -1) { + configuration.wpm_farnsworth = work_int; + #ifdef FEATURE_DISPLAY + lcd_status = LCD_REVERT; + #else + beep(); + #endif + config_dirty = 1; + } + + break; + #endif //FEATURE_FARNSWORTH + + case PS2_N_CTRL : + if (configuration.paddle_mode == PADDLE_NORMAL) { + configuration.paddle_mode = PADDLE_REVERSE; + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("Pdl Rev", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Paddle Reverse", 0, default_display_msg_delay); + } + #endif + } else { + configuration.paddle_mode = PADDLE_NORMAL; + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("Pdl Norm", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Paddle Normal", 0, default_display_msg_delay); + } + #endif + } + config_dirty = 1; + break; + + case PS2_O_CTRL : // CTRL-O - cycle through sidetone modes on, paddle only and off - New code Marc-Andre, VE2EVN + if (configuration.sidetone_mode == SIDETONE_PADDLE_ONLY) { + configuration.sidetone_mode = SIDETONE_OFF; + boop(); + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("ST Off", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Sidetone Off", 0, default_display_msg_delay); + } + #endif + } else if (configuration.sidetone_mode == SIDETONE_ON) { + configuration.sidetone_mode = SIDETONE_PADDLE_ONLY; + beep(); + delay(200); + beep(); + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("ST Pdl O", 0, default_display_msg_delay); + } + if (LCD_COLUMNS > 19){ + lcd_center_print_timed("Sidetone Paddle Only", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Sidetone", 0, default_display_msg_delay); + lcd_center_print_timed("Paddle Only", 1, default_display_msg_delay); + } + #endif + } else { + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("ST On", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Sidetone On", 0, default_display_msg_delay); + } + #endif + configuration.sidetone_mode = SIDETONE_ON; + beep(); + } + config_dirty = 1; + break; + + + #if defined(FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING) + case PS2_S_CTRL : + if (configuration.cmos_super_keyer_iambic_b_timing_on){ + configuration.cmos_super_keyer_iambic_b_timing_on = 0; + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("CMOS Superkeyer Off", 0, default_display_msg_delay); + #endif + } else { + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("CMOS Superkeyer On", 0, default_display_msg_delay); + #endif + configuration.cmos_super_keyer_iambic_b_timing_on = 1; + } + config_dirty = 1; + break; + #endif //FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING + + case PS2_T_CTRL : + #ifdef FEATURE_MEMORIES + repeat_memory = 255; + #endif + if (keyboard_tune_on) { + sending_mode = MANUAL_SENDING; + tx_and_sidetone_key(0); + keyboard_tune_on = 0; + #ifdef FEATURE_DISPLAY + lcd_status = LCD_REVERT; + #endif // FEATURE_DISPLAY + } else { + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("Tune", 0, default_display_msg_delay); + #endif + sending_mode = MANUAL_SENDING; + tx_and_sidetone_key(1); + keyboard_tune_on = 1; + } + break; + + case PS2_U_CTRL : + if (ptt_line_activated) { + manual_ptt_invoke = 0; + ptt_unkey(); + #ifdef FEATURE_DISPLAY + lcd_status = LCD_REVERT; + #endif // FEATURE_DISPLAY + } else { + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("PTTInvk", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("PTT Invoke", 0, default_display_msg_delay); + } + #endif + manual_ptt_invoke = 1; + ptt_key(); + } + break; + + case PS2_W_CTRL : + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("WPM Adj", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("WPM Adjust", 0, default_display_msg_delay); + } + #else + boop_beep(); + #endif + work_int = ps2_keyboard_get_number_input(3,0,1000); + if (work_int > 0) { + speed_set(work_int); + #ifdef FEATURE_DISPLAY + lcd_status = LCD_REVERT; + #else + beep(); + #endif + config_dirty = 1; + } + break; + + case PS2_F1_CTRL : + switch_to_tx_silent(1); + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("TX 1", 0, default_display_msg_delay); + #endif + break; + + case PS2_F2_CTRL : + if ((ptt_tx_2) || (tx_key_line_2)) { + switch_to_tx_silent(2); + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("TX 2", 0, default_display_msg_delay); + #endif + } + break; + #ifndef OPTION_SAVE_MEMORY_NANOKEYER + case PS2_F3_CTRL : + if ((ptt_tx_3) || (tx_key_line_3)) { + switch_to_tx_silent(3); + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("TX 3", 0, default_display_msg_delay); + #endif + } + break; + + case PS2_F4_CTRL : + if ((ptt_tx_4) || (tx_key_line_4)) { + switch_to_tx_silent(4); + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("TX 4", 0, default_display_msg_delay); + #endif + } + break; + + case PS2_F5_CTRL : + if ((ptt_tx_5) || (tx_key_line_5)) { + switch_to_tx_silent(5); + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("TX 5", 0, default_display_msg_delay); + #endif + } + break; + + case PS2_F6_CTRL : + if ((ptt_tx_6) || (tx_key_line_6)) { + switch_to_tx_silent(6); + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("TX 6", 0, default_display_msg_delay); + #endif + } + break; + #endif //OPTION_SAVE_MEMORY_NANOKEYER + + #ifdef FEATURE_AUTOSPACE + case PS2_Z_CTRL: + if (configuration.autospace_active) { + configuration.autospace_active = 0; + config_dirty = 1; + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("AutoSOff", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Autospace Off", 0, default_display_msg_delay); + } + #endif + } else { + configuration.autospace_active = 1; + config_dirty = 1; + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("AutoSpOn", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Autospace On", 0, default_display_msg_delay); + } + #endif + } + break; + #endif + + default : + if ((keystroke > 31) && (keystroke < 255 /*123*/)) { + if (ps2_prosign_flag) { + add_to_send_buffer(SERIAL_SEND_BUFFER_PROSIGN); + ps2_prosign_flag = 0; + } + keystroke = uppercase(keystroke); + add_to_send_buffer(keystroke); + #ifdef FEATURE_MEMORIES + repeat_memory = 255; + #endif + } + break; + } + } else { + + } + } //while ((keyboard.available()) && (play_memory_prempt == 0)) + + + + #else //FEATURE_MEMORIES -------------------------------------------------------------------- + + + + while (keyboard.available()) { + + // read the next key + keystroke = keyboard.read(); + + #ifdef FEATURE_SLEEP + last_activity_time = millis(); + #endif //FEATURE_SLEEP + #ifdef FEATURE_LCD_BACKLIGHT_AUTO_DIM + last_active_time = millis(); + #endif //FEATURE_LCD_BACKLIGHT_AUTO_DIM + + if (ps2_keyboard_mode == PS2_KEYBOARD_NORMAL) { + switch (keystroke) { + case PS2_PAGEUP : sidetone_adj(20); break; + case PS2_PAGEDOWN : sidetone_adj(-20); break; + case PS2_RIGHTARROW : adjust_dah_to_dit_ratio(int(configuration.dah_to_dit_ratio/10)); break; + case PS2_LEFTARROW : adjust_dah_to_dit_ratio(-1*int(configuration.dah_to_dit_ratio/10)); break; + case PS2_UPARROW : speed_set(configuration.wpm+1); break; + case PS2_DOWNARROW : speed_set(configuration.wpm-1); break; + case PS2_HOME : + configuration.dah_to_dit_ratio = initial_dah_to_dit_ratio; + key_tx = 1; + config_dirty = 1; + #ifdef FEATURE_DISPLAY + #ifdef OPTION_MORE_DISPLAY_MSGS + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("DfltRtio", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Default ratio", 0, default_display_msg_delay); + } + service_display(); + #endif + #endif + break; + case PS2_TAB : + if (pause_sending_buffer) { + pause_sending_buffer = 0; + #ifdef FEATURE_DISPLAY + #ifdef OPTION_MORE_DISPLAY_MSGS + lcd_center_print_timed("Resume", 0, default_display_msg_delay); + #endif + #endif + } else { + pause_sending_buffer = 1; + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("Pause", 0, default_display_msg_delay); + #endif + } + break; // pause + + case PS2_SCROLL : // Prosign next two characters + ps2_prosign_flag = 1; + #ifdef FEATURE_DISPLAY + #ifdef OPTION_MORE_DISPLAY_MSGS + lcd_center_print_timed("Prosign", 0, default_display_msg_delay); + #endif + #endif + break; + + #ifdef FEATURE_MEMORIES + case PS2_F1 : ps2_usb_keyboard_play_memory(0); break; + case PS2_F2 : ps2_usb_keyboard_play_memory(1); break; + case PS2_F3 : ps2_usb_keyboard_play_memory(2); break; + case PS2_F4 : ps2_usb_keyboard_play_memory(3); break; + case PS2_F5 : ps2_usb_keyboard_play_memory(4); break; + case PS2_F6 : ps2_usb_keyboard_play_memory(5); break; + case PS2_F7 : ps2_usb_keyboard_play_memory(6); break; + case PS2_F8 : ps2_usb_keyboard_play_memory(7); break; + case PS2_F9 : ps2_usb_keyboard_play_memory(8); break; + case PS2_F10 : ps2_usb_keyboard_play_memory(9); break; + case PS2_F11 : ps2_usb_keyboard_play_memory(10); break; + case PS2_F12 : ps2_usb_keyboard_play_memory(11); break; + case PS2_F1_ALT : if (number_of_memories > 0) {repeat_memory_msg(0);} break; + case PS2_F2_ALT : if (number_of_memories > 1) {repeat_memory_msg(1);} break; + case PS2_F3_ALT : if (number_of_memories > 2) {repeat_memory_msg(2);} break; + case PS2_F4_ALT : if (number_of_memories > 3) {repeat_memory_msg(3);} break; + case PS2_F5_ALT : if (number_of_memories > 4) {repeat_memory_msg(4);} break; + case PS2_F6_ALT : if (number_of_memories > 5) {repeat_memory_msg(5);} break; + case PS2_F7_ALT : if (number_of_memories > 6) {repeat_memory_msg(6);} break; + case PS2_F8_ALT : if (number_of_memories > 7) {repeat_memory_msg(7);} break; + case PS2_F9_ALT : if (number_of_memories > 8) {repeat_memory_msg(8);} break; + case PS2_F10_ALT : if (number_of_memories > 9) {repeat_memory_msg(9);} break; + case PS2_F11_ALT : if (number_of_memories > 10) {repeat_memory_msg(10);} break; + case PS2_F12_ALT : if (number_of_memories > 11) {repeat_memory_msg(11);} break; + #endif + + case PS2_DELETE : if (send_buffer_bytes) { send_buffer_bytes--; } break; + case PS2_ESC : // clear the serial send buffer and a bunch of other stuff + if (manual_ptt_invoke) { + manual_ptt_invoke = 0; + ptt_unkey(); + } + if (keyboard_tune_on) { + sending_mode = MANUAL_SENDING; + tx_and_sidetone_key(0); + keyboard_tune_on = 0; + } + if (pause_sending_buffer) { + pause_sending_buffer = 0; + } + clear_send_buffer(); + #ifdef FEATURE_MEMORIES + //clear_memory_button_buffer(); + play_memory_prempt = 1; + repeat_memory = 255; + #endif + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("Abort", 0, default_display_msg_delay); + #endif + break; + + #ifdef FEATURE_MEMORIES + case PS2_F1_SHIFT : + ps2_keyboard_program_memory(0); + break; + + case PS2_F2_SHIFT : + ps2_keyboard_program_memory(1); + break; + + case PS2_F3_SHIFT : + ps2_keyboard_program_memory(2); + break; + + case PS2_F4_SHIFT : + ps2_keyboard_program_memory(3); + break; + + case PS2_F5_SHIFT : + ps2_keyboard_program_memory(4); + break; + + case PS2_F6_SHIFT : + ps2_keyboard_program_memory(5); + break; + + case PS2_F7_SHIFT : + ps2_keyboard_program_memory(6); + break; + + case PS2_F8_SHIFT : + ps2_keyboard_program_memory(7); + break; + + case PS2_F9_SHIFT : + ps2_keyboard_program_memory(8); + break; + + case PS2_F10_SHIFT : + ps2_keyboard_program_memory(9); + break; + + case PS2_F11_SHIFT : + ps2_keyboard_program_memory(10); + break; + + case PS2_F12_SHIFT : + ps2_keyboard_program_memory(11); + break; + #endif //FEATURE_MEMORIES + + case PS2_INSERT : // send serial number and increment + put_serial_number_in_send_buffer(); + serial_number++; + break; + + case PS2_END : // send serial number no increment + put_serial_number_in_send_buffer(); + break; + + case PS2_BACKSPACE_SHIFT : // decrement serial number + serial_number--; + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("SN " + String(serial_number), 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Serial: " + String(serial_number), 0, default_display_msg_delay); + } + #endif + break; + + case PS2_LEFT_ALT : + #ifdef DEBUG_PS2_KEYBOARD + debug_serial_port->println("PS2_LEFT_ALT\n"); + #endif + break; + + case PS2_A_CTRL : + configuration.keyer_mode = IAMBIC_A; + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("Iambic A", 0, default_display_msg_delay); + #endif + + config_dirty = 1; + break; + + case PS2_B_CTRL : + configuration.keyer_mode = IAMBIC_B; + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("Iambic B", 0, default_display_msg_delay); + #endif + config_dirty = 1; + break; + + case PS2_C_CTRL : + configuration.keyer_mode = SINGLE_PADDLE; + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("Sngl Pdl", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Single Paddle", 0, default_display_msg_delay); + } + #endif + config_dirty = 1; + break; + + #ifndef OPTION_NO_ULTIMATIC + case PS2_D_CTRL : + configuration.keyer_mode = ULTIMATIC; + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("Ultimatc", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Ultimatic", 0, default_display_msg_delay); + } + #endif + config_dirty = 1; + break; + #endif // OPTION_NO_ULTIMATIC + + case PS2_E_CTRL : + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("Enter SN", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Enter Serial #", 0, default_display_msg_delay); + } + #else + boop_beep(); + #endif + work_int = ps2_keyboard_get_number_input(4,0,10000); + if (work_int > 0) { + serial_number = work_int; + #ifdef FEATURE_DISPLAY + lcd_status = LCD_REVERT; + #else + beep(); + #endif + } + break; + + case PS2_G_CTRL : + configuration.keyer_mode = BUG; + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("Bug", 0, default_display_msg_delay); + #endif + config_dirty = 1; + break; + + case PS2_H_CTRL : + #ifdef FEATURE_HELL + if (char_send_mode == CW) { + char_send_mode = HELL; + beep(); + } else { + char_send_mode = CW; + beep(); + } + #endif //FEATURE_HELL + break; + + case PS2_I_CTRL : + if (key_tx && keyer_machine_mode != KEYER_COMMAND_MODE) { //Added check that keyer is NOT in command mode or keyer might be enabled for paddle commands (WD9DMP-1) + key_tx = 0; + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("TX Off", 0, default_display_msg_delay); + #endif + + } else if (!key_tx && keyer_machine_mode != KEYER_COMMAND_MODE) { //Added check that keyer is NOT in command mode or keyer might be enabled for paddle commands (WD9DMP-1) + key_tx = 1; + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("TX On", 0, default_display_msg_delay); + #endif + } + break; + + case PS2_M_CTRL: + #ifdef FEATURE_FARNSWORTH + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("Frnswrth", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Farnsworth WPM", 0, default_display_msg_delay); + } + #else + boop_beep(); + #endif + work_int = ps2_keyboard_get_number_input(3,-1,1000); + if (work_int > -1) { + configuration.wpm_farnsworth = work_int; + #ifdef FEATURE_DISPLAY + lcd_status = LCD_REVERT; + #else + beep(); + #endif + config_dirty = 1; + } + #endif + break; + + case PS2_N_CTRL : + if (configuration.paddle_mode == PADDLE_NORMAL) { + configuration.paddle_mode = PADDLE_REVERSE; + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("Pdl Rev", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Paddle Reverse", 0, default_display_msg_delay); + } + #endif + } else { + configuration.paddle_mode = PADDLE_NORMAL; + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("Pdl Norm", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Paddle Normal", 0, default_display_msg_delay); + } + #endif + } + config_dirty = 1; + break; + + case PS2_O_CTRL : // CTRL-O - cycle through sidetone modes on, paddle only and off - New code Marc-Andre, VE2EVN + if (configuration.sidetone_mode == SIDETONE_PADDLE_ONLY) { + configuration.sidetone_mode = SIDETONE_OFF; + boop(); + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("ST Off", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Sidetone Off", 0, default_display_msg_delay); + } + #endif + } else if (configuration.sidetone_mode == SIDETONE_ON) { + configuration.sidetone_mode = SIDETONE_PADDLE_ONLY; + beep(); + delay(200); + beep(); + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("ST Pdl O", 0, default_display_msg_delay); + } + if (LCD_COLUMNS > 19){ + lcd_center_print_timed("Sidetone Paddle Only", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Sidetone", 0, default_display_msg_delay); + lcd_center_print_timed("Paddle Only", 1, default_display_msg_delay); + } + #endif + } else { + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("ST On", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Sidetone On", 0, default_display_msg_delay); + } + #endif + configuration.sidetone_mode = SIDETONE_ON; + beep(); + } + config_dirty = 1; + break; + + case PS2_T_CTRL : + #ifdef FEATURE_MEMORIES + repeat_memory = 255; + #endif + if (keyboard_tune_on) { + sending_mode = MANUAL_SENDING; + tx_and_sidetone_key(0); + keyboard_tune_on = 0; + #ifdef FEATURE_DISPLAY + lcd_status = LCD_REVERT; + #endif // FEATURE_DISPLAY + } else { + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("Tune", 0, default_display_msg_delay); + #endif + sending_mode = MANUAL_SENDING; + tx_and_sidetone_key(1); + keyboard_tune_on = 1; + } + break; + + case PS2_U_CTRL : + if (ptt_line_activated) { + manual_ptt_invoke = 0; + ptt_unkey(); + #ifdef FEATURE_DISPLAY + lcd_status = LCD_REVERT; + #endif // FEATURE_DISPLAY + } else { + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("PTT Invk", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("PTT Invoke", 0, default_display_msg_delay); + } + #endif + manual_ptt_invoke = 1; + ptt_key(); + } + break; + + case PS2_W_CTRL : + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("WPM Adj", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("WPM Adjust", 0, default_display_msg_delay); + } + #else + boop_beep(); + #endif + work_int = ps2_keyboard_get_number_input(3,0,1000); + if (work_int > 0) { + speed_set(work_int); + #ifdef FEATURE_DISPLAY + lcd_status = LCD_REVERT; + #else + beep(); + #endif + config_dirty = 1; + } + break; + + case PS2_F1_CTRL : + switch_to_tx_silent(1); + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("TX 1", 0, default_display_msg_delay); + #endif + break; + + case PS2_F2_CTRL : + if ((ptt_tx_2) || (tx_key_line_2)) { + switch_to_tx_silent(2); + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("TX 2", 0, default_display_msg_delay); + #endif + } + break; + + case PS2_F3_CTRL : + if ((ptt_tx_3) || (tx_key_line_3)) { + switch_to_tx_silent(3); + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("TX 3", 0, default_display_msg_delay); + #endif + } + break; + + case PS2_F4_CTRL : + if ((ptt_tx_4) || (tx_key_line_4)) { + switch_to_tx_silent(4); + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("TX 4", 0, default_display_msg_delay); + #endif + } + break; + + case PS2_F5_CTRL : + if ((ptt_tx_5) || (tx_key_line_5)) { + switch_to_tx_silent(5); + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("TX 5", 0, default_display_msg_delay); + #endif + } + break; + + case PS2_F6_CTRL : + if ((ptt_tx_6) || (tx_key_line_6)) { + switch_to_tx_silent(6); + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("TX 6", 0, default_display_msg_delay); + #endif + } + break; + + #ifdef FEATURE_AUTOSPACE + case PS2_Z_CTRL: + if (configuration.autospace_active) { + configuration.autospace_active = 0; + config_dirty = 1; + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("AutoSOff", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Autospace Off", 0, default_display_msg_delay); + } + #endif + } else { + configuration.autospace_active = 1; + config_dirty = 1; + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("AutoS On", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Autospace On", 0, default_display_msg_delay); + } + #endif + } + break; + #endif + + default : + if ((keystroke > 31) && (keystroke < 255 /*123*/)) { + if (ps2_prosign_flag) { + add_to_send_buffer(SERIAL_SEND_BUFFER_PROSIGN); + ps2_prosign_flag = 0; + } + keystroke = uppercase(keystroke); + add_to_send_buffer(keystroke); + #ifdef FEATURE_MEMORIES + repeat_memory = 255; + #endif + } + break; + } + } else { + + } + } //while (keyboard.available()) + #endif //FEATURE_MEMORIES +} +#endif //FEATURE_PS2_KEYBOARD + +//------------------------------------------------------------------------------------------------------- +#if (defined(FEATURE_PS2_KEYBOARD) || defined(FEATURE_USB_KEYBOARD)) && defined(FEATURE_MEMORIES) +void ps2_usb_keyboard_play_memory(byte memory_number){ + + if (memory_number < number_of_memories) { + add_to_send_buffer(SERIAL_SEND_BUFFER_MEMORY_NUMBER); + add_to_send_buffer(memory_number); + #ifdef FEATURE_MEMORIES + repeat_memory = 255; + #endif //FEATURE_MEMORIES + } +} +#endif //defined(FEATURE_PS2_KEYBOARD) || defined(FEATURE_USB_KEYBOARD) +//------------------------------------------------------------------------------------------------------- +#if defined(FEATURE_PS2_KEYBOARD) && defined(FEATURE_MEMORIES) +void ps2_keyboard_program_memory(byte memory_number) +{ + + char keystroke; + byte looping = 1; + byte error = 0; + int temp_memory_index = 0; + byte temp_memory[(memory_end(memory_number)-memory_start(memory_number) + 1)]; + int x; + String keyboard_string; + + #ifdef FEATURE_DISPLAY + String lcd_string; + if (LCD_COLUMNS < 9){ + lcd_string = "Pgm Mem"; + } else { + lcd_string = "Program Memory"; + } + #endif + + + if (memory_number > (number_of_memories - 1)) { + boop(); + return; + } + + #ifdef FEATURE_DISPLAY + if (memory_number < 9) { + lcd_string.concat(' '); + } + lcd_string.concat(memory_number+1); + lcd_center_print_timed(lcd_string, 0, default_display_msg_delay); + #else + boop_beep(); + #endif + repeat_memory = 255; + while (looping) { + while (keyboard.available() == 0) { + if (keyer_machine_mode == KEYER_NORMAL) { // might as well do something while we're waiting + check_paddles(); + service_dit_dah_buffers(); + } + } + keystroke = keyboard.read(); + #ifdef DEBUG_PS2_KEYBOARD + debug_serial_port->println(keystroke,DEC); + #endif + if (keystroke == 13) { // did we get a carriage return? + looping = 0; + } else { + if (keystroke == PS2_BACKSPACE) { + if (temp_memory_index) { + temp_memory_index--; + #ifdef FEATURE_DISPLAY + keyboard_string = keyboard_string.substring(0,keyboard_string.length()-1); + lcd_center_print_timed(keyboard_string, 1, default_display_msg_delay); + #endif + } + } else { + if (keystroke == PS2_ESC) { + looping = 0; + error = 1; + } else { + keystroke = uppercase(keystroke); + #ifdef FEATURE_DISPLAY + keyboard_string.concat(char(keystroke)); + if (keyboard_string.length() > LCD_COLUMNS) { + lcd_center_print_timed(keyboard_string.substring((keyboard_string.length()-LCD_COLUMNS)), 1, default_display_msg_delay); + } else { + lcd_center_print_timed(keyboard_string, 1, default_display_msg_delay); + } + #endif + temp_memory[temp_memory_index] = keystroke; + temp_memory_index++; + if (temp_memory_index > (memory_end(memory_number)-memory_start(memory_number))) { + looping = 0; + } + } + } + } + } //while (looping) + + if (error) { + #ifdef FEATURE_DISPLAY + lcd_status = LCD_REVERT; + #else + boop(); + #endif + } else { + for (x = 0;x < temp_memory_index;x++) { // write to memory + EEPROM.write((memory_start(memory_number)+x),temp_memory[x]); + if ((memory_start(memory_number) + x) == memory_end(memory_number)) { // are we at last memory location? + x = temp_memory_index; + } + } + // write terminating 255 + EEPROM.write((memory_start(memory_number)+x),255); + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("Done", 0, default_display_msg_delay); + #else + beep(); + #endif + } +} +#endif + +//------------------------------------------------------------------------------------------------------- + +#ifdef FEATURE_PS2_KEYBOARD + +int ps2_keyboard_get_number_input(byte places,int lower_limit, int upper_limit) +{ + + byte looping = 1; + byte error = 0; + byte numberindex = 0; + int numbers[6]; + char keystroke; + String keyboard_string; + + #ifdef FEATURE_MEMORIES + repeat_memory = 255; + #endif + + while (looping) { + if (keyboard.available() == 0) { // wait for the next keystroke + if (keyer_machine_mode == KEYER_NORMAL) { // might as well do something while we're waiting + check_paddles(); + service_dit_dah_buffers(); + service_send_buffer(PRINTCHAR); + + check_ptt_tail(); + #ifdef FEATURE_POTENTIOMETER + if (configuration.pot_activated) { + check_potentiometer(); + } + #endif + + #ifdef FEATURE_SIDETONE_SWITCH + check_sidetone_switch(); + #endif + + #ifdef FEATURE_ROTARY_ENCODER + check_rotary_encoder(); + #endif //FEATURE_ROTARY_ENCODER + } + } else { + keystroke = keyboard.read(); + if ((keystroke > 47) && (keystroke < 58)) { // ascii 48-57 = "0" - "9") + numbers[numberindex] = keystroke; + numberindex++; + #ifdef FEATURE_DISPLAY + keyboard_string.concat(String(keystroke-48)); + lcd_center_print_timed(keyboard_string, 1, default_display_msg_delay); + #endif + if (numberindex > places){ + looping = 0; + error = 1; + } + } else { + if (keystroke == PS2_BACKSPACE) { + if (numberindex) { + numberindex--; + #ifdef FEATURE_DISPLAY + keyboard_string = keyboard_string.substring(0,keyboard_string.length()-1); + lcd_center_print_timed(keyboard_string, 1, default_display_msg_delay); + #endif + } + } else { + if (keystroke == PS2_ENTER) { // carriage return - get out + looping = 0; + } else { // bogus input - error out + looping = 0; + error = 1; + } + } + } + } + } + if (error) { + boop(); + return(-1); + } else { + int y = 1; + int return_number = 0; + for (int x = (numberindex - 1); x >= 0 ; x = x - 1) { + return_number = return_number + ((numbers[x]-48) * y); + y = y * 10; + } + if ((return_number > lower_limit) && (return_number < upper_limit)) { + return(return_number); + } else { + boop(); + return(-1); + } + } +} +#endif + +//------------------------------------------------------------------------------------------------------- +#if (defined(FEATURE_PS2_KEYBOARD) || defined(FEATURE_USB_KEYBOARD)) && !defined(OPTION_SAVE_MEMORY_NANOKEYER) +void put_serial_number_in_send_buffer() +{ + + String serial_number_string; + + #ifdef FEATURE_MEMORIES + repeat_memory = 255; + #endif + + serial_number_string = String(serial_number, DEC); + if ((serial_number_string.length() < 3 ) && (serial_leading_zeros)) { + if (serial_cut_numbers) { + add_to_send_buffer('T'); + } else { + add_to_send_buffer('0'); + } + } + if ((serial_number_string.length() == 1) && (serial_leading_zeros)) { + if (serial_cut_numbers) { + add_to_send_buffer('T'); + } else { + add_to_send_buffer('0'); + } + } + for (byte a = 0; a < serial_number_string.length(); a++) { + if ((serial_number_string[a] == '0') && (serial_cut_numbers)) { + add_to_send_buffer('T'); + } else { + if ((serial_number_string[a] == '9') && (serial_cut_numbers)) { + add_to_send_buffer('N'); + } else { + add_to_send_buffer(serial_number_string[a]); + } + } + } +} +#endif //defined(FEATURE_PS2_KEYBOARD) || defined(FEATURE_USB_KEYBOARD) + +//------------------------------------------------------------------------------------------------------- + +#ifdef DEBUG_CAPTURE_COM_PORT +void debug_capture () +{ + + byte serial_byte_in; + int x = 1022; + + while (primary_serial_port->available() == 0) {} // wait for first byte + serial_byte_in = primary_serial_port->read(); + primary_serial_port->write(serial_byte_in); + //if ((serial_byte_in > 47) or (serial_byte_in = 20)) { primary_serial_port->write(serial_byte_in); } // echo back + if (serial_byte_in == '~') { + debug_capture_dump(); // go into dump mode if we get a tilde + } else { + EEPROM.write(x,serial_byte_in); + x--; + while ( x > 400) { + if (primary_serial_port->available() > 0) { + serial_byte_in = primary_serial_port->read(); + EEPROM.write(x,serial_byte_in); + EEPROM.write(x-1,255); + send_dit(); + x--; + primary_serial_port->write(serial_byte_in); + //if ((serial_byte_in > 47) or (serial_byte_in = 20)) { primary_serial_port->write(serial_byte_in); } // echo back + } + } + } + + while (1) {} + +} +#endif + +//------------------------------------------------------------------------------------------------------- + +#ifdef DEBUG_CAPTURE_COM_PORT +void debug_capture_dump() +{ + byte eeprom_byte_in; + + for ( int x = 1022; x > (1022-100); x-- ) { + eeprom_byte_in = EEPROM.read(x); + if (eeprom_byte_in < 255) { + primary_serial_port->print(eeprom_byte_in,BYTE); + } else { + x = 0; + } + } + primary_serial_port->println("\n"); + for ( int x = 1022; x > (1022-100); x-- ) { + eeprom_byte_in = EEPROM.read(x); + if (eeprom_byte_in < 255) { + primary_serial_port->print(eeprom_byte_in,HEX); + primary_serial_port->write(" :"); + primary_serial_port->println(eeprom_byte_in,BYTE); + } else { + x = 0; + } + } + + while (1) {} + +} +#endif + +//------------------------------------------------------------------------------------------------------- +#ifdef FEATURE_ROTARY_ENCODER +int chk_rotary_encoder(){ + + static unsigned long timestamp[5]; + + unsigned char pinstate = (digitalRead(rotary_pin2) << 1) | digitalRead(rotary_pin1); + state = ttable[state & 0xf][pinstate]; + unsigned char result = (state & 0x30); + if (result) { + timestamp[0] = timestamp[1]; // Encoder step timer + timestamp[1] = timestamp[2]; + timestamp[2] = timestamp[3]; + timestamp[3] = timestamp[4]; + timestamp[4] = millis(); + + unsigned long elapsed_time = (timestamp[4] - timestamp[0]); // Encoder step time difference for 10's step + + if (result == DIR_CW) { + if (elapsed_time < 250) {return 2;} else {return 1;}; + } + if (result == DIR_CCW) { + if (elapsed_time < 250) {return -2;} else {return -1;}; + } + } + return 0; +} + +void check_rotary_encoder(){ + + int step = chk_rotary_encoder(); + + if (step != 0) { + if (keyer_machine_mode == KEYER_COMMAND_MODE) speed_change_command_mode(step); + else speed_change(step); + + #ifdef FEATURE_LCD_BACKLIGHT_AUTO_DIM + last_active_time = millis(); + #endif //FEATURE_LCD_BACKLIGHT_AUTO_DIM + + // Start of Winkey Speed change mod for Rotary Encoder -- VE2EVN + #ifdef FEATURE_WINKEY_EMULATION + if ((primary_serial_port_mode == SERIAL_WINKEY_EMULATION) && (winkey_host_open)) { + winkey_port_write(((configuration.wpm-pot_wpm_low_value)|128),0); + winkey_last_unbuffered_speed_wpm = configuration.wpm; + } + #endif + // End of Winkey Speed change mod for Rotary Encoder -- VE2EVN + + } // if (step != 0) + +} +#endif //FEATURE_ROTARY_ENCODER +//------------------------------------------------------------------------------------------------------- + +#ifdef FEATURE_SIDETONE_SWITCH +void check_sidetone_switch() +{ + static unsigned long lastcheck = 0 ; + + if ( millis() - lastcheck < 250 ) return ; + + lastcheck = millis() ; + + int ss_read = sidetone_switch_value(); + + if ( (ss_read == HIGH) && ( (configuration.sidetone_mode == SIDETONE_ON) || + (configuration.sidetone_mode == SIDETONE_PADDLE_ONLY) )){ + return ; + } + if ( (ss_read == LOW) && configuration.sidetone_mode == SIDETONE_OFF ){ + return ; + } + config_dirty = 1; + #ifdef FEATURE_SLEEP + last_activity_time = millis(); + #endif //FEATURE_SLEEP + #ifdef FEATURE_LCD_BACKLIGHT_AUTO_DIM + last_active_time = millis(); + #endif //FEATURE_LCD_BACKLIGHT_AUTO_DIM + + if ( ss_read == HIGH ) { + configuration.sidetone_mode = SIDETONE_ON; + return ; + } + if ( ss_read == LOW ){ + configuration.sidetone_mode = SIDETONE_OFF; + return ; + } +} + +int sidetone_switch_value() +{ + return digitalRead(SIDETONE_SWITCH); +} + +#endif + + +//------------------------------------------------------------------------------------------------------- + + +#ifdef FEATURE_POTENTIOMETER +void check_potentiometer() +{ + #ifdef DEBUG_LOOP + debug_serial_port->println(F("loop: entering check_potentiometer")); + #endif + + static unsigned long last_pot_check_time = 0; + + if ((configuration.pot_activated || potentiometer_always_on) && ((millis() - last_pot_check_time) > potentiometer_check_interval_ms)) { + last_pot_check_time = millis(); + if ((potentiometer_enable_pin) && (digitalRead(potentiometer_enable_pin) == HIGH)){ + return; + } + byte pot_value_wpm_read = pot_value_wpm(); + if (((abs(pot_value_wpm_read - last_pot_wpm_read) * 10) > (potentiometer_change_threshold * 10))) { + #ifdef DEBUG_POTENTIOMETER + debug_serial_port->print(F("check_potentiometer: speed change: ")); + debug_serial_port->print(pot_value_wpm_read); + debug_serial_port->print(F(" analog read: ")); + debug_serial_port->println(analogRead(potentiometer)); + #endif + if (keyer_machine_mode == KEYER_COMMAND_MODE) command_speed_set(pot_value_wpm_read); + else speed_set(pot_value_wpm_read); + last_pot_wpm_read = pot_value_wpm_read; + #ifdef FEATURE_WINKEY_EMULATION + if ((primary_serial_port_mode == SERIAL_WINKEY_EMULATION) && (winkey_host_open)) { + winkey_port_write(((pot_value_wpm_read-pot_wpm_low_value)|128),0); + winkey_last_unbuffered_speed_wpm = configuration.wpm; + } + #endif + #ifdef FEATURE_SLEEP + last_activity_time = millis(); + #endif //FEATURE_SLEEP + #ifdef FEATURE_LCD_BACKLIGHT_AUTO_DIM + last_active_time = millis(); + #endif //FEATURE_LCD_BACKLIGHT_AUTO_DIM + } + } +} + +#endif +//------------------------------------------------------------------------------------------------------- +#ifdef FEATURE_POTENTIOMETER +byte pot_value_wpm() +{ + // int pot_read = analogRead(potentiometer); + // byte return_value = map(pot_read, 0, pot_full_scale_reading, pot_wpm_low_value, pot_wpm_high_value); + // return return_value; + + + static int last_pot_read = 0; + static byte return_value = 0; + int pot_read = analogRead(potentiometer); + if (abs(pot_read - last_pot_read) > potentiometer_reading_threshold ) { + return_value = map(pot_read, 0, pot_full_scale_reading, pot_wpm_low_value, pot_wpm_high_value); + last_pot_read = pot_read; + } + return return_value; + +} + +#endif + +//------------------------------------------------------------------------------------------------------- + +#ifdef FEATURE_HELL +void hell_test () +{ + for (byte h = 65; h < 91; h++) { + transmit_hell_char(h); + } + transmit_hell_char('0'); + transmit_hell_char('1'); + transmit_hell_char('2'); + transmit_hell_char('3'); + transmit_hell_char('4'); + transmit_hell_char('5'); + transmit_hell_char('6'); + transmit_hell_char('7'); + transmit_hell_char('8'); + transmit_hell_char('9'); + transmit_hell_char('+'); + transmit_hell_char('-'); + transmit_hell_char('?'); + transmit_hell_char('/'); + transmit_hell_char('.'); + transmit_hell_char(','); + transmit_hell_char('!');//sp5iou +// transmit_hell_char('‘'); // this causes compiler warning; unicode character or something? + transmit_hell_char('='); + transmit_hell_char(')'); + transmit_hell_char('('); + transmit_hell_char(':'); +} +#endif + +//------------------------------------------------------------------------------------------------------- + +#ifdef FEATURE_HELL +void transmit_hell_char (byte hellchar) +{ + + // blank column + for (byte w = 0; w < 14; w++) { + transmit_hell_pixel(0); + } + + if ((hellchar > 64) && (hellchar < 91)) { // A - Z + hellchar = ((hellchar - 65) * 9); + transmit_hell_pixels(hell_font1, hellchar); + } else { + if ((hellchar > 47) && (hellchar < 58)) { // 0 - 9 + hellchar = ((hellchar - 48) * 9); + transmit_hell_pixels(hell_font2, hellchar); + } else { + switch (hellchar) { + case '+': hellchar = 0; break; + case '-': hellchar = 1; break; + case '?': hellchar = 2; break; + case '/': hellchar = 3; break; + case '.': hellchar = 4; break; + case ',': hellchar = 5; break; +// case '‘': hellchar = 6; break; // this causes compiler warning; unicode character or something? + case '=': hellchar = 7; break; + case ')': hellchar = 8; break; + case '(': hellchar = 9; break; + case ':': hellchar = 10; break; + default : hellchar = 11; break; + } + hellchar = hellchar * 9; + transmit_hell_pixels(hell_font3, hellchar); + + } + } + + // blank column + for (byte w = 0; w < 14; w++) { + transmit_hell_pixel(0); + } + +} +#endif + +//------------------------------------------------------------------------------------------------------- + +#ifdef FEATURE_HELL +void transmit_hell_pixels (const char* hell_pixels, byte hellchar) +//void transmit_hell_pixels (prog_uchar* hell_pixels, byte hellchar) +{ + + for (byte x = 0; x < 9; x++) { + for (int y = 7; y > -1; y--) { + if ((x < 8) || ((x == 8) && (y > 1))) { // drop the last 2 bits in byte 9 + if (bitRead(pgm_read_byte(hell_pixels + hellchar + x ),y)) { + transmit_hell_pixel(1); + } else { + transmit_hell_pixel(0); + } + } + } + } + +} +#endif + +//------------------------------------------------------------------------------------------------------- + +#ifdef FEATURE_HELL +void transmit_hell_pixel (byte hellbit) +{ + sending_mode = AUTOMATIC_SENDING; + if (hellbit) { + tx_and_sidetone_key(1); + } else { + tx_and_sidetone_key(0); + } + delayMicroseconds(hell_pixel_microseconds); +} +#endif + +//------------------------------------------------------------------------------------------------------- + +#ifdef FEATURE_MEMORIES +void put_memory_button_in_buffer(byte memory_number_to_put_in_buffer) +{ + + if (memory_number_to_put_in_buffer < number_of_memories) { + #ifdef DEBUG_MEMORIES + debug_serial_port->print(F("put_memory_button_in_buffer: memory_number_to_put_in_buffer:")); + debug_serial_port->println(memory_number_to_put_in_buffer,DEC); + #endif + repeat_memory = 255; + if ((millis() - last_memory_button_buffer_insert) > 400) { // don't do another buffer insert if we just did one - button debounce + #ifdef FEATURE_WINKEY_EMULATION + if (winkey_sending && winkey_host_open) { + winkey_port_write(0xc0|winkey_sending|winkey_xoff,0); + winkey_interrupted = 1; + } + #endif + + add_to_send_buffer(SERIAL_SEND_BUFFER_MEMORY_NUMBER); + add_to_send_buffer(memory_number_to_put_in_buffer); + last_memory_button_buffer_insert = millis(); + } + } else { + #ifdef DEBUG_MEMORIES + debug_serial_port->println(F("put_memory_button_in_buffer: bad memory_number_to_put_in_buffer")); + #endif + } +} +#endif + +//------------------------------------------------------------------------------------------------------- + +void check_paddles() +{ + + #ifdef DEBUG_LOOP + debug_serial_port->println(F("loop: entering check_paddles")); + #endif + + #define NO_CLOSURE 0 + #define DIT_CLOSURE_DAH_OFF 1 + #define DAH_CLOSURE_DIT_OFF 2 + #define DIT_CLOSURE_DAH_ON 3 + #define DAH_CLOSURE_DIT_ON 4 + + if (keyer_machine_mode == BEACON){return;} + + static byte last_closure = NO_CLOSURE; + + + check_dit_paddle(); + check_dah_paddle(); + + #ifdef FEATURE_WINKEY_EMULATION + if (winkey_dit_invoke) { + dit_buffer = 1; + } + if (winkey_dah_invoke) { + dah_buffer = 1; + } + #endif //FEATURE_WINKEY_EMULATION + #ifndef OPTION_NO_ULTIMATIC + if (configuration.keyer_mode == ULTIMATIC) { + if (ultimatic_mode == ULTIMATIC_NORMAL) { + + switch (last_closure) { + case DIT_CLOSURE_DAH_OFF: + if (dah_buffer) { + if (dit_buffer) { + last_closure = DAH_CLOSURE_DIT_ON; + dit_buffer = 0; + } else { + last_closure = DAH_CLOSURE_DIT_OFF; + } + } else { + if (!dit_buffer) { + last_closure = NO_CLOSURE; + } + } + break; + case DIT_CLOSURE_DAH_ON: + if (dit_buffer) { + if (dah_buffer) { + dah_buffer = 0; + } else { + last_closure = DIT_CLOSURE_DAH_OFF; + } + } else { + if (dah_buffer) { + last_closure = DAH_CLOSURE_DIT_OFF; + } else { + last_closure = NO_CLOSURE; + } + } + break; + + case DAH_CLOSURE_DIT_OFF: + if (dit_buffer) { + if (dah_buffer) { + last_closure = DIT_CLOSURE_DAH_ON; + dah_buffer = 0; + } else { + last_closure = DIT_CLOSURE_DAH_OFF; + } + } else { + if (!dah_buffer) { + last_closure = NO_CLOSURE; + } + } + break; + + case DAH_CLOSURE_DIT_ON: + if (dah_buffer) { + if (dit_buffer) { + dit_buffer = 0; + } else { + last_closure = DAH_CLOSURE_DIT_OFF; + } + } else { + if (dit_buffer) { + last_closure = DIT_CLOSURE_DAH_OFF; + } else { + last_closure = NO_CLOSURE; + } + } + break; + + case NO_CLOSURE: + if ((dit_buffer) && (!dah_buffer)) { + last_closure = DIT_CLOSURE_DAH_OFF; + } else { + if ((dah_buffer) && (!dit_buffer)) { + last_closure = DAH_CLOSURE_DIT_OFF; + } else { + if ((dit_buffer) && (dah_buffer)) { + // need to handle dit/dah priority here + last_closure = DIT_CLOSURE_DAH_ON; + dah_buffer = 0; + } + } + } + break; + } + } else { // if (ultimatic_mode == ULTIMATIC_NORMAL) + if ((dit_buffer) && (dah_buffer)) { // dit or dah priority mode + if (ultimatic_mode == ULTIMATIC_DIT_PRIORITY) { + dah_buffer = 0; + } else { + dit_buffer = 0; + } + } + } // if (ultimatic_mode == ULTIMATIC_NORMAL) + } // if (configuration.keyer_mode == ULTIMATIC) + #endif // OPTION_NO_ULTIMATIC + + if (configuration.keyer_mode == SINGLE_PADDLE){ + switch (last_closure) { + case DIT_CLOSURE_DAH_OFF: + if (dit_buffer) { + if (dah_buffer) { + dah_buffer = 0; + } else { + last_closure = DIT_CLOSURE_DAH_OFF; + } + } else { + if (dah_buffer) { + last_closure = DAH_CLOSURE_DIT_OFF; + } else { + last_closure = NO_CLOSURE; + } + } + break; + + case DIT_CLOSURE_DAH_ON: + + if (dah_buffer) { + if (dit_buffer) { + last_closure = DAH_CLOSURE_DIT_ON; + dit_buffer = 0; + } else { + last_closure = DAH_CLOSURE_DIT_OFF; + } + } else { + if (!dit_buffer) { + last_closure = NO_CLOSURE; + } + } + break; + + + + case DAH_CLOSURE_DIT_OFF: + if (dah_buffer) { + if (dit_buffer) { + dit_buffer = 0; + } else { + last_closure = DAH_CLOSURE_DIT_OFF; + } + } else { + if (dit_buffer) { + last_closure = DIT_CLOSURE_DAH_OFF; + } else { + last_closure = NO_CLOSURE; + } + } + break; + + case DAH_CLOSURE_DIT_ON: + if (dit_buffer) { + if (dah_buffer) { + last_closure = DIT_CLOSURE_DAH_ON; + dah_buffer = 0; + } else { + last_closure = DIT_CLOSURE_DAH_OFF; + } + } else { + if (!dah_buffer) { + last_closure = NO_CLOSURE; + } + } + break; + + case NO_CLOSURE: + if ((dit_buffer) && (!dah_buffer)) { + last_closure = DIT_CLOSURE_DAH_OFF; + } else { + if ((dah_buffer) && (!dit_buffer)) { + last_closure = DAH_CLOSURE_DIT_OFF; + } else { + if ((dit_buffer) && (dah_buffer)) { + // need to handle dit/dah priority here + last_closure = DIT_CLOSURE_DAH_ON; + dah_buffer = 0; + } + } + } + break; + } + } //if (configuration.keyer_mode == SINGLE_PADDLE) + + service_tx_inhibit_and_pause(); + +} + +//------------------------------------------------------------------------------------------------------- + +void ptt_key(){ + + unsigned long ptt_activation_time = millis(); + byte all_delays_satisfied = 0; + + #ifdef FEATURE_SEQUENCER + byte sequencer_1_ok = 0; + byte sequencer_2_ok = 0; + byte sequencer_3_ok = 0; + byte sequencer_4_ok = 0; + byte sequencer_5_ok = 0; + #endif + + if (configuration.ptt_disabled){return;} + + if (ptt_line_activated == 0) { // if PTT is currently deactivated, bring it up and insert PTT lead time delay + #ifdef FEATURE_SO2R_BASE + if (current_tx_ptt_line && (configuration.ptt_buffer_hold_active || manual_ptt_invoke_ptt_input_pin)) { + + #if defined(FEATURE_WINKEY_EMULATION) && defined(OPTION_WINKEY_PINCONFIG_PTT_CONTROLS_PTT_LINE) + if (winkey_pinconfig_ptt_bit){ + digitalWrite (configuration.current_ptt_line, ptt_line_active_state); + } + #else + digitalWrite (configuration.current_ptt_line, ptt_line_active_state); + #endif // defined(FEATURE_WINKEY_EMULATION) && !defined(OPTION_WINKEY_PINCONFIG_PTT_CONTROLS_PTT_HOLD) + + + //digitalWrite (current_tx_ptt_line, ptt_line_active_state); + + //delay(configuration.ptt_lead_time[configuration.current_tx-1]); + #ifdef FEATURE_SEQUENCER + sequencer_ptt_inactive_time = 0; + #endif + } + #else + if (configuration.current_ptt_line) { + + #if defined(FEATURE_WINKEY_EMULATION) && defined(OPTION_WINKEY_PINCONFIG_PTT_CONTROLS_PTT_LINE) + if (winkey_pinconfig_ptt_bit){ + digitalWrite (configuration.current_ptt_line, ptt_line_active_state); + } + #else + digitalWrite (configuration.current_ptt_line, ptt_line_active_state); + #endif // defined(FEATURE_WINKEY_EMULATION) && !defined(OPTION_WINKEY_PINCONFIG_PTT_CONTROLS_PTT_HOLD) + + //digitalWrite (configuration.current_ptt_line, ptt_line_active_state); + + + + #if defined(OPTION_WINKEY_2_SUPPORT) && defined(FEATURE_WINKEY_EMULATION) + if ((wk2_both_tx_activated) && (ptt_tx_2)) { + digitalWrite (ptt_tx_2, ptt_line_active_state); + } + #endif + //delay(configuration.ptt_lead_time[configuration.current_tx-1]); + #ifdef FEATURE_SEQUENCER + sequencer_ptt_inactive_time = 0; + #endif + } + #endif //FEATURE_SO2R_BASE + + ptt_line_activated = 1; + + #ifdef FEATURE_SO2R_BASE + so2r_set_rx(); + #endif + + while (!all_delays_satisfied){ + #ifdef FEATURE_SEQUENCER + if (sequencer_1_pin){ + if (((millis() - ptt_activation_time) >= configuration.ptt_active_to_sequencer_active_time[0]) || sequencer_1_active){ + digitalWrite(sequencer_1_pin,sequencer_pins_active_state); + sequencer_1_ok = 1; + sequencer_1_active = 1; + } + } else { + sequencer_1_ok = 1; + } + if (sequencer_2_pin){ + if (((millis() - ptt_activation_time) >= configuration.ptt_active_to_sequencer_active_time[1]) || sequencer_2_active){ + digitalWrite(sequencer_2_pin,sequencer_pins_active_state); + sequencer_2_ok = 1; + sequencer_2_active = 1; + } + } else { + sequencer_2_ok = 1; + } + if (sequencer_3_pin){ + if (((millis() - ptt_activation_time) >= configuration.ptt_active_to_sequencer_active_time[2]) || sequencer_3_active){ + digitalWrite(sequencer_3_pin,sequencer_pins_active_state); + sequencer_3_ok = 1; + sequencer_3_active = 1; + } + } else { + sequencer_3_ok = 1; + } + if (sequencer_4_pin){ + if (((millis() - ptt_activation_time) >= configuration.ptt_active_to_sequencer_active_time[3]) || sequencer_4_active){ + digitalWrite(sequencer_4_pin,sequencer_pins_active_state); + sequencer_4_ok = 1; + sequencer_4_active = 1; + } + } else { + sequencer_4_ok = 1; + } + if (sequencer_5_pin){ + if (((millis() - ptt_activation_time) >= configuration.ptt_active_to_sequencer_active_time[4]) || sequencer_5_active){ + digitalWrite(sequencer_5_pin,sequencer_pins_active_state); + sequencer_5_ok = 1; + sequencer_5_active = 1; + } + } else { + sequencer_5_ok = 1; + } + + if (((millis() - ptt_activation_time) >= configuration.ptt_lead_time[configuration.current_tx-1]) && sequencer_1_ok && sequencer_2_ok && sequencer_3_ok && sequencer_4_ok && sequencer_5_ok){ + all_delays_satisfied = 1; + } + + #else //FEATURE_SEQUENCER + #if defined(FEATURE_WINKEY_EMULATION) && !defined(OPTION_WINKEY_PINCONFIG_PTT_CONTROLS_PTT_HOLD) + if (((millis() - ptt_activation_time) >= configuration.ptt_lead_time[configuration.current_tx-1]) || (winkey_host_open && !winkey_pinconfig_ptt_bit) ) { + all_delays_satisfied = 1; + } + #else + if ((millis() - ptt_activation_time) >= configuration.ptt_lead_time[configuration.current_tx-1]){ + all_delays_satisfied = 1; + } + #endif + #endif //FEATURE_SEQUENCER + + } //while (!all_delays_satisfied) + + } + ptt_time = millis(); +} +//------------------------------------------------------------------------------------------------------- +#ifdef FEATURE_SEQUENCER +void check_sequencer_tail_time(){ + + if (sequencer_ptt_inactive_time){ + if (sequencer_1_pin){ + if (sequencer_1_active && ((millis() - sequencer_ptt_inactive_time) >= configuration.ptt_inactive_to_sequencer_inactive_time[0])){ + digitalWrite(sequencer_1_pin,sequencer_pins_inactive_state); + sequencer_1_active = 0; + } + } + if (sequencer_2_pin){ + if (sequencer_2_active && ((millis() - sequencer_ptt_inactive_time) >= configuration.ptt_inactive_to_sequencer_inactive_time[1])){ + digitalWrite(sequencer_2_pin,sequencer_pins_inactive_state); + sequencer_2_active = 0; + } + } + if (sequencer_3_pin){ + if (sequencer_3_active && ((millis() - sequencer_ptt_inactive_time) >= configuration.ptt_inactive_to_sequencer_inactive_time[2])){ + digitalWrite(sequencer_3_pin,sequencer_pins_inactive_state); + sequencer_3_active = 0; + } + } + if (sequencer_4_pin){ + if (sequencer_4_active && ((millis() - sequencer_ptt_inactive_time) >= configuration.ptt_inactive_to_sequencer_inactive_time[3])){ + digitalWrite(sequencer_4_pin,sequencer_pins_inactive_state); + sequencer_4_active = 0; + } + } + if (sequencer_5_pin){ + if (sequencer_5_active && ((millis() - sequencer_ptt_inactive_time) >= configuration.ptt_inactive_to_sequencer_inactive_time[4])){ + digitalWrite(sequencer_5_pin,sequencer_pins_inactive_state); + sequencer_5_active = 0; + } + } + } + + if (!sequencer_1_active && !sequencer_2_active && !sequencer_3_active && !sequencer_4_active && !sequencer_5_active){ + sequencer_ptt_inactive_time = 0; + } + + +} +#endif //FEATURE_SEQUENCER + +//------------------------------------------------------------------------------------------------------- +void ptt_unkey(){ + + if (ptt_line_activated) { + + #ifdef FEATURE_SO2R_BASE + if (current_tx_ptt_line) { + digitalWrite (current_tx_ptt_line, ptt_line_inactive_state); + #else + if (configuration.current_ptt_line) { + digitalWrite (configuration.current_ptt_line, ptt_line_inactive_state); + #if defined(OPTION_WINKEY_2_SUPPORT) && defined(FEATURE_WINKEY_EMULATION) + if ((wk2_both_tx_activated) && (ptt_tx_2)) { + digitalWrite (ptt_tx_2, ptt_line_inactive_state); + } + #endif + #endif //FEATURE_SO2R_BASE + } + ptt_line_activated = 0; + #ifdef FEATURE_SEQUENCER + sequencer_ptt_inactive_time = millis(); + #endif + + #ifdef FEATURE_SO2R_BASE + if (so2r_pending_tx) { + so2r_tx = so2r_pending_tx; + so2r_pending_tx = 0; + so2r_set_tx(); + } + + so2r_set_rx(); + #endif //FEATURE_SO2R_BASE + + } +} + +//------------------------------------------------------------------------------------------------------- +void check_ptt_tail() +{ + #ifdef DEBUG_LOOP + debug_serial_port->println(F("loop: entering check_ptt_tail")); + #endif + + #ifdef FEATURE_SO2R_BASE + if (so2r_ptt) { + return; + } + #endif + + //static byte manual_ptt_invoke_ptt_input_pin = 0; + + if (ptt_input_pin){ + if ((digitalRead(ptt_input_pin) == ptt_input_pin_active_state)){ + if (!manual_ptt_invoke){ + manual_ptt_invoke = 1; + manual_ptt_invoke_ptt_input_pin = 1; + ptt_key(); + return; + } + } else { + if ((manual_ptt_invoke) && (manual_ptt_invoke_ptt_input_pin)){ + manual_ptt_invoke = 0; + manual_ptt_invoke_ptt_input_pin = 0; + if (!key_state){ + ptt_unkey(); + } + } + } + } + + + #if !defined(FEATURE_WINKEY_EMULATION) + if (key_state) { + ptt_time = millis(); + } else { + if ((ptt_line_activated) && (manual_ptt_invoke == 0)) { + //if ((millis() - ptt_time) > ptt_tail_time) { + if (last_sending_mode == MANUAL_SENDING) { + #ifndef OPTION_INCLUDE_PTT_TAIL_FOR_MANUAL_SENDING + + // PTT Tail Time: N PTT Hang Time: Y + + if ((millis() - ptt_time) >= ((configuration.length_wordspace*ptt_hang_time_wordspace_units)*float(1200/configuration.wpm)) ) { + ptt_unkey(); + } + #else //ndef OPTION_INCLUDE_PTT_TAIL_FOR_MANUAL_SENDING + #ifndef OPTION_EXCLUDE_PTT_HANG_TIME_FOR_MANUAL_SENDING + + // PTT Tail Time: Y PTT Hang Time: Y + + if ((millis() - ptt_time) >= (((configuration.length_wordspace*ptt_hang_time_wordspace_units)*float(1200/configuration.wpm))+configuration.ptt_tail_time[configuration.current_tx-1])) { + ptt_unkey(); + } + #else //OPTION_EXCLUDE_PTT_HANG_TIME_FOR_MANUAL_SENDING + if ((millis() - ptt_time) >= configuration.ptt_tail_time[configuration.current_tx-1]) { + + + // PTT Tail Time: Y PTT Hang Time: N + + ptt_unkey(); + } + #endif //OPTION_EXCLUDE_PTT_HANG_TIME_FOR_MANUAL_SENDING + #endif //ndef OPTION_INCLUDE_PTT_TAIL_FOR_MANUAL_SENDING + } else { // automatic sending + if (((millis() - ptt_time) > configuration.ptt_tail_time[configuration.current_tx-1]) && ( !configuration.ptt_buffer_hold_active || ((!send_buffer_bytes) && configuration.ptt_buffer_hold_active) || (pause_sending_buffer))){ + ptt_unkey(); + } + } + } + } + #else //FEATURE_WINKEY_EMULATION + + if (key_state) { + ptt_time = millis(); + } else { + if ((ptt_line_activated) && (manual_ptt_invoke == 0)) { + //if ((millis() - ptt_time) > ptt_tail_time) { + if (last_sending_mode == MANUAL_SENDING) { + #ifndef OPTION_INCLUDE_PTT_TAIL_FOR_MANUAL_SENDING + + // PTT Tail Time: N PTT Hang Time: Y + + if ((millis() - ptt_time) >= ((configuration.length_wordspace*ptt_hang_time_wordspace_units)*float(1200/configuration.wpm)) ) { + ptt_unkey(); + } + #else //ndef OPTION_INCLUDE_PTT_TAIL_FOR_MANUAL_SENDING + #ifndef OPTION_EXCLUDE_PTT_HANG_TIME_FOR_MANUAL_SENDING + + // PTT Tail Time: Y PTT Hang Time: Y + + if (winkey_host_open){ + if ((millis() - ptt_time) >= (((configuration.length_wordspace*ptt_hang_time_wordspace_units)*float(1200/configuration.wpm))+ (int(winkey_session_ptt_tail) * 10) + (3 * (1200/configuration.wpm)) )) { + ptt_unkey(); + } + } else { + if ((millis() - ptt_time) >= (((configuration.length_wordspace*ptt_hang_time_wordspace_units)*float(1200/configuration.wpm))+configuration.ptt_tail_time[configuration.current_tx-1])) { + ptt_unkey(); + } + } + + + + #else //OPTION_EXCLUDE_PTT_HANG_TIME_FOR_MANUAL_SENDING + if (winkey_host_open){ + if ((millis() - ptt_time) >= ((int(winkey_session_ptt_tail) * 10) + (3 * (1200/configuration.wpm)))) { + + // PTT Tail Time: Y PTT Hang Time: N + + ptt_unkey(); + } + } else { + if ((millis() - ptt_time) >= configuration.ptt_tail_time[configuration.current_tx-1]) { + + + // PTT Tail Time: Y PTT Hang Time: N + + ptt_unkey(); + } + } + + + + + + #endif //OPTION_EXCLUDE_PTT_HANG_TIME_FOR_MANUAL_SENDING + #endif //ndef OPTION_INCLUDE_PTT_TAIL_FOR_MANUAL_SENDING + } else { // automatic sending + if (winkey_host_open){ + if (((millis() - ptt_time) > ((int(winkey_session_ptt_tail) * 10) + (3 * (1200/configuration.wpm)))) && ( !configuration.ptt_buffer_hold_active || ((!send_buffer_bytes) && configuration.ptt_buffer_hold_active) || (pause_sending_buffer))) { + ptt_unkey(); + } + } else { + if (((millis() - ptt_time) > configuration.ptt_tail_time[configuration.current_tx-1]) && ( !configuration.ptt_buffer_hold_active || ((!send_buffer_bytes) && configuration.ptt_buffer_hold_active) || (pause_sending_buffer))){ + ptt_unkey(); + } + } + } + } + } + #endif //FEATURE_WINKEY_EMULATION + +} + +//------------------------------------------------------------------------------------------------------- +void write_settings_to_eeprom(int initialize_eeprom) { + + #if !defined(ARDUINO_SAM_DUE) || (defined(ARDUINO_SAM_DUE) && defined(FEATURE_EEPROM_E24C1024)) + + if (initialize_eeprom) { + //configuration.magic_number = eeprom_magic_number; + EEPROM.write(0,eeprom_magic_number); + #ifdef FEATURE_MEMORIES + initialize_eeprom_memories(); + #endif //FEATURE_MEMORIES + const byte* p = (const byte*)(const void*)&configuration; + unsigned int i; + int ee = 1; // starting point of configuration struct + for (i = 0; i < sizeof(configuration); i++){ + EEPROM.write(ee++, *p++); + } + } else { + + async_eeprom_write = 1; // initiate an asyncrhonous eeprom write + + } + + #endif //!defined(ARDUINO_SAM_DUE) || (defined(ARDUINO_SAM_DUE) && defined(FEATURE_EEPROM_E24C1024)) + + config_dirty = 0; + +} + +//------------------------------------------------------------------------------------------------------- + +void service_async_eeprom_write(){ + + // This writes one byte out to EEPROM each time it is called + + static byte last_async_eeprom_write_status = 0; + static int ee = 0; + static unsigned int i = 0; + static const byte* p; + + if ((async_eeprom_write) && (!send_buffer_bytes) && (!ptt_line_activated) && (!dit_buffer) && (!dah_buffer) && (paddle_pin_read(paddle_left) == HIGH) && (paddle_pin_read(paddle_right) == HIGH)) { + if (last_async_eeprom_write_status){ // we have an ansynchronous write to eeprom in progress + + + #if defined(_BOARD_PIC32_PINGUINO_) || defined(ARDUINO_SAMD_VARIANT_COMPLIANCE) + if (EEPROM.read(ee) != *p) { + EEPROM.write(ee, *p); + } + ee++; + p++; + #else + EEPROM.update(ee++, *p++); + #endif + + if (i < sizeof(configuration)){ + #if defined(DEBUG_ASYNC_EEPROM_WRITE) + debug_serial_port->print(F("service_async_eeprom_write: write: ")); + debug_serial_port->println(i); + #endif + i++; + } else { // we're done + async_eeprom_write = 0; + last_async_eeprom_write_status = 0; + #if defined(ARDUINO_SAMD_VARIANT_COMPLIANCE) + EEPROM.commit(); + #endif + + #if defined(DEBUG_ASYNC_EEPROM_WRITE) + debug_serial_port->println(F("service_async_eeprom_write: complete")); + #endif + } + + } else { // we don't have one in progress - initialize things + + p = (const byte*)(const void*)&configuration; + ee = 1; // starting point of configuration struct + i = 0; + last_async_eeprom_write_status = 1; + #if defined(DEBUG_ASYNC_EEPROM_WRITE) + debug_serial_port->println(F("service_async_eeprom_write: init")); + #endif + } + } + +} + +//------------------------------------------------------------------------------------------------------- + +int read_settings_from_eeprom() { + + // returns 0 if eeprom had valid settings, returns 1 if eeprom needs initialized + + #if defined(DEBUG_FORCE_RESET) + return 1; + #endif + + + + #if !defined(ARDUINO_SAM_DUE) || (defined(ARDUINO_SAM_DUE) && defined(FEATURE_EEPROM_E24C1024)) + + #if defined(DEBUG_EEPROM_READ_SETTINGS) + debug_serial_port->println(F("read_settings_from_eeprom: start")); + #endif + + if (EEPROM.read(0) == eeprom_magic_number){ + + byte* p = (byte*)(void*)&configuration; + unsigned int i; + int ee = 1; // starting point of configuration struct + for (i = 0; i < sizeof(configuration); i++){ + #if defined(DEBUG_EEPROM_READ_SETTINGS) + debug_serial_port->print(F("read_settings_from_eeprom: read: i:")); + debug_serial_port->print(i); + debug_serial_port->print(F(":")); + debug_serial_port->print(EEPROM.read(ee)); + debug_serial_port->println(); + #endif + *p++ = EEPROM.read(ee++); + } + + #ifndef FEATURE_SO2R_BASE + switch_to_tx_silent(configuration.current_tx); + #endif + + config_dirty = 0; + + #if defined(DEBUG_EEPROM_READ_SETTINGS) + debug_serial_port->println(F("read_settings_from_eeprom: read complete")); + #endif + return 0; + } else { + #if defined(DEBUG_EEPROM_READ_SETTINGS) + debug_serial_port->println(F("read_settings_from_eeprom: eeprom needs initialized")); + #endif + return 1; + } + + #endif //!defined(ARDUINO_SAM_DUE) || (defined(ARDUINO_SAM_DUE) && defined(FEATURE_EEPROM_E24C1024)) + + #if defined(DEBUG_EEPROM_READ_SETTINGS) + debug_serial_port->println(F("read_settings_from_eeprom: bypassed read - no eeprom")); + #endif + + return 1; + +} + +//------------------------------------------------------------------------------------------------------- + +void check_dit_paddle() +{ + + + + byte pin_value = 0; + byte dit_paddle = 0; + #ifdef OPTION_DIT_PADDLE_NO_SEND_ON_MEM_RPT + static byte memory_rpt_interrupt_flag = 0; + #endif + + if (configuration.paddle_mode == PADDLE_NORMAL) { + dit_paddle = paddle_left; + } else { + dit_paddle = paddle_right; + } + + pin_value = paddle_pin_read(dit_paddle); + + + #if defined(FEATURE_USB_MOUSE) || defined(FEATURE_USB_KEYBOARD) + if (usb_dit) {pin_value = 0;} + #endif + + #ifdef OPTION_DIT_PADDLE_NO_SEND_ON_MEM_RPT + if (pin_value && memory_rpt_interrupt_flag) { + memory_rpt_interrupt_flag = 0; + sending_mode = MANUAL_SENDING; + loop_element_lengths(3,0,configuration.wpm); + dit_buffer = 0; + } + #endif + + #ifdef OPTION_DIT_PADDLE_NO_SEND_ON_MEM_RPT + if ((pin_value == 0) && (memory_rpt_interrupt_flag == 0)) { + #else + if (pin_value == 0) { + #endif + #ifdef FEATURE_DEAD_OP_WATCHDOG + if (dit_buffer == 0) { + dit_counter++; + dah_counter = 0; + } + #endif + dit_buffer = 1; + + #if defined(OPTION_WINKEY_SEND_BREAKIN_STATUS_BYTE) && defined(FEATURE_WINKEY_EMULATION) + if (!winkey_interrupted && winkey_host_open && !winkey_breakin_status_byte_inhibit){ + send_winkey_breakin_byte_flag = 1; + // winkey_port_write(0xc2|winkey_sending|winkey_xoff); // 0xc2 - BREAKIN bit set high + // winkey_interrupted = 1; + + // tone(sidetone_line,1000); + // delay(500); + // noTone(sidetone_line); + + dit_buffer = 0; + } + #endif //defined(OPTION_WINKEY_SEND_BREAKIN_STATUS_BYTE) && defined(FEATURE_WINKEY_EMULATION) + + + #ifdef FEATURE_SLEEP + last_activity_time = millis(); + #endif //FEATURE_SLEEP + #ifdef FEATURE_LCD_BACKLIGHT_AUTO_DIM + last_active_time = millis(); + #endif //FEATURE_LCD_BACKLIGHT_AUTO_DIM + manual_ptt_invoke = 0; + #ifdef FEATURE_MEMORIES + if (repeat_memory < 255) { + repeat_memory = 255; + #ifdef OPTION_DIT_PADDLE_NO_SEND_ON_MEM_RPT + dit_buffer = 0; + while (!paddle_pin_read(dit_paddle)) {}; + memory_rpt_interrupt_flag = 1; + #endif + } + #endif + clear_send_buffer(); + } + + + +} + +//------------------------------------------------------------------------------------------------------- + +void check_dah_paddle() +{ + + + + byte pin_value = 0; + byte dah_paddle; + + if (configuration.paddle_mode == PADDLE_NORMAL) { + dah_paddle = paddle_right; + } else { + dah_paddle = paddle_left; + } + + pin_value = paddle_pin_read(dah_paddle); + + #if defined(FEATURE_USB_MOUSE) || defined(FEATURE_USB_KEYBOARD) + if (usb_dah) {pin_value = 0;} + #endif + + if (pin_value == 0) { + #ifdef FEATURE_DEAD_OP_WATCHDOG + if (dah_buffer == 0) { + dah_counter++; + dit_counter = 0; + } + #endif + dah_buffer = 1; + + #if defined(OPTION_WINKEY_SEND_BREAKIN_STATUS_BYTE) && defined(FEATURE_WINKEY_EMULATION) + if (!winkey_interrupted && winkey_host_open && !winkey_breakin_status_byte_inhibit){ + send_winkey_breakin_byte_flag = 1; + dah_buffer = 0; + } + #endif //defined(OPTION_WINKEY_SEND_BREAKIN_STATUS_BYTE) && defined(FEATURE_WINKEY_EMULATION) + + + #ifdef FEATURE_SLEEP + last_activity_time = millis(); + #endif //FEATURE_SLEEP + #ifdef FEATURE_LCD_BACKLIGHT_AUTO_DIM + last_active_time = millis(); + #endif //FEATURE_LCD_BACKLIGHT_AUTO_DIM + #ifdef FEATURE_MEMORIES + repeat_memory = 255; + #endif + clear_send_buffer(); + manual_ptt_invoke = 0; + } + + + +} + +//------------------------------------------------------------------------------------------------------- + +void send_dit(){ + + // notes: key_compensation is a straight x mS lengthening or shortening of the key down time + // weighting is + + unsigned int character_wpm = configuration.wpm; + + + #ifdef FEATURE_FARNSWORTH + if ((sending_mode == AUTOMATIC_SENDING) && (configuration.wpm_farnsworth > configuration.wpm)) { + character_wpm = configuration.wpm_farnsworth; + #if defined(DEBUG_FARNSWORTH) + debug_serial_port->println(F("send_dit: farns act")); + #endif + } + #if defined(DEBUG_FARNSWORTH) + + else { + debug_serial_port->println(F("send_dit: farns inact")); + } + #endif + #endif //FEATURE_FARNSWORTH + + if (keyer_machine_mode == KEYER_COMMAND_MODE){ + character_wpm = configuration.wpm_command_mode; + } + + being_sent = SENDING_DIT; + tx_and_sidetone_key(1); + #ifdef DEBUG_VARIABLE_DUMP + dit_start_time = millis(); + #endif + if ((tx_key_dit) && (key_tx)) {digitalWrite(tx_key_dit,tx_key_dit_and_dah_pins_active_state);} + + + #ifdef FEATURE_QLF + if (qlf_active){ + loop_element_lengths((1.0*(float(configuration.weighting)/50)*(random(qlf_dit_min,qlf_dit_max)/100.0)),configuration.keying_compensation,character_wpm); + } else { + loop_element_lengths((1.0*(float(configuration.weighting)/50)),configuration.keying_compensation,character_wpm); + } + #else //FEATURE_QLF + loop_element_lengths((1.0*(float(configuration.weighting)/50)),configuration.keying_compensation,character_wpm); + #endif //FEATURE_QLF + + + + if ((tx_key_dit) && (key_tx)) {digitalWrite(tx_key_dit,tx_key_dit_and_dah_pins_inactive_state);} + #ifdef DEBUG_VARIABLE_DUMP + dit_end_time = millis(); + #endif + tx_and_sidetone_key(0); + + + loop_element_lengths((2.0-(float(configuration.weighting)/50)),(-1.0*configuration.keying_compensation),character_wpm); + + #ifdef FEATURE_AUTOSPACE + + byte autospace_end_of_character_flag = 0; + + if ((sending_mode == MANUAL_SENDING) && (configuration.autospace_active)) { + check_paddles(); + } + if ((sending_mode == MANUAL_SENDING) && (configuration.autospace_active) && (dit_buffer == 0) && (dah_buffer == 0)) { + loop_element_lengths((float)configuration.autospace_timing_factor/(float)100,0,configuration.wpm); + autospace_end_of_character_flag = 1; + } + #endif + + #ifdef FEATURE_WINKEY_EMULATION + if ((winkey_host_open) && (winkey_paddle_echo_activated) && (sending_mode == MANUAL_SENDING)) { + winkey_paddle_echo_buffer = (winkey_paddle_echo_buffer * 10) + 1; + //winkey_paddle_echo_buffer_decode_time = millis() + (float(winkey_paddle_echo_buffer_decode_time_factor/float(configuration.wpm))*length_letterspace); + winkey_paddle_echo_buffer_decode_time = millis() + (float(winkey_paddle_echo_buffer_decode_timing_factor*1200.0/float(configuration.wpm))*length_letterspace); + + #ifdef FEATURE_AUTOSPACE + if (autospace_end_of_character_flag){winkey_paddle_echo_buffer_decode_time = 0;} + #endif //FEATURE_AUTOSPACE + } + #endif + + + #ifdef FEATURE_PADDLE_ECHO + if (sending_mode == MANUAL_SENDING) { + paddle_echo_buffer = (paddle_echo_buffer * 10) + 1; + paddle_echo_buffer_decode_time = millis() + (((float)1200.0/(float)configuration.wpm) * ((float)configuration.cw_echo_timing_factor/(float)100)); + + #ifdef FEATURE_AUTOSPACE + if (autospace_end_of_character_flag){paddle_echo_buffer_decode_time = 0;} + #endif //FEATURE_AUTOSPACE + } + #endif //FEATURE_PADDLE_ECHO + + #ifdef FEATURE_AUTOSPACE + autospace_end_of_character_flag = 0; + #endif //FEATURE_AUTOSPACE + + being_sent = SENDING_NOTHING; + last_sending_mode = sending_mode; + + check_paddles(); + +} + +//------------------------------------------------------------------------------------------------------- + +void send_dah(){ + + unsigned int character_wpm = configuration.wpm; + + #ifdef FEATURE_FARNSWORTH + if ((sending_mode == AUTOMATIC_SENDING) && (configuration.wpm_farnsworth > configuration.wpm)) { + character_wpm = configuration.wpm_farnsworth; + } + #endif //FEATURE_FARNSWORTH + + if (keyer_machine_mode == KEYER_COMMAND_MODE){ + character_wpm = configuration.wpm_command_mode; + } + + being_sent = SENDING_DAH; + tx_and_sidetone_key(1); + #ifdef DEBUG_VARIABLE_DUMP + dah_start_time = millis(); + #endif + if ((tx_key_dah) && (key_tx)) {digitalWrite(tx_key_dah,tx_key_dit_and_dah_pins_active_state);} + + #ifdef FEATURE_QLF + if (qlf_active){ + loop_element_lengths((float(configuration.dah_to_dit_ratio/100.0)*(float(configuration.weighting)/50)*(random(qlf_dah_min,qlf_dah_max)/100.0)),configuration.keying_compensation,character_wpm); + } else { + loop_element_lengths((float(configuration.dah_to_dit_ratio/100.0)*(float(configuration.weighting)/50)),configuration.keying_compensation,character_wpm); + } + #else //FEATURE_QLF + loop_element_lengths((float(configuration.dah_to_dit_ratio/100.0)*(float(configuration.weighting)/50)),configuration.keying_compensation,character_wpm); + #endif //FEATURE_QLF + + if ((tx_key_dah) && (key_tx)) {digitalWrite(tx_key_dah,tx_key_dit_and_dah_pins_inactive_state);} + + #ifdef DEBUG_VARIABLE_DUMP + dah_end_time = millis(); + #endif + + tx_and_sidetone_key(0); + + loop_element_lengths((4.0-(3.0*(float(configuration.weighting)/50))),(-1.0*configuration.keying_compensation),character_wpm); + + #ifdef FEATURE_AUTOSPACE + + byte autospace_end_of_character_flag = 0; + + if ((sending_mode == MANUAL_SENDING) && (configuration.autospace_active)) { + check_paddles(); + } + if ((sending_mode == MANUAL_SENDING) && (configuration.autospace_active) && (dit_buffer == 0) && (dah_buffer == 0)) { + loop_element_lengths(2,0,configuration.wpm); + autospace_end_of_character_flag = 1; + } + #endif + + #ifdef FEATURE_WINKEY_EMULATION + if ((winkey_host_open) && (winkey_paddle_echo_activated) && (sending_mode == MANUAL_SENDING)) { + winkey_paddle_echo_buffer = (winkey_paddle_echo_buffer * 10) + 2; + //winkey_paddle_echo_buffer_decode_time = millis() + (float(winkey_paddle_echo_buffer_decode_time_factor/float(configuration.wpm))*length_letterspace); + winkey_paddle_echo_buffer_decode_time = millis() + (float(winkey_paddle_echo_buffer_decode_timing_factor*1200.0/float(configuration.wpm))*length_letterspace); + #ifdef FEATURE_AUTOSPACE + if (autospace_end_of_character_flag){winkey_paddle_echo_buffer_decode_time = 0;} + #endif //FEATURE_AUTOSPACE + } + #endif + + #ifdef FEATURE_PADDLE_ECHO + if (sending_mode == MANUAL_SENDING) { + paddle_echo_buffer = (paddle_echo_buffer * 10) + 2; + paddle_echo_buffer_decode_time = millis() + (((float)1200.0/(float)configuration.wpm) * ((float)configuration.cw_echo_timing_factor/(float)100)); + + #ifdef FEATURE_AUTOSPACE + if (autospace_end_of_character_flag){paddle_echo_buffer_decode_time = 0;} + #endif //FEATURE_AUTOSPACE + } + #endif //FEATURE_PADDLE_ECHO + + #ifdef FEATURE_AUTOSPACE + autospace_end_of_character_flag = 0; + #endif //FEATURE_AUTOSPACE + + check_paddles(); + + being_sent = SENDING_NOTHING; + last_sending_mode = sending_mode; + +} + +/* + + The Dash + + by Linda Ellis + + + I read of a man who stood to speak at a funeral of a friend. He referred to the dates on the tombstone from the beginning...to the end. + + He noted that first came the date of birth and spoke of the following date with tears, but said what mattered most of all was the dash + between those years. + + For that dash represents all the time they spent alive on earth and now only those who loved them know what that little line is worth. + + For it matters not, how much we own, the cars..the house...the cash. + + What matters is how we lived and loved and how we spend our dash. + + So think about this long and hard; are there things you'd like to change? + + For you never know how much time is left that still can be rearranged. + + To be less quick to anger and show appreciation more and love the people in our lives like we've never loved before. + + If we treat each other with respect and more often wear a smile...remembering that this special dash might only last a little while. + + So when your eulogy is being read, with your life's actions to rehash, + + + would you be proud of the things they say about how you lived your dash? + + + +*/ + + +//------------------------------------------------------------------------------------------------------- + +void tx_and_sidetone_key (int state) +{ + + #if defined(FEATURE_COMPETITION_COMPRESSION_DETECTION) + + byte i; + + if ((state == 0) && (key_state) && (compression_detection_key_up_time == 0) && (compression_detection_key_down_time == 0)){ + compression_detection_key_up_time = millis(); + //debug_serial_port->println("UP"); + } + if ((state) && (key_state == 0) && (compression_detection_key_up_time > 0) && (compression_detection_key_down_time == 0)) { + compression_detection_key_down_time = millis(); + //debug_serial_port->println("DOWN"); + } + + unsigned long key_up_to_key_down_time = 0; + + if ((compression_detection_key_down_time != 0) && (compression_detection_key_up_time != 0)){ // do we have a measurement waiting for us? + key_up_to_key_down_time = compression_detection_key_down_time - compression_detection_key_up_time; + #if defined(DEBUG_FEATURE_COMPETITION_COMPRESSION_DETECTION) + // debug_serial_port->print("service_competition_compression_detection: key_up_to_key_down_time:"); + //debug_serial_port->println(key_up_to_key_down_time); + #endif + // is the time within the limits of what would be inter-character time? + if ((key_up_to_key_down_time > ((1200/configuration.wpm)*COMPETITION_COMPRESSION_DETECTION_TIME_INTERCHAR_LOWER_LIMIT)) && (key_up_to_key_down_time < ((1200/configuration.wpm)*COMPETITION_COMPRESSION_DETECTION_TIME_INTERCHAR_UPPER_LIMIT))){ + // add it to the array + if (time_array_index < COMPETITION_COMPRESSION_DETECTION_ARRAY_SIZE){ + + #if defined(DEBUG_FEATURE_COMPETITION_COMPRESSION_DETECTION) + debug_serial_port->print("tx_and_sidetone_key: service_competition_compression_detection: array entry "); + debug_serial_port->print(time_array_index); + debug_serial_port->print(":"); + debug_serial_port->println(key_up_to_key_down_time); + #endif + + time_array[time_array_index] = key_up_to_key_down_time; + time_array_index++; + + } else { // if time array is completely filled up, we do a first in, first out + for(i = 0;i < (COMPETITION_COMPRESSION_DETECTION_ARRAY_SIZE-1);i++){ + time_array[i]=time_array[i+1]; + } + time_array[COMPETITION_COMPRESSION_DETECTION_ARRAY_SIZE-1] = key_up_to_key_down_time; + + + #if defined(DEBUG_FEATURE_COMPETITION_COMPRESSION_DETECTION) + debug_serial_port->print("tx_and_sidetone_key: service_competition_compression_detection: FIFO array entry "); + debug_serial_port->print(time_array_index); + debug_serial_port->print(":"); + debug_serial_port->println(key_up_to_key_down_time); + #endif + + } + + } else { + #if defined(DEBUG_FEATURE_COMPETITION_COMPRESSION_DETECTION) + //debug_serial_port->print("tx_and_sidetone_key: service_competition_compression_detection: discarded entry: "); + //debug_serial_port->println(key_up_to_key_down_time); + #endif + } + compression_detection_key_down_time = 0; + compression_detection_key_up_time = 0; + } + + + + + + #endif //defined(FEATURE_COMPETITION_COMPRESSION_DETECTION) + + + #if !defined(FEATURE_PTT_INTERLOCK) + if ((state) && (key_state == 0)) { + if (key_tx) { + byte previous_ptt_line_activated = ptt_line_activated; + ptt_key(); + if (current_tx_key_line) {digitalWrite (current_tx_key_line, tx_key_line_active_state);} + #if defined(OPTION_WINKEY_2_SUPPORT) && defined(FEATURE_WINKEY_EMULATION) && !defined(FEATURE_SO2R_BASE) + if ((wk2_both_tx_activated) && (tx_key_line_2)) { + digitalWrite (tx_key_line_2, HIGH); + } + #endif + if ((first_extension_time) && (previous_ptt_line_activated == 0)) { + delay(first_extension_time); + } + } + if ((configuration.sidetone_mode == SIDETONE_ON) || (keyer_machine_mode == KEYER_COMMAND_MODE) || ((configuration.sidetone_mode == SIDETONE_PADDLE_ONLY) && (sending_mode == MANUAL_SENDING))) { + #if !defined(OPTION_SIDETONE_DIGITAL_OUTPUT_NO_SQUARE_WAVE) + tone(sidetone_line, configuration.hz_sidetone); + #else + if (sidetone_line) { + digitalWrite(sidetone_line, sidetone_line_active_state); + } + #endif + } + key_state = 1; + } else { + if ((state == 0) && (key_state)) { + if (key_tx) { + if (current_tx_key_line) {digitalWrite (current_tx_key_line, tx_key_line_inactive_state);} + #if defined(OPTION_WINKEY_2_SUPPORT) && defined(FEATURE_WINKEY_EMULATION) && !defined(FEATURE_SO2R_BASE) + if ((wk2_both_tx_activated) && (tx_key_line_2)) { + digitalWrite (tx_key_line_2, LOW); + } + #endif + ptt_key(); + } + if ((configuration.sidetone_mode == SIDETONE_ON) || (keyer_machine_mode == KEYER_COMMAND_MODE) || ((configuration.sidetone_mode == SIDETONE_PADDLE_ONLY) && (sending_mode == MANUAL_SENDING))) { + #if !defined(OPTION_SIDETONE_DIGITAL_OUTPUT_NO_SQUARE_WAVE) + noTone(sidetone_line); + #else + if (sidetone_line) { + digitalWrite(sidetone_line, sidetone_line_inactive_state); + } + #endif + } + key_state = 0; + } + } + #else //FEATURE_PTT_INTERLOCK + if ((state) && (key_state == 0)) { + if (key_tx) { + byte previous_ptt_line_activated = ptt_line_activated; + if (!ptt_interlock_active) { + ptt_key(); + } + if (current_tx_key_line) {digitalWrite (current_tx_key_line, tx_key_line_active_state);} + #if defined(OPTION_WINKEY_2_SUPPORT) && defined(FEATURE_WINKEY_EMULATION) && !defined(FEATURE_SO2R_BASE) + if ((wk2_both_tx_activated) && (tx_key_line_2)) { + digitalWrite (tx_key_line_2, HIGH); + } + #endif + if ((first_extension_time) && (previous_ptt_line_activated == 0)) { + delay(first_extension_time); + } + } + if ((configuration.sidetone_mode == SIDETONE_ON) || (keyer_machine_mode == KEYER_COMMAND_MODE) || ((configuration.sidetone_mode == SIDETONE_PADDLE_ONLY) && (sending_mode == MANUAL_SENDING))) { + #if !defined(OPTION_SIDETONE_DIGITAL_OUTPUT_NO_SQUARE_WAVE) + tone(sidetone_line, configuration.hz_sidetone); + #else + if (sidetone_line) { + digitalWrite(sidetone_line, sidetone_line_active_state); + } + #endif + } + key_state = 1; + } else { + if ((state == 0) && (key_state)) { + if (key_tx) { + if (current_tx_key_line) {digitalWrite (current_tx_key_line, tx_key_line_inactive_state);} + #if defined(OPTION_WINKEY_2_SUPPORT) && defined(FEATURE_WINKEY_EMULATION) && !defined(FEATURE_SO2R_BASE) + if ((wk2_both_tx_activated) && (tx_key_line_2)) { + digitalWrite (tx_key_line_2, LOW); + } + #endif + if (!ptt_interlock_active) { + ptt_key(); + } + } + if ((configuration.sidetone_mode == SIDETONE_ON) || (keyer_machine_mode == KEYER_COMMAND_MODE) || ((configuration.sidetone_mode == SIDETONE_PADDLE_ONLY) && (sending_mode == MANUAL_SENDING))) { + #if !defined(OPTION_SIDETONE_DIGITAL_OUTPUT_NO_SQUARE_WAVE) + noTone(sidetone_line); + #else + if (sidetone_line) { + digitalWrite(sidetone_line, sidetone_line_inactive_state); + } + #endif + } + key_state = 0; + } + } + + #endif //FEATURE_PTT_INTERLOCK + + #if defined(FEATURE_INTERNET_LINK) + link_key(state); + #endif + + check_ptt_tail(); + + +} + + +//------------------------------------------------------------------------------------------------------- + +void loop_element_lengths(float lengths, float additional_time_ms, int speed_wpm_in){ + + #if defined(FEATURE_SERIAL) && !defined(OPTION_DISABLE_SERIAL_PORT_CHECKING_WHILE_SENDING_CW) + loop_element_lengths_breakout_flag = 1; + #endif //FEATURE_SERIAL + + #if defined(DEBUG_LOOP_ELEMENT_LENGTHS) + debug_serial_port->println("loop_element_lengths: enter"); + #endif + + + float element_length; + + if (lengths <= 0) { + return; + } + + #if defined(FEATURE_FARNSWORTH) + + if ((lengths == 1) && (speed_wpm_in == 0)){ + element_length = additional_time_ms; + } else { + if (speed_mode == SPEED_NORMAL) { + element_length = 1200/speed_wpm_in; + } else { + element_length = qrss_dit_length * 1000; + } + } + + #else //FEATURE_FARNSWORTH + if (speed_mode == SPEED_NORMAL) { + element_length = 1200/speed_wpm_in; + } else { + element_length = qrss_dit_length * 1000; + } + #endif //FEATURE_FARNSWORTH + + unsigned long ticks; + if ((long(element_length*lengths*1000) + long(additional_time_ms*1000)) < 0){ + return; + } else { + ticks = long(element_length*lengths*1000) + long(additional_time_ms*1000); // improvement from Paul, K1XM + } + unsigned long start = micros(); + + #if defined(FEATURE_SERIAL) && !defined(OPTION_DISABLE_SERIAL_PORT_CHECKING_WHILE_SENDING_CW) + while (((micros() - start) < ticks) && (service_tx_inhibit_and_pause() == 0) && loop_element_lengths_breakout_flag ){ + #else + while (((micros() - start) < ticks) && (service_tx_inhibit_and_pause() == 0)){ + #endif + + check_ptt_tail(); + + #if defined(FEATURE_SERIAL) && !defined(OPTION_DISABLE_SERIAL_PORT_CHECKING_WHILE_SENDING_CW) + if (((ticks - (micros() - start)) > (10 * 1000)) && (sending_mode == AUTOMATIC_SENDING)){ + check_serial(); + if (loop_element_lengths_breakout_flag == 0){ + dump_current_character_flag = 1; + } + } + #endif //FEATURE_SERIAL + + #if defined(FEATURE_INTERNET_LINK) /*&& !defined(OPTION_INTERNET_LINK_NO_UDP_SVC_DURING_KEY_DOWN)*/ + //if ((millis() > 1000) && ((millis()-start) > FEATURE_INTERNET_LINK_SVC_DURING_LOOP_TIME_MS)){ + if ((ticks - (micros() - start)) > (FEATURE_INTERNET_LINK_SVC_DURING_LOOP_TIME_MS * 1000)) { + service_udp_send_buffer(); + service_udp_receive(); + service_internet_link_udp_receive_buffer(); + } + #endif //FEATURE_INTERNET_LINK + + #if defined(OPTION_WATCHDOG_TIMER) + wdt_reset(); + #endif //OPTION_WATCHDOG_TIMER + + #if defined(FEATURE_ROTARY_ENCODER) + check_rotary_encoder(); + #endif //FEATURE_ROTARY_ENCODER + + #if defined(FEATURE_USB_KEYBOARD) || defined(FEATURE_USB_MOUSE) + service_usb(); + #endif //FEATURE_USB_KEYBOARD || FEATURE_USB_MOUSE + + #if defined(FEATURE_PTT_INTERLOCK) + service_ptt_interlock(); + #endif //FEATURE_PTT_INTERLOCK + + #if defined(FEATURE_4x4_KEYPAD) || defined(FEATURE_3x4_KEYPAD) + service_keypad(); + #endif + + #if defined(FEATURE_DISPLAY) + if ((ticks - (micros() - start)) > (10 * 1000)) { + service_display(); + } + #endif + + if ((configuration.keyer_mode != ULTIMATIC) && (configuration.keyer_mode != SINGLE_PADDLE)) { + if ((configuration.keyer_mode == IAMBIC_A) && (paddle_pin_read(paddle_left) == LOW ) && (paddle_pin_read(paddle_right) == LOW )) { + iambic_flag = 1; + } + + #ifndef FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING + if (being_sent == SENDING_DIT) { + check_dah_paddle(); + } else { + if (being_sent == SENDING_DAH) { + check_dit_paddle(); + } else { + check_dah_paddle(); + check_dit_paddle(); + } + } + #else ////FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING + //if (configuration.cmos_super_keyer_iambic_b_timing_on){ + if ((configuration.cmos_super_keyer_iambic_b_timing_on) && (sending_mode == MANUAL_SENDING)) { + if ((float(float(micros()-start)/float(ticks))*100) >= configuration.cmos_super_keyer_iambic_b_timing_percent) { + //if ((float(float(millis()-starttime)/float(starttime-ticks))*100) >= configuration.cmos_super_keyer_iambic_b_timing_percent) { + if (being_sent == SENDING_DIT) { + check_dah_paddle(); + } else { + if (being_sent == SENDING_DAH) { + check_dit_paddle(); + } + } + } else { + if (((being_sent == SENDING_DIT) || (being_sent == SENDING_DAH)) && (paddle_pin_read(paddle_left) == LOW ) && (paddle_pin_read(paddle_right) == LOW )) { + dah_buffer = 0; + dit_buffer = 0; + } + } + } else { + if (being_sent == SENDING_DIT) { + check_dah_paddle(); + } else { + if (being_sent == SENDING_DAH) { + check_dit_paddle(); + } else { + check_dah_paddle(); + check_dit_paddle(); + } + } + } + #endif //FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING + + } else { //(configuration.keyer_mode != ULTIMATIC) + #ifndef OPTION_NO_ULTIMATIC + if (being_sent == SENDING_DIT) { + check_dah_paddle(); + } else { + if (being_sent == SENDING_DAH) { + check_dit_paddle(); + } else { + check_dah_paddle(); + check_dit_paddle(); + } + } + #endif // OPTION_NO_ULTIMATIC + + } + + #if defined(FEATURE_MEMORIES) && defined(FEATURE_BUTTONS) + check_the_memory_buttons(); + #endif + + // blow out prematurely if we're automatic sending and a paddle gets hit + #ifdef FEATURE_BUTTONS + if (sending_mode == AUTOMATIC_SENDING && (paddle_pin_read(paddle_left) == LOW || paddle_pin_read(paddle_right) == LOW || analogbuttonread(0) || dit_buffer || dah_buffer)) { + if (keyer_machine_mode == KEYER_NORMAL) { + sending_mode = AUTOMATIC_SENDING_INTERRUPTED; + automatic_sending_interruption_time = millis(); + return; + } + } + #else + if (sending_mode == AUTOMATIC_SENDING && (paddle_pin_read(paddle_left) == LOW || paddle_pin_read(paddle_right) == LOW || dit_buffer || dah_buffer)) { + if (keyer_machine_mode == KEYER_NORMAL) { + sending_mode = AUTOMATIC_SENDING_INTERRUPTED; + automatic_sending_interruption_time = millis(); + + #ifdef FEATURE_SO2R_BASE + so2r_set_rx(); + #endif + + return; + } + } + #endif + + #ifdef FEATURE_STRAIGHT_KEY + service_straight_key(); + #endif //FEATURE_STRAIGHT_KEY + + + #if defined(FEATURE_WEB_SERVER) + if (speed_mode == SPEED_QRSS){ + service_web_server(); + } + #endif //FEATURE_WEB_SERVER + + #ifdef FEATURE_SO2R_SWITCHES + so2r_switches(); + #endif + + } //while ((millis() < endtime) && (millis() > 200)) + + if ((configuration.keyer_mode == IAMBIC_A) && (iambic_flag) && (paddle_pin_read(paddle_left) == HIGH ) && (paddle_pin_read(paddle_right) == HIGH )) { + iambic_flag = 0; + dit_buffer = 0; + dah_buffer = 0; + } + + if ((being_sent == SENDING_DIT) || (being_sent == SENDING_DAH)){ + if (configuration.dit_buffer_off) {dit_buffer = 0;} + if (configuration.dah_buffer_off) {dah_buffer = 0;} + } + + #if defined(DEBUG_LOOP_ELEMENT_LENGTHS) + debug_serial_port->println("loop_element_lengths: exit"); + #endif + + +} //void loop_element_lengths + + +//------------------------------------------------------------------------------------------------------- + +void speed_change(int change) +{ + if (((configuration.wpm + change) > wpm_limit_low) && ((configuration.wpm + change) < wpm_limit_high)) { + speed_set(configuration.wpm + change); + } + + + + #ifdef FEATURE_DISPLAY + lcd_center_print_timed_wpm(); + #endif +} + +//------------------------------------------------------------------------------------------------------- + +void speed_change_command_mode(int change) +{ + if (((configuration.wpm_command_mode + change) > wpm_limit_low) && ((configuration.wpm_command_mode + change) < wpm_limit_high)) { + configuration.wpm_command_mode = configuration.wpm_command_mode + change; + config_dirty = 1; + } + + + #ifdef FEATURE_DISPLAY + lcd_center_print_timed(String(configuration.wpm_command_mode) + " wpm", 0, default_display_msg_delay); + #endif +} + +//------------------------------------------------------------------------------------------------------- + +void speed_set(int wpm_set){ + + + if ((wpm_set > 0) && (wpm_set < 1000)){ + configuration.wpm = wpm_set; + config_dirty = 1; + + #ifdef FEATURE_DYNAMIC_DAH_TO_DIT_RATIO + if ((configuration.wpm >= DYNAMIC_DAH_TO_DIT_RATIO_LOWER_LIMIT_WPM) && (configuration.wpm <= DYNAMIC_DAH_TO_DIT_RATIO_UPPER_LIMIT_WPM)){ + int dynamicweightvalue=map(configuration.wpm,DYNAMIC_DAH_TO_DIT_RATIO_LOWER_LIMIT_WPM,DYNAMIC_DAH_TO_DIT_RATIO_UPPER_LIMIT_WPM,DYNAMIC_DAH_TO_DIT_RATIO_LOWER_LIMIT_RATIO,DYNAMIC_DAH_TO_DIT_RATIO_UPPER_LIMIT_RATIO); + configuration.dah_to_dit_ratio=dynamicweightvalue; + } + #endif //FEATURE_DYNAMIC_DAH_TO_DIT_RATIO + + + #ifdef FEATURE_LED_RING + update_led_ring(); + #endif //FEATURE_LED_RING + + #ifdef FEATURE_DISPLAY + lcd_center_print_timed_wpm(); + #endif + } +} +//------------------------------------------------------------------------------------------------------- + +void command_speed_set(int wpm_set) { + if ((wpm_set > 0) && (wpm_set < 1000)) { + configuration.wpm_command_mode = wpm_set; + config_dirty = 1; + + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("Cmd Spd " + String(configuration.wpm_command_mode) + " wpm", 0, default_display_msg_delay); + #endif // FEATURE_DISPLAY + } // end if +} // end command_speed_set + +//------------------------------------------------------------------------------------------------------- + +#ifdef FEATURE_DISPLAY + void lcd_center_print_timed_wpm(){ + + + #if defined(OPTION_ADVANCED_SPEED_DISPLAY) + lcd_center_print_timed(String(configuration.wpm) + " wpm - " + (configuration.wpm*5) + " cpm", 0, default_display_msg_delay); + lcd_center_print_timed(String(1200/configuration.wpm) + ":" + (((1200/configuration.wpm)*configuration.dah_to_dit_ratio)/100) + "ms 1:" + (float(configuration.dah_to_dit_ratio)/100.00), 1, default_display_msg_delay); + #else + lcd_center_print_timed(String(configuration.wpm) + " wpm", 0, default_display_msg_delay); + #endif + + } +#endif +//------------------------------------------------------------------------------------------------------- + +long get_cw_input_from_user(unsigned int exit_time_milliseconds) { + + byte looping = 1; + byte paddle_hit = 0; + long cw_char = 0; + unsigned long last_element_time = 0; + byte button_hit = 0; + unsigned long entry_time = millis(); + + while (looping) { + + #ifdef OPTION_WATCHDOG_TIMER + wdt_reset(); + #endif //OPTION_WATCHDOG_TIMER + + #ifdef FEATURE_POTENTIOMETER + if (configuration.pot_activated) { + check_potentiometer(); + } + #endif + + #ifdef FEATURE_ROTARY_ENCODER + check_rotary_encoder(); + #endif //FEATURE_ROTARY_ENCODER + + check_paddles(); + + if (dit_buffer) { + sending_mode = MANUAL_SENDING; + send_dit(); + dit_buffer = 0; + paddle_hit = 1; + cw_char = (cw_char * 10) + 1; + last_element_time = millis(); + } + if (dah_buffer) { + sending_mode = MANUAL_SENDING; + send_dah(); + dah_buffer = 0; + paddle_hit = 1; + cw_char = (cw_char * 10) + 2; + last_element_time = millis(); + } + if ((paddle_hit) && (millis() > (last_element_time + (float(600/configuration.wpm) * length_letterspace)))) { + #ifdef DEBUG_GET_CW_INPUT_FROM_USER + debug_serial_port->println(F("get_cw_input_from_user: hit length_letterspace")); + #endif + looping = 0; + } + + if ((!paddle_hit) && (exit_time_milliseconds) && ((millis() - entry_time) > exit_time_milliseconds)) { // if we were passed an exit time and no paddle was hit, blow out of here + return 0; + } + + #ifdef FEATURE_BUTTONS + while (analogbuttonread(0)) { // hit the button to get out of command mode if no paddle was hit + looping = 0; + button_hit = 1; + } + #endif + + #if defined(FEATURE_SERIAL) + check_serial(); + #endif + + } //while (looping) + + + + + if (button_hit) { + #ifdef DEBUG_GET_CW_INPUT_FROM_USER + debug_serial_port->println(F("get_cw_input_from_user: button_hit exit 9")); + #endif + return 9; + } else { + #ifdef DEBUG_GET_CW_INPUT_FROM_USER + debug_serial_port->print(F("get_cw_input_from_user: exiting cw_char:")); + debug_serial_port->println(cw_char); + #endif + return cw_char; + } +} + +//------------------------------------------------------------------------------------------------------- +#ifdef FEATURE_COMMAND_MODE + + void send_chars(char* buffer){ + + int x = 0; + + while(buffer[x] != 0){ + send_char(buffer[x++],0); + } + } + +#endif + +//------------------------------------------------------------------------------------------------------- + +#ifdef FEATURE_COMMAND_MODE +void command_mode() { + + keyer_machine_mode = KEYER_COMMAND_MODE; + + #ifdef DEBUG_COMMAND_MODE + debug_serial_port->println(F("command_mode: entering")); + #endif + + #ifdef OPTION_WATCHDOG_TIMER + wdt_disable(); + #endif //OPTION_WATCHDOG_TIMER + + char weight_deci[] = "00"; + byte looping; + byte button_that_was_pressed = 0; + byte paddle_hit = 0; + unsigned long last_element_time = 0; + unsigned long cw_char; + byte stay_in_command_mode = 1; + byte speed_mode_before = speed_mode; + speed_mode = SPEED_NORMAL; // put us in normal speed mode (life is too short to do command mode in QRSS) + byte keyer_mode_before = configuration.keyer_mode; + char c[4]; + if ((configuration.keyer_mode != IAMBIC_A) && (configuration.keyer_mode != IAMBIC_B) && (configuration.keyer_mode != ULTIMATIC)) { + //if ((configuration.keyer_mode != IAMBIC_A) && (configuration.keyer_mode != IAMBIC_B)) { + configuration.keyer_mode = IAMBIC_B; // we got to be in iambic mode (life is too short to make this work in bug mode) + } + + // command_mode_disable_tx = 0; //Removed disable TX state every time Command Mode is entered - now set to actual key_tx status on CM entry (WD9DMP) + + boop_beep(); + #ifdef command_mode_active_led + if (command_mode_active_led) {digitalWrite(command_mode_active_led,HIGH);} + #endif //command_mode_active_led + + #ifdef FEATURE_DISPLAY + lcd_clear(); + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("Cmd Mode", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Command Mode", 0, default_display_msg_delay); + } + #endif + + #if defined(FEATURE_WINKEY_EMULATION) && defined(OPTION_WINKEY_SEND_BREAKIN_STATUS_BYTE) + winkey_breakin_status_byte_inhibit = 1; + #endif + + while (stay_in_command_mode) { + cw_char = 0; + looping = 1; + while (looping) { + int8_t button_temp = button_array.Pressed(); + + #ifdef FEATURE_DISPLAY + service_display(); + #endif + + #ifdef FEATURE_POTENTIOMETER + if (configuration.pot_activated) { + check_potentiometer(); + } + #endif + + #ifdef FEATURE_ROTARY_ENCODER + check_rotary_encoder(); + #endif //FEATURE_ROTARY_ENCODER + + check_paddles(); + + if (dit_buffer) { + sending_mode = MANUAL_SENDING; + send_dit(); + dit_buffer = 0; + paddle_hit = 1; + cw_char = (cw_char * 10) + 1; + last_element_time = millis(); + } + if (dah_buffer) { + sending_mode = MANUAL_SENDING; + send_dah(); + dah_buffer = 0; + paddle_hit = 1; + cw_char = (cw_char * 10) + 2; + last_element_time = millis(); + } + if ((paddle_hit) && (millis() > (last_element_time + (float(600/configuration.wpm) * length_letterspace)))) { + #ifdef DEBUG_GET_CW_INPUT_FROM_USER + debug_serial_port->println(F("get_cw_input_from_user: hit length_letterspace")); + #endif + looping = 0; + } + if (button_temp >=0 ){ // check for a button press + looping = 0; + cw_char = 9; + delay(50); + button_that_was_pressed = button_temp; + while (button_array.Held(button_that_was_pressed)) {} + } + + #if defined(FEATURE_SERIAL) + configuration.keyer_mode = keyer_mode_before; + check_serial(); + if ((configuration.keyer_mode != IAMBIC_A) && (configuration.keyer_mode != IAMBIC_B) && (configuration.keyer_mode != ULTIMATIC) && (configuration.keyer_mode != SINGLE_PADDLE)) { + configuration.keyer_mode = IAMBIC_B; + } + #endif + + } //while (looping) +// end new code + + #ifdef DEBUG_COMMAND_MODE + debug_serial_port->print(F("command_mode: cwchar: ")); + debug_serial_port->println(cw_char); + #endif + if (cw_char > 0) { // do the command + switch (cw_char) { + case 12: // A - Iambic mode + configuration.keyer_mode = IAMBIC_A; + keyer_mode_before = IAMBIC_A; + configuration.dit_buffer_off = 0; + configuration.dah_buffer_off = 0; + config_dirty = 1; + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("Iambic A", 0, default_display_msg_delay); + #endif + #if defined(FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT) + send_chars((char*)command_a_iambic_a); + #else + send_char(command_mode_acknowledgement_character, 0); + #endif + break; + + case 2111: // B - Iambic mode + configuration.keyer_mode = IAMBIC_B; + keyer_mode_before = IAMBIC_B; + configuration.dit_buffer_off = 0; + configuration.dah_buffer_off = 0; + config_dirty = 1; + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("Iambic B", 0, default_display_msg_delay); + #endif + #if defined(FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT) + send_chars((char*)command_b_iambic_b); + #else + send_char(command_mode_acknowledgement_character, 0); + #endif + break; + + case 2121: // C - Single paddle mode + configuration.keyer_mode = SINGLE_PADDLE; + keyer_mode_before = SINGLE_PADDLE; + config_dirty = 1; + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("Sngl Pdl", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Single Paddle", 0, default_display_msg_delay); + } + #endif + #if defined(FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT) + send_chars((char*)command_c_single_paddle); + #else + send_char(command_mode_acknowledgement_character, 0); + #endif + break; + case 1: // E - announce spEed + #ifdef FEATURE_DISPLAY + #if defined(OPTION_ADVANCED_SPEED_DISPLAY) + lcd_center_print_timed("Speed", 0, default_display_msg_delay); + lcd_center_print_timed(String(configuration.wpm) + " wpm - " + (configuration.wpm*5) + " cpm ", 1, default_display_msg_delay); + if (LCD_ROWS > 2) { + lcd_center_print_timed(String(1200/configuration.wpm) + ":" + (((1200/configuration.wpm)*configuration.dah_to_dit_ratio)/100) + "ms 1:" + (float(configuration.dah_to_dit_ratio)/100.00), 2, default_display_msg_delay); + } + #else // OPTION_ADVANCED_SPEED_DISPLAY_DISPLAY + lcd_center_print_timed("Speed " + String(configuration.wpm) + " wpm", 0, default_display_msg_delay); + #endif // OPTION_ADVANCED_SPEED_DISPLAY_DISPLAY + #endif // FEATURE_DISPLAY + delay(250); + sprintf(c, "%d", configuration.wpm); + send_char(c[0],KEYER_NORMAL); + send_char(c[1],KEYER_NORMAL); + break; + #ifndef OPTION_NO_ULTIMATIC + case 211: // D - Ultimatic mode + configuration.keyer_mode = ULTIMATIC; + keyer_mode_before = ULTIMATIC; + configuration.dit_buffer_off = 1; + configuration.dah_buffer_off = 1; + config_dirty = 1; + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9) { + lcd_center_print_timed("Ultimatc", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Ultimatic", 0, default_display_msg_delay); + } + #endif + #if defined(FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT) + send_chars((char*)command_d_ultimatic); + #else + send_char(command_mode_acknowledgement_character, 0); + #endif + break; + #endif //OPTION_NO_ULTIMATIC + #if !defined(OPTION_SIDETONE_DIGITAL_OUTPUT_NO_SQUARE_WAVE) + case 1121: command_sidetone_freq_adj(); break; // F - adjust sidetone frequency + #endif + + case 221: // G - switch to buG mode + configuration.keyer_mode = BUG; + keyer_mode_before = BUG; + config_dirty = 1; + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("Bug", 0, default_display_msg_delay); + #endif + send_char(command_mode_acknowledgement_character, 0); + break; + + case 1111: // H - set weighting and dah to dit ratio to defaults + configuration.weighting = default_weighting; + configuration.dah_to_dit_ratio = initial_dah_to_dit_ratio; + config_dirty = 1; + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("Dflt W+R", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Dflt Wght & Ratio", 0, default_display_msg_delay); + } + #endif + #if defined(FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT) + send_chars((char*)command_h_weight_dit_dah_ratio_default); + #else + send_char(command_mode_acknowledgement_character, 0); + #endif + break; + case 11: // I - toggle TX enable / disable + if (command_mode_disable_tx) { + command_mode_disable_tx = 0; + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("TX On", 0, default_display_msg_delay); + #endif + #if defined(FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT) + send_chars((char*)command_i_tx_on); + #endif + } else { + command_mode_disable_tx = 1; + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("TX Off", 0, default_display_msg_delay); + #endif + #if defined(FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT) + send_chars((char*)command_i_tx_off); + #endif + } + #if !defined(FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT) + send_char(command_mode_acknowledgement_character, 0); + #endif + break; + case 1222: command_dah_to_dit_ratio_adjust(); break; // J - dah to dit ratio adjust + #ifndef OPTION_NO_ULTIMATIC + case 212: // K - turn dit and dah buffers on and off + if (configuration.keyer_mode == ULTIMATIC){ + //send_char('O',KEYER_NORMAL); + if (configuration.dit_buffer_off){ + configuration.dit_buffer_off = 0; + configuration.dah_buffer_off = 0; + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("BffersOn", 0, default_display_msg_delay); + } + if (LCD_COLUMNS > 17){ + lcd_center_print_timed("Dit Dah Buffers On", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Buffers On", 0, default_display_msg_delay); + } + #endif + #if defined(FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT) + send_chars((char*)command_k_dit_dah_buffers_on); + #else + send_char(command_mode_acknowledgement_character, 0); + #endif + } else { + configuration.dit_buffer_off = 1; + configuration.dah_buffer_off = 1; + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("BffrsOff", 0, default_display_msg_delay); + } + if (LCD_COLUMNS > 18){ + lcd_center_print_timed("Dit Dah Buffers Off", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Buffers Off", 0, default_display_msg_delay); + } + #endif + #if defined(FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT) + send_chars((char*)command_k_dit_dah_buffers_off); + #else + send_char(command_mode_acknowledgement_character, 0); + #endif + } + } else { + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("Error", 0, default_display_msg_delay); + #endif + #if defined(FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT) + send_chars((char*)command_error); + #else + boop(); + #endif + } + break; + #endif //OPTION_NO_ULTIMATIC + + case 1211: command_weighting_adjust();break; // L - weight adjust + + case 22: // M - Set command mode WPM + command_speed_mode(COMMAND_SPEED_MODE_COMMAND_MODE_WPM); + break; + + #ifdef FEATURE_MEMORIES + case 1221: command_program_memory(); break; // P - program a memory + #endif //FEATURE_MEMORIES Acknowledgement: LA3ZA fixed! + case 21: // N - paddle mode toggle + if (configuration.paddle_mode == PADDLE_NORMAL) { + configuration.paddle_mode = PADDLE_REVERSE; + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("Pdl Rev", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Paddle Reverse", 0, default_display_msg_delay); + } + #endif //FEATURE_DISPLAY + #if defined(FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT) + send_chars((char*)command_n_paddle_reverse); + #endif + } else { + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("Pdl Norm", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Paddle Normal", 0, default_display_msg_delay); + } + #endif //FEATURE_DISPLAY + #if defined(FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT) + send_chars((char*)command_n_paddle_normal); + #endif + configuration.paddle_mode = PADDLE_NORMAL; + } + config_dirty = 1; + #if !defined(FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT) + send_char(command_mode_acknowledgement_character, 0); + #endif + break; + case 222: // O - cycle through sidetone modes on, paddle only and off - enhanced by Marc-Andre, VE2EVN + if (configuration.sidetone_mode == SIDETONE_PADDLE_ONLY) { + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9) { + lcd_center_print_timed("ST Off", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Sidetone Off", 0, default_display_msg_delay); + } + #endif + #ifdef DEBUG_COMMAND_MODE + debug_serial_port->println(F("command_mode: SIDETONE_OFF")); + #endif + configuration.sidetone_mode = SIDETONE_OFF; + #if !defined(FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT) + boop(); + #else + send_chars((char*)command_o_sidetone_off); + #endif + } else if (configuration.sidetone_mode == SIDETONE_ON) { + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("ST Pdl O", 0, default_display_msg_delay); + } + if (LCD_COLUMNS > 19) { + lcd_center_print_timed("Sidetone Paddle Only", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Sidetone", 0, default_display_msg_delay); + lcd_center_print_timed("Paddle Only", 1, default_display_msg_delay); + } + #endif + #ifdef DEBUG_COMMAND_MODE + debug_serial_port->println(F("command_mode: SIDETONE_PADDLE_ONLY")); + #endif + configuration.sidetone_mode = SIDETONE_PADDLE_ONLY; + #if !defined(FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT) + beep(); + delay(200); + beep(); + #else + send_chars((char*)command_o_sidetone_paddle_only); + #endif + } else { + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9) { + lcd_center_print_timed("ST On", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Sidetone On", 0, default_display_msg_delay); + } + #endif + #ifdef DEBUG_COMMAND_MODE + debug_serial_port->println(F("command_mode: SIDETONE_ON")); + #endif + configuration.sidetone_mode = SIDETONE_ON; + #if !defined(FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT) + beep(); + #else + send_chars((char*)command_o_sidetone_on); + #endif + } + config_dirty = 1; + break; + case 2212: // Q - set keying compensation +//zzzzzz + command_keying_compensation_adjust(); + break; + + case 121: command_set_serial_number(); break; // R - Set serial number + + case 2: command_tuning_mode(); break; // T - tuning mode + #ifdef FEATURE_POTENTIOMETER + case 1112: // V - toggle pot active + if (configuration.pot_activated) { + configuration.pot_activated = 0; + #if defined(FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT) + send_chars((char*)command_v_potentiometer_off); + #endif + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS > 14) { + lcd_center_print_timed("Pot Deactivated", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Pot Off", 0, default_display_msg_delay); + } + #endif + } else { + configuration.pot_activated = 1; + #if defined(FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT) + send_chars((char*)command_v_potentiometer_on); + #endif + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS > 13){ + lcd_center_print_timed("Pot Activated", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Pot On", 0, default_display_msg_delay); + } + #endif + } + config_dirty = 1; + #if !defined(FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT) + send_char(command_mode_acknowledgement_character, 0); + #endif + break; + #endif + + case 122: // W - change wpm + command_speed_mode(COMMAND_SPEED_MODE_KEYER_WPM); + break; + + #ifdef FEATURE_MEMORIES + case 2122: command_set_mem_repeat_delay(); break; // Y - set memory repeat delay + #endif + + case 2112: stay_in_command_mode = 0; break; // X - exit command mode + #ifdef FEATURE_AUTOSPACE + case 2211: // Z - Autospace + if (configuration.autospace_active) { + configuration.autospace_active = 0; + config_dirty = 1; + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("AutoSOff", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Autospace Off", 0, default_display_msg_delay); + } + send_char(command_mode_acknowledgement_character, 0); + #else + send_char('O',KEYER_NORMAL); + send_char('F',KEYER_NORMAL); + send_char('F',KEYER_NORMAL); + #endif + } else { + configuration.autospace_active = 1; + config_dirty = 1; + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("AutoS On", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Autospace On", 0, default_display_msg_delay); + } + send_char(command_mode_acknowledgement_character, 0); + #else + send_char('O',KEYER_NORMAL); + send_char('N',KEYER_NORMAL); + #endif + } + break; + #endif +// #ifdef FEATURE_MEMORIES +// case 12222: play_memory(0); break; +// case 11222: play_memory(1); break; +// case 11122: play_memory(2); break; +// case 11112: play_memory(3); break; +// case 11111: play_memory(4); break; +// #endif + + #ifdef FEATURE_MEMORIES + case 12222: + if (number_of_memories > 0) { + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("Memory #1", 0, default_display_msg_delay); + #ifdef OPTION_DISPLAY_MEMORY_CONTENTS_COMMAND_MODE + command_display_memory(0); + #endif // OPTION_DISPLAY_MEMORY_CONTENTS_COMMAND_MODE + #endif // FEATURE_DISPLAY + play_memory(0); + } else { + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS > 17) lcd_center_print_timed("Invalid memory #1", 0, default_display_msg_delay); + else if (LCD_COLUMNS > 13) lcd_center_print_timed("Invalid mem #1", 0, default_display_msg_delay); + #endif // FEATURE_DISPLAY + beep_boop(); + } + break; + + case 11222: + if (number_of_memories > 1) { + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("Memory #2", 0, default_display_msg_delay); + #ifdef OPTION_DISPLAY_MEMORY_CONTENTS_COMMAND_MODE + command_display_memory(1); + #endif // OPTION_DISPLAY_MEMORY_CONTENTS_COMMAND_MODE + #endif // FEATURE_DISPLAY + play_memory(1); + } else { + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS > 17) lcd_center_print_timed("Invalid memory #2", 0, default_display_msg_delay); + else if (LCD_COLUMNS > 13) lcd_center_print_timed("Invalid mem #2", 0, default_display_msg_delay); + #endif // FEATURE_DISPLAY + beep_boop(); + } + break; + + case 11122: + if (number_of_memories > 2) { + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("Memory #3", 0, default_display_msg_delay); + #ifdef OPTION_DISPLAY_MEMORY_CONTENTS_COMMAND_MODE + command_display_memory(2); + #endif // OPTION_DISPLAY_MEMORY_CONTENTS_COMMAND_MODE + #endif // FEATURE_DISPLAY + play_memory(2); + } else { + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS > 17) lcd_center_print_timed("Invalid memory #3", 0, default_display_msg_delay); + else if (LCD_COLUMNS > 13) lcd_center_print_timed("Invalid mem #3", 0, default_display_msg_delay); + #endif // FEATURE_DISPLAY + beep_boop(); + } + break; + + case 11112: + if (number_of_memories > 3) { + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("Memory #4", 0, default_display_msg_delay); + #ifdef OPTION_DISPLAY_MEMORY_CONTENTS_COMMAND_MODE + command_display_memory(3); + #endif // OPTION_DISPLAY_MEMORY_CONTENTS_COMMAND_MODE + #endif // FEATURE_DISPLAY + play_memory(3); + } else { + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS > 17) lcd_center_print_timed("Invalid memory #4", 0, default_display_msg_delay); + else if (LCD_COLUMNS > 13) lcd_center_print_timed("Invalid mem #4", 0, default_display_msg_delay); + #endif // FEATURE_DISPLAY + beep_boop(); + } + break; + + case 11111: + if (number_of_memories > 4) { + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("Memory #5", 0, default_display_msg_delay); + #ifdef OPTION_DISPLAY_MEMORY_CONTENTS_COMMAND_MODE + command_display_memory(4); + #endif // OPTION_DISPLAY_MEMORY_CONTENTS_COMMAND_MODE + #endif // FEATURE_DISPLAY + play_memory(4); + } else { + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS > 17) lcd_center_print_timed("Invalid memory #5", 0, default_display_msg_delay); + else if (LCD_COLUMNS > 13) lcd_center_print_timed("Invalid mem #5", 0, default_display_msg_delay); + #endif // FEATURE_DISPLAY + beep_boop(); + } + break; + #endif // FEATURE_MEMORIES + + case 21112: // - : enable / disable PTT + if (configuration.ptt_disabled){ + configuration.ptt_disabled = 0; + } else { + configuration.ptt_disabled = 1; + } + config_dirty = 1; + send_char(command_mode_acknowledgement_character, 0); + break; + + case 121212:send_char(75,KEYER_NORMAL);send_char(51,KEYER_NORMAL);send_char(78,KEYER_NORMAL);send_char(71,KEYER_NORMAL);send_char(32,KEYER_NORMAL); + send_char(55,KEYER_NORMAL);send_char(51,KEYER_NORMAL);send_char(32,KEYER_NORMAL);send_char(69,KEYER_NORMAL);send_char(69,KEYER_NORMAL); + break; + + #ifdef FEATURE_ALPHABET_SEND_PRACTICE // enhanced by Fred, VK2EFL + case 111: // S - Alphabet Send Practice + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("SendPrct", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Send Practice", 0, default_display_msg_delay); + if (LCD_ROWS > 1){ + lcd_center_print_timed("Cmd button to exit", 1, default_display_msg_delay); + } + } + #endif + beep(); + command_alphabet_send_practice(); + stay_in_command_mode = 0; + break; + #endif //FEATURE_ALPHABET_SEND_PRACTICE + + #ifdef FEATURE_COMMAND_MODE_PROGRESSIVE_5_CHAR_ECHO_PRACTICE + case 112: // U - 5 Character Echo Practice + command_progressive_5_char_echo_practice(); + stay_in_command_mode = 0; + break; + #endif //FEATURE_COMMAND_MODE_PROGRESSIVE_5_CHAR_PRACTICE + + case 112211: // ? - status + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("Status", 0, default_display_msg_delay); + lcd_center_print_timed("wpm " + String(configuration.wpm), 1, default_display_msg_delay); + delay(250); + #endif // FEATURE_DISPLAY + + sprintf(c, "%d", configuration.wpm); + send_char(c[0],KEYER_NORMAL); + send_char(c[1],KEYER_NORMAL); + send_char(' ' ,KEYER_NORMAL); + + switch(keyer_mode_before) { + case IAMBIC_A: + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("mode Iambic A", 1, default_display_msg_delay); + delay(250); + #endif // FEATURE_DISPLAY + send_char('A',KEYER_NORMAL); + break; + case IAMBIC_B: + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("mode Iambic B", 1, default_display_msg_delay); + delay(250); + #endif // FEATURE_DISPLAY + send_char('B',KEYER_NORMAL); + break; + case SINGLE_PADDLE: + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("mode Single Pdl", 1, default_display_msg_delay); + delay(250); + #endif // FEATURE_DISPLAY + send_char('S',KEYER_NORMAL); + break; + + #ifndef OPTION_NO_ULTIMATIC + case ULTIMATIC: + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("mode Ultimatic", 1, default_display_msg_delay); + delay(250); + #endif // FEATURE_DISPLAY + send_char('U',KEYER_NORMAL); + break; + #endif //OPTION_NO_ULTIMATIC + case BUG: + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("mode Bug", 1, default_display_msg_delay); + delay(250); + #endif // FEATURE_DISPLAY + send_char('G',KEYER_NORMAL); + break; + } // switch(keyer_mode_before) + send_char(' ',KEYER_NORMAL); + send_char(' ',KEYER_NORMAL); + + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("weighting " + String(configuration.weighting), 1, default_display_msg_delay); + delay(250); + #endif // FEATURE_DISPLAY + sprintf(c, "%d", configuration.weighting); + send_char(c[0],KEYER_NORMAL); + send_char(c[1],KEYER_NORMAL); + send_char(' ',KEYER_NORMAL); + + #ifdef FEATURE_DISPLAY + strcpy(weight_deci, "00"); // reset the two character array + if ((configuration.dah_to_dit_ratio % 100) != 0) { // test if it is X.00 + weight_deci[0] = ((configuration.dah_to_dit_ratio % 100) / 10) + '0'; // for cases where decimal part is 10 to 99 + weight_deci[1] = (configuration.dah_to_dit_ratio % 10) + '0'; // get the single digit units part + } // end if ((configuration.dah_to_dit_ratio % 100) != 0) + lcd_center_print_timed("dah:dit " + String(configuration.dah_to_dit_ratio / 100) + "." + weight_deci, 1, default_display_msg_delay); + delay(250); + #endif // FEATURE_DISPLAY + sprintf(c, "%d", configuration.dah_to_dit_ratio); + send_char(c[0],KEYER_NORMAL); + send_char('.',KEYER_NORMAL); + send_char(c[1],KEYER_NORMAL); + send_char(c[2],KEYER_NORMAL); + send_char(' ',KEYER_NORMAL); + break; + + case 9: // button was hit + // Serial.print("Button - "); + // Serial.println(button_that_was_pressed); + + #if defined(FEATURE_MEMORIES) + if (button_that_was_pressed == 0){ // button 0 was hit - exit + stay_in_command_mode = 0; + } else { + program_memory(button_that_was_pressed - 1); // a button other than 0 was pressed - program a memory + } + #else + stay_in_command_mode = 0; + #endif + break; + default: // unknown command, send a ? + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("???", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Unknown command", 0, default_display_msg_delay); + } + #endif + send_char('?',KEYER_NORMAL); + break; + } + } + } + beep_boop(); + #if defined(FEATURE_WINKEY_EMULATION) && defined(OPTION_WINKEY_SEND_BREAKIN_STATUS_BYTE) + winkey_breakin_status_byte_inhibit = 0; + #endif + + #ifdef command_mode_active_led + if (command_mode_active_led) {digitalWrite(command_mode_active_led,LOW);} + #endif //command_mode_active_led + + keyer_machine_mode = KEYER_NORMAL; + //configuration.wpm = speed_wpm_before; + speed_mode = speed_mode_before; // go back to whatever speed mode we were in before + configuration.keyer_mode = keyer_mode_before; + + #ifdef DEBUG_COMMAND_MODE + if (command_mode_disable_tx) { + debug_serial_port->print(F("command_mode: command_mode_disable_tx set")); + } + #endif //DEBUG_COMMAND_MODE + + #if defined(FEATURE_PADDLE_ECHO) + paddle_echo_buffer = 0; + #endif + + #ifdef OPTION_WATCHDOG_TIMER + wdt_enable(WDTO_4S); + #endif //OPTION_WATCHDOG_TIMER + +} +#endif //FEATURE_COMMAND_MODE + +//------------------------------------------------------------------------------------------------------- + +void command_display_memory(byte memory_number) { + + #if defined(FEATURE_DISPLAY) && defined(FEATURE_MEMORIES) + byte eeprom_byte_read = 0; + char memory_char[LCD_COLUMNS]; // an array of char to hold the retrieved memory from EEPROM + int j; + int fill_char; // a flag that is set if we need to fill the char array with spaces + + j = 0; + fill_char = 0; + for(int y = (memory_start(memory_number)); y < (memory_start(memory_number)) + LCD_COLUMNS; y++) { + eeprom_byte_read = EEPROM.read(y); // read memory characters from EEPROM + if (eeprom_byte_read == 255) fill_char = 1; // if it is the 'end of stored memory' character set a flag + if (!fill_char) memory_char[j] = eeprom_byte_read; // save the retrieved character in the character array + else memory_char[j] = ' '; // else fill the rest of the array with spaces + j++; // move to the next character to be stored in the array + } // end for + lcd_center_print_timed(memory_char, 1, default_display_msg_delay); // write the retrieved char array to line 2 of LCD display + #endif // FEATURE_DISPLAY +} // end command_display_memory + +//------------------------------------------------------------------------------------------------------- + +#if defined(FEATURE_COMMAND_MODE_PROGRESSIVE_5_CHAR_ECHO_PRACTICE) && defined(FEATURE_COMMAND_MODE) +void command_progressive_5_char_echo_practice() { + + byte loop1 = 1; + byte loop2 = 0; + byte x = 0; + byte user_send_loop = 0; + String cw_to_send_to_user(10); + char incoming_char = ' '; + String user_sent_cw = ""; + byte paddle_hit = 0; + unsigned long last_element_time = 0; + unsigned long cw_char; + byte speed_mode_before = speed_mode; + byte keyer_mode_before = configuration.keyer_mode; + byte progressive_step_counter; + byte practice_mode; + char word_buffer[10]; + + speed_mode = SPEED_NORMAL; // put us in normal speed mode + if ((configuration.keyer_mode != IAMBIC_A) && (configuration.keyer_mode != IAMBIC_B)) { + configuration.keyer_mode = IAMBIC_B; // we got to be in iambic mode (life is too short to make this work in bug mode) + } + randomSeed(millis()); + + #ifdef FEATURE_DISPLAY // enhanced by Fred, VK2EFL + lcd_clear(); + if (LCD_COLUMNS > 17){ + lcd_center_print_timed("Receive / Transmit", 0, default_display_msg_delay); + lcd_center_print_timed("5 Char Echo Practice", 1, default_display_msg_delay); + if (LCD_ROWS > 2){ + lcd_center_print_timed("Cmd button to exit", 2, default_display_msg_delay); + } + } else { + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("RXTX 5Ch", 0, default_display_msg_delay); + if (LCD_ROWS > 1){ + lcd_center_print_timed("EchoPrct", 1, default_display_msg_delay); + } + } else { + lcd_center_print_timed("RX / TX 5 Char", 0, default_display_msg_delay); + if (LCD_ROWS > 1){ + lcd_center_print_timed("Echo Practice", 1, default_display_msg_delay); + } + } + } + service_display(); + #else + send_char('E',0); + send_char('C',0); + send_char('H',0); + send_char('O',0); + send_char(' ',0); + send_char(' ',0); + send_char(' ',0); + beep(); + beep(); + #endif + + while (loop1) { + // if (practice_mode_called == ECHO_MIXED){ + // practice_mode = random(ECHO_2_CHAR_WORDS,ECHO_QSO_WORDS+1); + // } else { + // practice_mode = practice_mode_called; + // } + + // progressive_step_counter = 255; + + // switch (practice_mode){ + // case CALLSIGN_INTERNATIONAL: + // case CALLSIGN_US: + // case CALLSIGN_EUROPEAN: + // case CALLSIGN_CANADA: + // cw_to_send_to_user = generate_callsign(practice_mode); + // break; + // case ECHO_PROGRESSIVE_5: + cw_to_send_to_user = (char)random(65,91); + cw_to_send_to_user.concat((char)random(65,91)); + cw_to_send_to_user.concat((char)random(65,91)); + cw_to_send_to_user.concat((char)random(65,91)); + cw_to_send_to_user.concat((char)random(65,91)); + progressive_step_counter = 1; + // break; + // case ECHO_2_CHAR_WORDS: + // //word_index = random(0,s2_size); // min parm is inclusive, max parm is exclusive + // strcpy_P(word_buffer, (char*)pgm_read_word(&(s2_table[random(0,s2_size)]))); + // cw_to_send_to_user = word_buffer; + // break; + // case ECHO_3_CHAR_WORDS: + // //word_index = random(0,s3_size); // min parm is inclusive, max parm is exclusive + // strcpy_P(word_buffer, (char*)pgm_read_word(&(s3_table[random(0,s3_size)]))); + // cw_to_send_to_user = word_buffer; + // break; + // case ECHO_4_CHAR_WORDS: + // //word_index = random(0,s4_size); // min parm is inclusive, max parm is exclusive + // strcpy_P(word_buffer, (char*)pgm_read_word(&(s4_table[random(0,s4_size)]))); + // cw_to_send_to_user = word_buffer; + // break; + // case ECHO_NAMES: + // //word_index = random(0,name_size); // min parm is inclusive, max parm is exclusive + // strcpy_P(word_buffer, (char*)pgm_read_word(&(name_table[random(0,name_size)]))); + // cw_to_send_to_user = word_buffer; + // break; + // case ECHO_QSO_WORDS: + // //word_index = random(0,qso_size); // min parm is inclusive, max parm is exclusive + // strcpy_P(word_buffer, (char*)pgm_read_word(&(qso_table[random(0,qso_size)]))); + // cw_to_send_to_user = word_buffer; + // break; + // } //switch (practice_mode) + + loop2 = 1; + while (loop2) { + user_send_loop = 1; + user_sent_cw = ""; + cw_char = 0; + x = 0; + + // send the CW to the user + while ((x < (cw_to_send_to_user.length())) && (x < progressive_step_counter)) { + send_char(cw_to_send_to_user[x],KEYER_NORMAL); + // test + // port_to_use->print(cw_to_send_to_user[x]); + // + x++; + } + //port_to_use->println(); + + while (user_send_loop) { + // get their paddle input + + #ifdef FEATURE_DISPLAY + service_display(); + #endif + + #ifdef FEATURE_POTENTIOMETER + if (configuration.pot_activated) { + check_potentiometer(); + } + #endif + + #ifdef FEATURE_ROTARY_ENCODER + check_rotary_encoder(); + #endif //FEATURE_ROTARY_ENCODER + + check_paddles(); + + if (dit_buffer) { + sending_mode = MANUAL_SENDING; + send_dit(); + dit_buffer = 0; + paddle_hit = 1; + cw_char = (cw_char * 10) + 1; + last_element_time = millis(); + } + if (dah_buffer) { + sending_mode = MANUAL_SENDING; + send_dah(); + dah_buffer = 0; + paddle_hit = 1; + cw_char = (cw_char * 10) + 2; + last_element_time = millis(); + } + + // have we hit letterspace time (end of a letter?) + if ((paddle_hit) && (millis() > (last_element_time + (float(600/configuration.wpm) * length_letterspace)))) { + #ifdef DEBUG_PRACTICE_CMD_MODE + debug_serial_port->println(F("command_progressive_5_char_echo_practice: user_send_loop: hit length_letterspace")); + #endif + incoming_char = convert_cw_number_to_ascii(cw_char); + //port_to_use->print(incoming_char); + user_sent_cw.concat(incoming_char); + cw_char = 0; + paddle_hit = 0; + // TODO - print it to serial and lcd + } + + // do we have all the characters from the user? - if so, get out of user_send_loop + if ((user_sent_cw.length() >= cw_to_send_to_user.length()) || ((progressive_step_counter < 255) && (user_sent_cw.length() == progressive_step_counter))) { + user_send_loop = 0; + //port_to_use->println(); + } + + // does the user want to exit? + while (analogbuttonread(0)) { + user_send_loop = 0; + loop1 = 0; + loop2 = 0; + if (correct_answer_led) digitalWrite(correct_answer_led, LOW); // clear the LEDs as we exit + if (wrong_answer_led) digitalWrite(wrong_answer_led, LOW); + } + + } //while (user_send_loop) + + if (loop1 && loop2) { + if (progressive_step_counter < 255) { // we're in progressive mode + if (user_sent_cw.substring(0,progressive_step_counter) == cw_to_send_to_user.substring(0,progressive_step_counter)) { // we get here if the character entered is correct + if (progressive_step_counter < 6) { // if the step counter is less than 6 then we have not finished the 5 character string + if (correct_answer_led) digitalWrite(correct_answer_led, HIGH); // set the correct answer LED high + if (wrong_answer_led) digitalWrite(wrong_answer_led, LOW); // clear the wrong answer LED + beep(); // and beep + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS > 17) lcd_center_print_timed("Success! " + String(progressive_step_counter) + " correct", 0, default_display_msg_delay); + else lcd_center_print_timed("Success!", 0, default_display_msg_delay); + #endif // FEATURE_DISPLAY + send_char(' ',0); + send_char(' ',0); + progressive_step_counter++; + } // end if (progressive_step_counter < 6) + if (progressive_step_counter == 6) { // we get here if the five character string is correct + loop2 = 0; + if (correct_answer_led) digitalWrite(correct_answer_led, HIGH); // set the correct answer LED high + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("Success!", 0, default_display_msg_delay); + if (LCD_COLUMNS > 17) lcd_center_print_timed("5 characters correct", 1, default_display_msg_delay); + if (LCD_COLUMNS < 17) lcd_center_print_timed("5 char correct", 1, default_display_msg_delay); + #endif // FEATURE_DISPLAY + unsigned int NEWtone = 400; // the initial tone freuency for the tone sequence + unsigned int TONEduration = 50; // define the duration of each tone element in the tone sequence to drive a speaker + for (int k=0; k<6; k++) { // a loop to generate some increasing tones + tone(sidetone_line,NEWtone); // generate a tone on the speaker pin + delay(TONEduration); // hold the tone for the specified delay period + noTone(sidetone_line); // turn off the tone + NEWtone = NEWtone*1.25; // calculate a new value for the tone frequency + } // end for + send_char(' ',0); + send_char(' ',0); + if (correct_answer_led) digitalWrite(correct_answer_led, LOW); // clear the correct answer LED + } // end if (progressive_step_counter == 6) + } else { // we get here if the character entered is wrong + if (wrong_answer_led) digitalWrite(wrong_answer_led, HIGH); // set the wrong answer LED high + if (correct_answer_led) digitalWrite(correct_answer_led, LOW); // clear the correct answer LED + boop(); + send_char(' ',0); + send_char(' ',0); + } + } else { // I don't think we ever get here, unless the progressive_step_counter is more than 255, but it is reset as Loop1 starts + if (user_sent_cw == cw_to_send_to_user) { // correct answer + beep(); + send_char(' ',0); + send_char(' ',0); + loop2 = 0; + } else { // wrong answer + boop(); + send_char(' ',0); + send_char(' ',0); + } + } // if (progressive_step_counter < 255) + } // if (loop1 && loop2) + } //loop2 + } //loop1 + + speed_mode = speed_mode_before; + configuration.keyer_mode = keyer_mode_before; + paddle_echo_buffer = 0; +} +#endif //defined(FEATURE_COMMAND_MODE_PROGRESSIVE_5_CHAR_ECHO_PRACTICE) && defined(FEATURE_COMMAND_MODE) + +//------------------------------------------------------------------------------------------------------- + +#if defined(FEATURE_COMMAND_MODE) +void command_set_serial_number() { + + byte character_count = 0;; + int cw_char = 0; + byte number_sent = 0; + unsigned int repeat_value = 0; + byte error_flag = 0; + + for (character_count = 0; character_count < 4; character_count++) { + cw_char = get_cw_input_from_user(0); + number_sent = (convert_cw_number_to_ascii(cw_char) - 48); + if ((number_sent >= 0) && (number_sent < 10)) { + repeat_value = (repeat_value * 10) + number_sent; + } else { // we got a bad value + error_flag = 1; + character_count = 5; + } + } + + if (error_flag) { + boop(); + } else { + serial_number = repeat_value; + //config_dirty = 1; + beep(); + } +} +#endif + +//------------------------------------------------------------------------------------------------------- + +#ifdef FEATURE_MEMORIES +void command_set_mem_repeat_delay() { + + byte character_count = 0;; + int cw_char = 0; + byte number_sent = 0; + unsigned int repeat_value = 0; + byte error_flag = 0; + + for (character_count = 0; character_count < 4; character_count++) { + cw_char = get_cw_input_from_user(0); + number_sent = (convert_cw_number_to_ascii(cw_char) - 48); + if ((number_sent >= 0) && (number_sent < 10)) { + repeat_value = (repeat_value * 10) + number_sent; + } else { // we got a bad value + error_flag = 1; + character_count = 5; + } + } + + if (error_flag) { + boop(); + } else { + configuration.memory_repeat_time = repeat_value; + config_dirty = 1; + beep(); + } + +} +#endif //FEATURE_MEMORIES + +//------------------------------------------------------------------------------------------------------- + +void adjust_dah_to_dit_ratio(int adjustment) { + + if ((configuration.dah_to_dit_ratio + adjustment) > 150 && (configuration.dah_to_dit_ratio + adjustment) < 810) { + configuration.dah_to_dit_ratio = configuration.dah_to_dit_ratio + adjustment; + #ifdef FEATURE_DISPLAY + #ifdef OPTION_MORE_DISPLAY_MSGS + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("DDR:" + String(configuration.dah_to_dit_ratio), 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Dah/Dit: " + String(configuration.dah_to_dit_ratio), 0, default_display_msg_delay); + } + service_display(); + #endif + #endif + } + + config_dirty = 1; +} + +//------------------------------------------------------------------------------------------------------- + +#ifdef FEATURE_COMMAND_MODE +void command_keying_compensation_adjust() { + + byte looping = 1; + + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("Key Comp", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Adj Keying Comp", 0, default_display_msg_delay); + } + #endif + + while (looping) { + send_dit(); + send_dah(); + if (paddle_pin_read(paddle_left) == LOW) { + if (configuration.keying_compensation < 255){ + configuration.keying_compensation++; + } + } + if (paddle_pin_read(paddle_right) == LOW) { + if (configuration.keying_compensation > 0){ + configuration.keying_compensation--; + } + } + while ((paddle_pin_read(paddle_left) == LOW && paddle_pin_read(paddle_right) == LOW) || (analogbuttonread(0))) { // if paddles are squeezed or button0 pressed - exit + looping = 0; + } + + + #ifdef OPTION_WATCHDOG_TIMER + wdt_reset(); + #endif //OPTION_WATCHDOG_TIMER + + } + while (paddle_pin_read(paddle_left) == LOW || paddle_pin_read(paddle_right) == LOW || analogbuttonread(0) ) {} // wait for all lines to go high + dit_buffer = 0; + dah_buffer = 0; + config_dirty = 1; +} +#endif //FEATURE_COMMAND_MODE + + +//------------------------------------------------------------------------------------------------------- + +#ifdef FEATURE_COMMAND_MODE +void command_dah_to_dit_ratio_adjust() { + + byte looping = 1; + + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("Adj DTDR", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Adj dah to dit", 0, default_display_msg_delay); + } + #endif + + while (looping) { + send_dit(); + send_dah(); + if (paddle_pin_read(paddle_left) == LOW) { + adjust_dah_to_dit_ratio(10); + } + if (paddle_pin_read(paddle_right) == LOW) { + adjust_dah_to_dit_ratio(-10); + } + while ((paddle_pin_read(paddle_left) == LOW && paddle_pin_read(paddle_right) == LOW) || (analogbuttonread(0))) { // if paddles are squeezed or button0 pressed - exit + looping = 0; + } + + + #ifdef OPTION_WATCHDOG_TIMER + wdt_reset(); + #endif //OPTION_WATCHDOG_TIMER + + } + while (paddle_pin_read(paddle_left) == LOW || paddle_pin_read(paddle_right) == LOW || analogbuttonread(0) ) {} // wait for all lines to go high + dit_buffer = 0; + dah_buffer = 0; +} +#endif //FEATURE_COMMAND_MODE + +//------------------------------------------------------------------------------------------------------- + +#ifdef FEATURE_COMMAND_MODE +void command_weighting_adjust() { + + byte looping = 1; + + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("Weight", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Adj weighting", 0, default_display_msg_delay); + } + #endif + + while (looping) { + send_dit(); + send_dah(); + if (paddle_pin_read(paddle_left) == LOW) { + configuration.weighting = configuration.weighting + 1; + if (configuration.weighting > 90){configuration.weighting = 90;} + } + if (paddle_pin_read(paddle_right) == LOW) { + configuration.weighting = configuration.weighting - 1; + if (configuration.weighting < 10){configuration.weighting = 10;} + } + while ((paddle_pin_read(paddle_left) == LOW && paddle_pin_read(paddle_right) == LOW) || (analogbuttonread(0))) { // if paddles are squeezed or button0 pressed - exit + looping = 0; + } + + #ifdef OPTION_WATCHDOG_TIMER + wdt_reset(); + #endif //OPTION_WATCHDOG_TIMER + + } + while (paddle_pin_read(paddle_left) == LOW || paddle_pin_read(paddle_right) == LOW || analogbuttonread(0) ) {} // wait for all lines to go high + dit_buffer = 0; + dah_buffer = 0; +} +#endif //FEATURE_COMMAND_MODE + +//------------------------------------------------------------------------------------------------------- + +#ifdef FEATURE_COMMAND_MODE +void command_tuning_mode() { + + byte looping = 1; + byte latched = 0; + + + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("TuneMode", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Tune Mode", 0, default_display_msg_delay); + } + #endif + + + #if !defined(FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT) + send_char(command_mode_acknowledgement_character, 0); + #else + send_chars((char*)command_t_tune_mode); + #endif + + key_tx = 1; + while (looping) { + + #ifdef OPTION_WATCHDOG_TIMER + wdt_reset(); + #endif //OPTION_WATCHDOG_TIMER + + if (paddle_pin_read(paddle_left) == LOW) { + sending_mode = MANUAL_SENDING; + tx_and_sidetone_key(1); + ptt_key(); + latched = 0; + } else { + if (paddle_pin_read(paddle_left) == HIGH && latched == 0) { + sending_mode = MANUAL_SENDING; + tx_and_sidetone_key(0); + ptt_unkey(); + } + } + + if (paddle_pin_read(paddle_right) == LOW && latched == 0) { + latched = 1; + sending_mode = MANUAL_SENDING; + tx_and_sidetone_key(1); + ptt_key(); + while ((paddle_pin_read(paddle_right) == LOW) && (paddle_pin_read(paddle_left) == HIGH)) { + delay(10); + } + } else { + if ((paddle_pin_read(paddle_right) == LOW) && (latched)) { + latched = 0; + sending_mode = MANUAL_SENDING; + tx_and_sidetone_key(0); + ptt_unkey(); + while ((paddle_pin_read(paddle_right) == LOW) && (paddle_pin_read(paddle_left) == HIGH)) { + delay(10); + } + } + } + if ((analogbuttonread(0)) || ((paddle_pin_read(paddle_left) == LOW) && (paddle_pin_read(paddle_right) == LOW))) { // if paddles are squeezed or button0 pressed - exit + looping = 0; + } + + } + sending_mode = MANUAL_SENDING; + tx_and_sidetone_key(0); + ptt_unkey(); + while (paddle_pin_read(paddle_left) == LOW || paddle_pin_read(paddle_right) == LOW || analogbuttonread(0) ) {} // wait for all lines to go high + key_tx = 0; + send_dit(); + dit_buffer = 0; + dah_buffer = 0; +} +#endif //FEATURE_COMMAND_MODE + +//------------------------------------------------------------------------------------------------------- + + void sidetone_adj(int hz) { + + if (((configuration.hz_sidetone + hz) > sidetone_hz_limit_low) && ((configuration.hz_sidetone + hz) < sidetone_hz_limit_high)) { + configuration.hz_sidetone = configuration.hz_sidetone + hz; + config_dirty = 1; + #if defined(FEATURE_DISPLAY) && defined(OPTION_MORE_DISPLAY_MSGS) + if (LCD_COLUMNS < 9){ + lcd_center_print_timed(String(configuration.hz_sidetone) + " Hz", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Sidetone " + String(configuration.hz_sidetone) + " Hz", 0, default_display_msg_delay); + } + #endif + } + } + +//------------------------------------------------------------------------------------------------------- + +#if defined(FEATURE_COMMAND_MODE) && !defined(OPTION_SIDETONE_DIGITAL_OUTPUT_NO_SQUARE_WAVE) +void command_sidetone_freq_adj() { + + byte looping = 1; + + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed(String(configuration.hz_sidetone) + " Hz", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Sidetone " + String(configuration.hz_sidetone) + " Hz", 0, default_display_msg_delay); + } + #endif + + while (looping) { + tone(sidetone_line, configuration.hz_sidetone); + if (paddle_pin_read(paddle_left) == LOW) { + #ifdef FEATURE_DISPLAY + #ifdef OPTION_SWAP_PADDLE_PARAMETER_CHANGE_DIRECTION + sidetone_adj(-5); + #else + sidetone_adj(5); + #endif // OPTION_SWAP_PADDLE_PARAMETER_CHANGE_DIRECTION + if (LCD_COLUMNS < 9){ + lcd_center_print_timed(String(configuration.hz_sidetone) + " Hz", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Sidetone " + String(configuration.hz_sidetone) + " Hz", 0, default_display_msg_delay); + } + #else + #ifdef OPTION_SWAP_PADDLE_PARAMETER_CHANGE_DIRECTION + sidetone_adj(-1); + #else + sidetone_adj(1); + #endif // OPTION_SWAP_PADDLE_PARAMETER_CHANGE_DIRECTION + #endif // FEATURE_DISPLAY + delay(10); + } + if (paddle_pin_read(paddle_right) == LOW) { + #ifdef FEATURE_DISPLAY + #ifdef OPTION_SWAP_PADDLE_PARAMETER_CHANGE_DIRECTION + sidetone_adj(5); + #else + sidetone_adj(-5); + #endif // OPTION_SWAP_PADDLE_PARAMETER_CHANGE_DIRECTION + if (LCD_COLUMNS < 9){ + lcd_center_print_timed(String(configuration.hz_sidetone) + " Hz", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Sidetone " + String(configuration.hz_sidetone) + " Hz", 0, default_display_msg_delay); + } + #else + #ifdef OPTION_SWAP_PADDLE_PARAMETER_CHANGE_DIRECTION + sidetone_adj(1); + #else + sidetone_adj(-1); + #endif // OPTION_SWAP_PADDLE_PARAMETER_CHANGE_DIRECTION + #endif // FEATURE_DISPLAY + delay(10); + } + while ((paddle_pin_read(paddle_left) == LOW && paddle_pin_read(paddle_right) == LOW) || (analogbuttonread(0))) { // if paddles are squeezed or button0 pressed - exit + looping = 0; + } + + #ifdef OPTION_WATCHDOG_TIMER + wdt_reset(); + #endif //OPTION_WATCHDOG_TIMER + + } + while (paddle_pin_read(paddle_left) == LOW || paddle_pin_read(paddle_right) == LOW || analogbuttonread(0) ) {} // wait for all lines to go high + noTone(sidetone_line); +} +#endif //FEATURE_COMMAND_MODE + +//------------------------------------------------------------------------------------------------------- +#ifdef FEATURE_COMMAND_MODE +void command_speed_mode(byte mode) { + + byte looping = 1; + #ifndef FEATURE_DISPLAY + static char c[4]; + #endif + //static String wpm_string; + + if (mode == COMMAND_SPEED_MODE_KEYER_WPM){ + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("AdjSpeed", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Adjust Speed", 0, default_display_msg_delay); + } + #endif + keyer_machine_mode = KEYER_COMMAND_MODE_SPEED_OVERRIDE; + } else { + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("CmdSpeed", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Cmd Mode Speed", 0, default_display_msg_delay); + } + #endif + } + + while (looping) { + send_dit(); + if ((paddle_pin_read(paddle_left) == LOW)) { + if (mode == COMMAND_SPEED_MODE_KEYER_WPM) { + #ifdef OPTION_SWAP_PADDLE_PARAMETER_CHANGE_DIRECTION + speed_change(-1); + #else + speed_change(1); + #endif // OPTION_SWAP_PADDLE_PARAMETER_CHANGE_DIRECTION + } else { + #ifdef OPTION_SWAP_PADDLE_PARAMETER_CHANGE_DIRECTION + speed_change_command_mode(-1); + #else + speed_change_command_mode(1); + #endif // OPTION_SWAP_PADDLE_PARAMETER_CHANGE_DIRECTION + } // endif (mode == COMMAND_SPEED_MODE_KEYER_WPM) + } // end while looping + if ((paddle_pin_read(paddle_right) == LOW)) { + if (mode == COMMAND_SPEED_MODE_KEYER_WPM) { + #ifdef OPTION_SWAP_PADDLE_PARAMETER_CHANGE_DIRECTION + speed_change(1); + #else + speed_change(-1); + #endif // OPTION_SWAP_PADDLE_PARAMETER_CHANGE_DIRECTION + } else { + #ifdef OPTION_SWAP_PADDLE_PARAMETER_CHANGE_DIRECTION + speed_change_command_mode(1); + #else + speed_change_command_mode(-1); + #endif // OPTION_SWAP_PADDLE_PARAMETER_CHANGE_DIRECTION + } + } + while ((paddle_pin_read(paddle_left) == LOW && paddle_pin_read(paddle_right) == LOW) || (analogbuttonread(0) )) // if paddles are squeezed or button0 pressed - exit + { + looping = 0; + } + + #ifdef OPTION_WATCHDOG_TIMER + wdt_reset(); + #endif //OPTION_WATCHDOG_TIMER + + } + + keyer_machine_mode = KEYER_COMMAND_MODE; + + dit_buffer = 0; + dah_buffer = 0; + + while (paddle_pin_read(paddle_left) == LOW || paddle_pin_read(paddle_right) == LOW || analogbuttonread(0) ) {} // wait for all lines to go high + #ifndef FEATURE_DISPLAY + // announce speed in CW + if (mode == COMMAND_SPEED_MODE_KEYER_WPM){ + sprintf(c, "%d", configuration.wpm); + } else { + sprintf(c, "%d", configuration.wpm_command_mode); + } + send_char(' ',KEYER_NORMAL); + send_char(c[0],KEYER_NORMAL); + send_char(c[1],KEYER_NORMAL); + #endif + +} +#endif //FEATURE_COMMAND_MODE + +//------------------------------------------------------------------ + +#ifndef FEATURE_DISPLAY +void send_tx() { + + send_char('T',KEYER_NORMAL); + send_char('X',KEYER_NORMAL); +} +#endif + +//------------------------------------------------------------------ + +void switch_to_tx_silent(byte tx) { + + switch (tx) { + case 1: if ((ptt_tx_1) || (tx_key_line_1)) { configuration.current_ptt_line = ptt_tx_1; current_tx_key_line = tx_key_line_1; configuration.current_tx = 1; config_dirty = 1; } break; + case 2: if ((ptt_tx_2) || (tx_key_line_2)) { configuration.current_ptt_line = ptt_tx_2; current_tx_key_line = tx_key_line_2; configuration.current_tx = 2; config_dirty = 1; } break; + case 3: if ((ptt_tx_3) || (tx_key_line_3)) { configuration.current_ptt_line = ptt_tx_3; current_tx_key_line = tx_key_line_3; configuration.current_tx = 3; config_dirty = 1; } break; + case 4: if ((ptt_tx_4) || (tx_key_line_4)) { configuration.current_ptt_line = ptt_tx_4; current_tx_key_line = tx_key_line_4; configuration.current_tx = 4; config_dirty = 1; } break; + case 5: if ((ptt_tx_5) || (tx_key_line_5)) { configuration.current_ptt_line = ptt_tx_5; current_tx_key_line = tx_key_line_5; configuration.current_tx = 5; config_dirty = 1; } break; + case 6: if ((ptt_tx_6) || (tx_key_line_6)) { configuration.current_ptt_line = ptt_tx_6; current_tx_key_line = tx_key_line_6; configuration.current_tx = 6; config_dirty = 1; } break; + } + +} + +//------------------------------------------------------------------ +void switch_to_tx(byte tx) +{ + + #ifdef FEATURE_MEMORIES + repeat_memory = 255; + #endif + + #ifdef FEATURE_DISPLAY + switch (tx) { + case 1: if ((ptt_tx_1) || (tx_key_line_1)) { switch_to_tx_silent(1); lcd_center_print_timed("TX 1", 0, default_display_msg_delay); } break; + case 2: if ((ptt_tx_2) || (tx_key_line_2)) { switch_to_tx_silent(2); lcd_center_print_timed("TX 2", 0, default_display_msg_delay); } break; + case 3: if ((ptt_tx_3) || (tx_key_line_3)) { switch_to_tx_silent(3); lcd_center_print_timed("TX 3", 0, default_display_msg_delay); } break; + case 4: if ((ptt_tx_4) || (tx_key_line_4)) { switch_to_tx_silent(4); lcd_center_print_timed("TX 4", 0, default_display_msg_delay); } break; + case 5: if ((ptt_tx_5) || (tx_key_line_5)) { switch_to_tx_silent(5); lcd_center_print_timed("TX 5", 0, default_display_msg_delay); } break; + case 6: if ((ptt_tx_6) || (tx_key_line_6)) { switch_to_tx_silent(6); lcd_center_print_timed("TX 6", 0, default_display_msg_delay); } break; + } + #else + switch (tx) { + case 1: if ((ptt_tx_1) || (tx_key_line_1)) { switch_to_tx_silent(1); send_tx(); send_char('1',KEYER_NORMAL); } break; + case 2: if ((ptt_tx_2) || (tx_key_line_2)) { switch_to_tx_silent(2); send_tx(); send_char('2',KEYER_NORMAL); } break; + case 3: if ((ptt_tx_3) || (tx_key_line_3)) { switch_to_tx_silent(3); send_tx(); send_char('3',KEYER_NORMAL); } break; + case 4: if ((ptt_tx_4) || (tx_key_line_4)) { switch_to_tx_silent(4); send_tx(); send_char('4',KEYER_NORMAL); } break; + case 5: if ((ptt_tx_5) || (tx_key_line_5)) { switch_to_tx_silent(5); send_tx(); send_char('5',KEYER_NORMAL); } break; + case 6: if ((ptt_tx_6) || (tx_key_line_6)) { switch_to_tx_silent(6); send_tx(); send_char('6',KEYER_NORMAL); } break; + } + #endif +} + +//------------------------------------------------------------------ + +#if defined(FEATURE_MEMORIES) && defined(FEATURE_BUTTONS) +void check_the_memory_buttons() +{ + + byte analogbuttontemp = button_array.Pressed(); + if ((analogbuttontemp > 0) && (analogbuttontemp < (number_of_memories + 1)) && ((millis() - button_last_add_to_send_buffer_time) > 400)) { + add_to_send_buffer(SERIAL_SEND_BUFFER_MEMORY_NUMBER); + add_to_send_buffer(analogbuttontemp - 1); + button_last_add_to_send_buffer_time = millis(); + } +} +#endif + +//------------------------------------------------------------------ + +void initialize_analog_button_array() { +#ifdef FEATURE_BUTTONS + + #ifdef DEBUG_BUTTONS + debug_serial_port->print("initialize_analog_button_array: "); + #endif + + #if defined(FEATURE_DL2SBA_BANKSWITCH) + button_array.Add(0,0); + button_array.Add(1,3); + button_array.Add(2,2); + button_array.Add(3,1); + button_array.Add(4,9); + button_array.Add(5,8); + button_array.Add(6,7); + button_array.Add(7,6); + button_array.Add(8,5); + button_array.Add(9,4); + + #elseif defined(OPTION_DFROBOT_LCD_COMMAND_BUTTONS) + button_array.Add(0,dfrobot_btnSELECT, dfrobot_btnLEFT_analog, dfrobot_btnSELECT_analog); + button_array.Add(1,dfrobot_btnLEFT, dfrobot_btnDOWN_analog, dfrobot_btnLEFT_analog); + button_array.Add(2,dfrobot_btnDOWN, dfrobot_btnUP_analog, dfrobot_btnDOWN_analog); + button_array.Add(3,dfrobot_btnUP, dfrobot_btnRIGHT_analog, dfrobot_btnUP_analog); + button_array.Add(4,dfrobot_btnRIGHT, 0, dfrobot_btnRIGHT_analog); + #else //FEATURE_DL2SBA_BANKSWITCH + button_array.AddAll(); + + + #endif //FEATURE_DL2SBA_BANKSWITCH +#endif //FEATURE_BUTTONS +} + +//------------------------------------------------------------------ +#ifdef FEATURE_BUTTONS +byte analogbuttonread(byte button_number) { + + if (button_array.Pressed(button_number)) { + return 1; + } + return 0; +} +#endif + +//------------------------------------------------------------------ + +#ifdef FEATURE_BUTTONS +void check_buttons() { + + + #ifdef DEBUG_LOOP + debug_serial_port->println(F("loop: entering check_buttons")); + #endif + + static long last_button_action = 0; + int analogbuttontemp = button_array.Pressed(); + long button_depress_time; + byte paddle_was_hit = 0; + byte previous_sidetone_mode = 0; + + if (analogbuttontemp < 0 ) { // no button pressed. + return; + } + + #ifdef FEATURE_MEMORIES + repeat_memory = 255; + #endif + + button_depress_time = button_array.last_pressed_ms; + + while (button_array.Held(analogbuttontemp, button_depress_time + 1000)) { + if ((paddle_pin_read(paddle_left) == LOW) || (paddle_pin_read(paddle_right) == LOW)) { + button_depress_time = 1001; // if button 0 is held and a paddle gets hit, assume we have a hold and shortcut out + } + } + if ((millis() - button_depress_time) < 500) { // regular button press + #ifdef FEATURE_COMMAND_MODE + if (analogbuttontemp == 0) { + + command_mode_disable_tx = !key_tx; //Added to sync the Command Mode entry state to actual key_tx state in case changed by CLI or keyboard (WD9DMP) + key_tx = 0; + command_mode(); + if (command_mode_disable_tx) { + //key_tx = !store_key_tx; //Inverting pre-command mode state seems to cause Command Mode sync issues (WD9DMP) + key_tx = 0; //Added this line to explicitly disable key_tx if command_mode_disable_tx is set after exiting Command Mode (WD9DMP) + } else { + key_tx = 1; + } + + } + #endif //FEATURE_COMMAND_MODE + #ifdef FEATURE_MEMORIES + if ((analogbuttontemp > 0) && (analogbuttontemp < (number_of_memories + 1)) && ((millis() - button_last_add_to_send_buffer_time) > 400)) { + + #ifdef FEATURE_WINKEY_EMULATION + #ifndef OPTION_WINKEY_2_SUPPORT + add_to_send_buffer(SERIAL_SEND_BUFFER_MEMORY_NUMBER); + add_to_send_buffer(analogbuttontemp - 1); + #else //OPTION_WINKEY_2_SUPPORT + if ((winkey_host_open) && (wk2_mode == 2)) { // if winkey is open and in wk2 mode, tell it about the button press + byte winkey_byte_to_send = 0xc8; + switch(analogbuttontemp) { + case 1: winkey_byte_to_send = winkey_byte_to_send | 1; break; + case 2: winkey_byte_to_send = winkey_byte_to_send | 2; break; + case 3: winkey_byte_to_send = winkey_byte_to_send | 4; break; + case 4: winkey_byte_to_send = winkey_byte_to_send | 16; break; + } + winkey_port_write(winkey_byte_to_send,0); + winkey_port_write(0xc8,0); // tell it that the button is unpressed + } else { // otherwise, have the buttons act as normal + add_to_send_buffer(SERIAL_SEND_BUFFER_MEMORY_NUMBER); + add_to_send_buffer(analogbuttontemp - 1); + } + #endif //OPTION_WINKEY_2_SUPPORT + #else //FEATURE_WINKEY_EMULATION + add_to_send_buffer(SERIAL_SEND_BUFFER_MEMORY_NUMBER); + add_to_send_buffer(analogbuttontemp - 1); + #endif //FEATURE_WINKEY_EMULATION + + button_last_add_to_send_buffer_time = millis(); + #ifdef DEBUG_BUTTONS + debug_serial_port->print(F("\ncheck_buttons: add_to_send_buffer: ")); + debug_serial_port->println(analogbuttontemp - 1); + #endif //DEBUG_BUTTONS + } + #endif //ifdef FEATURE_MEMORIES + + } else { //if ((millis() - button_depress_time) < 500) -- Button hold down + + if (analogbuttontemp == 0) { + key_tx = 0; + // do stuff if this is a command button hold down + while (button_array.Held(analogbuttontemp)) { + if (paddle_pin_read(paddle_left) == LOW) { + #ifdef OPTION_SWAP_PADDLE_PARAMETER_CHANGE_DIRECTION + speed_change(-1); // left paddle decrease speed + #else + speed_change(1); // left paddle increase speed + #endif // OPTION_SWAP_PADDLE_PARAMETER_CHANGE_DIRECTION + previous_sidetone_mode = configuration.sidetone_mode; + configuration.sidetone_mode = SIDETONE_ON; + sending_mode = MANUAL_SENDING; + send_dit(); + configuration.sidetone_mode = previous_sidetone_mode; + //speed_button_cmd_executed = 1; + dit_buffer = 0; + + #ifdef DEBUG_BUTTONS + #ifdef OPTION_SWAP_PADDLE_PARAMETER_CHANGE_DIRECTION + debug_serial_port->println(F("\ncheck_buttons: speed_change(-1)")); + #else + debug_serial_port->println(F("\ncheck_buttons: speed_change(1)")); + #endif // OPTION_SWAP_PADDLE_PARAMETER_CHANGE_DIRECTION + #endif // DEBUG_BUTTONS + + #if defined(FEATURE_WINKEY_EMULATION) && defined(FEATURE_POTENTIOMETER) + if ((primary_serial_port_mode == SERIAL_WINKEY_EMULATION) && (winkey_host_open)) { + winkey_port_write(((configuration.wpm-pot_wpm_low_value)|128),0); + winkey_last_unbuffered_speed_wpm = configuration.wpm; + } + #endif + + } + if (paddle_pin_read(paddle_right) == LOW) { + #ifdef OPTION_SWAP_PADDLE_PARAMETER_CHANGE_DIRECTION + speed_change(1); // right paddle increase speed + #else + speed_change(-1); // right paddle decrease speed + #endif + previous_sidetone_mode = configuration.sidetone_mode; + configuration.sidetone_mode = SIDETONE_ON; + sending_mode = MANUAL_SENDING; + send_dah(); + configuration.sidetone_mode = previous_sidetone_mode; + dah_buffer = 0; + + #ifdef DEBUG_BUTTONS + #ifdef OPTION_SWAP_PADDLE_PARAMETER_CHANGE_DIRECTION + debug_serial_port->println(F("\ncheck_buttons: speed_change(1)")); + #else + debug_serial_port->println(F("\ncheck_buttons: speed_change(-1)")); + #endif // OPTION_SWAP_PADDLE_PARAMETER_CHANGE_DIRECTION + #endif // DEBUG_BUTTONS + + #if defined(FEATURE_WINKEY_EMULATION) && defined(FEATURE_POTENTIOMETER) + if ((primary_serial_port_mode == SERIAL_WINKEY_EMULATION) && (winkey_host_open)) { + winkey_port_write(((configuration.wpm-pot_wpm_low_value)|128),0); + winkey_last_unbuffered_speed_wpm = configuration.wpm; + } + #endif + } //if (paddle_pin_read(paddle_right) == LOW) { + } + key_tx = 1; + } // (analogbuttontemp == 0) + if ((analogbuttontemp > 0) && (analogbuttontemp < analog_buttons_number_of_buttons)) { + while (button_array.Held(analogbuttontemp)) { + if (((paddle_pin_read(paddle_left) == LOW) || (paddle_pin_read(paddle_right) == LOW)) && (analogbuttontemp < (number_of_memories + 1))){ + #ifdef FEATURE_MEMORIES + repeat_memory = analogbuttontemp - 1; + last_memory_repeat_time = 0; + #ifdef DEBUG_BUTTONS + debug_serial_port->print(F("\ncheck_buttons: repeat_memory:")); + debug_serial_port->println(repeat_memory); + #endif //DEBUG_BUTTONS + #endif + paddle_was_hit = 1; + } + } //while (button_array.Held(analogbuttontemp)) { + + if (!paddle_was_hit) { // if no paddle was hit, this was a button hold to change transmitters + key_tx = 0; + previous_sidetone_mode = configuration.sidetone_mode; + configuration.sidetone_mode = SIDETONE_ON; + switch_to_tx(analogbuttontemp); + key_tx = 1; + configuration.sidetone_mode = previous_sidetone_mode; + } + } //if ((analogbuttontemp > 0) && (analogbuttontemp < analog_buttons_number_of_buttons)) { + //} // button hold + } + last_button_action = millis(); + + #ifdef FEATURE_SLEEP + last_activity_time = millis(); + #endif //FEATURE_SLEEP + #ifdef FEATURE_LCD_BACKLIGHT_AUTO_DIM + last_active_time = millis(); + #endif //FEATURE_LCD_BACKLIGHT_AUTO_DIM + +} +#endif // FEATURE_BUTTONS + +//------------------------------------------------------------------------------------------------------- + +void service_dit_dah_buffers() +{ + #ifdef DEBUG_LOOP + debug_serial_port->println(F("loop: entering service_dit_dah_buffers")); + #endif + + if (keyer_machine_mode == BEACON){return;} + + if (automatic_sending_interruption_time != 0){ + if ((millis() - automatic_sending_interruption_time) > (configuration.paddle_interruption_quiet_time_element_lengths*(1200/configuration.wpm))){ + automatic_sending_interruption_time = 0; + sending_mode = MANUAL_SENDING; + } else { + dit_buffer = 0; + dah_buffer = 0; + return; + } + } + + static byte bug_dah_flag = 0; + + #ifdef FEATURE_PADDLE_ECHO + static unsigned long bug_dah_key_down_time = 0; + #endif //FEATURE_PADDLE_ECHO + + + if ((configuration.keyer_mode == IAMBIC_A) || (configuration.keyer_mode == IAMBIC_B) || (configuration.keyer_mode == ULTIMATIC) || (configuration.keyer_mode == SINGLE_PADDLE)) { + if ((configuration.keyer_mode == IAMBIC_A) && (iambic_flag) && (paddle_pin_read(paddle_left)) && (paddle_pin_read(paddle_right))) { + iambic_flag = 0; + dit_buffer = 0; + dah_buffer = 0; + } else { + if (dit_buffer) { + dit_buffer = 0; + sending_mode = MANUAL_SENDING; + send_dit(); + } + if (dah_buffer) { + dah_buffer = 0; + sending_mode = MANUAL_SENDING; + send_dah(); + } + } + } else { + if (configuration.keyer_mode == BUG) { + if (dit_buffer) { + dit_buffer = 0; + sending_mode = MANUAL_SENDING; + send_dit(); + } + + if (dah_buffer) { + dah_buffer = 0; + if (!bug_dah_flag) { + sending_mode = MANUAL_SENDING; + tx_and_sidetone_key(1); + bug_dah_flag = 1; + #ifdef FEATURE_PADDLE_ECHO + bug_dah_key_down_time = millis(); + #endif //FEATURE_PADDLE_ECHO + } + + + + } else { + if (bug_dah_flag){ + sending_mode = MANUAL_SENDING; + tx_and_sidetone_key(0); + #ifdef FEATURE_PADDLE_ECHO + if ((millis() - bug_dah_key_down_time) > (0.5 * (1200.0/configuration.wpm))){ + if ((millis() - bug_dah_key_down_time) > (2 * (1200.0/configuration.wpm))){ + paddle_echo_buffer = (paddle_echo_buffer * 10) + 2; + } else { + paddle_echo_buffer = (paddle_echo_buffer * 10) + 1; + } + paddle_echo_buffer_decode_time = millis() + (((float)3000.0/(float)configuration.wpm) * ((float)configuration.cw_echo_timing_factor/(float)100)); + } + #endif //FEATURE_PADDLE_ECHO + bug_dah_flag = 0; + } + } + #ifdef FEATURE_DEAD_OP_WATCHDOG + dah_counter = 0; + #endif + } else { + if (configuration.keyer_mode == STRAIGHT) { + if (dit_buffer) { + dit_buffer = 0; + sending_mode = MANUAL_SENDING; + tx_and_sidetone_key(1); + } else { + sending_mode = MANUAL_SENDING; + tx_and_sidetone_key(0); + } + #ifdef FEATURE_DEAD_OP_WATCHDOG + dit_counter = 0; + #endif + } + } + } + +} + +//------------------------------------------------------------------------------------------------------- + +void beep() +{ + #if !defined(OPTION_SIDETONE_DIGITAL_OUTPUT_NO_SQUARE_WAVE) + // #if defined(FEATURE_SINEWAVE_SIDETONE) + // tone(sidetone_line, hz_high_beep); + // delay(200); + // noTone(sidetone_line); + // #else + tone(sidetone_line, hz_high_beep, 200); + // #endif + #else + if (sidetone_line) { + digitalWrite(sidetone_line, sidetone_line_active_state); + delay(200); + digitalWrite(sidetone_line, sidetone_line_inactive_state); + } + #endif +} + +//------------------------------------------------------------------------------------------------------- + +void boop() +{ + #if !defined(OPTION_SIDETONE_DIGITAL_OUTPUT_NO_SQUARE_WAVE) + tone(sidetone_line, hz_low_beep); + delay(100); + noTone(sidetone_line); + #else + if (sidetone_line) { + digitalWrite(sidetone_line, sidetone_line_active_state); + delay(100); + digitalWrite(sidetone_line, sidetone_line_inactive_state); + } + #endif +} + +//------------------------------------------------------------------------------------------------------- + +void beep_boop() +{ + #if !defined(OPTION_SIDETONE_DIGITAL_OUTPUT_NO_SQUARE_WAVE) + tone(sidetone_line, hz_high_beep); + delay(100); + tone(sidetone_line, hz_low_beep); + delay(100); + noTone(sidetone_line); + #else + if (sidetone_line) { + digitalWrite(sidetone_line, sidetone_line_active_state); + delay(200); + digitalWrite(sidetone_line, sidetone_line_inactive_state); + } + #endif +} + +//------------------------------------------------------------------------------------------------------- + +void boop_beep() +{ + #if !defined(OPTION_SIDETONE_DIGITAL_OUTPUT_NO_SQUARE_WAVE) + tone(sidetone_line, hz_low_beep); + delay(100); + tone(sidetone_line, hz_high_beep); + delay(100); + noTone(sidetone_line); + #else + if (sidetone_line) { + digitalWrite(sidetone_line, sidetone_line_active_state); + delay(200); + digitalWrite(sidetone_line, sidetone_line_inactive_state); + } + #endif +} + + + +//------------------------------------------------------------------------------------------------------- +void send_the_dits_and_dahs(char const * cw_to_send){ + + + /* American Morse - Special Symbols + + ~ long dah (4 units) + + = very long dah (5 units) + + & an extra space (1 unit) + + */ + + //debug_serial_port->println(F("send_the_dits_and_dahs()")); + + sending_mode = AUTOMATIC_SENDING; + + + #if defined(FEATURE_SERIAL) && !defined(OPTION_DISABLE_SERIAL_PORT_CHECKING_WHILE_SENDING_CW) + dump_current_character_flag = 0; + #endif + + #if defined(FEATURE_FARNSWORTH) + float additional_intercharacter_time_ms; + #endif + + for (int x = 0;x < 12;x++){ + switch(cw_to_send[x]){ + case '.': send_dit(); break; + case '-': send_dah(); break; + #if defined(FEATURE_AMERICAN_MORSE) // this is a bit of a hack, but who cares! :-) + case '~': + + being_sent = SENDING_DAH; + tx_and_sidetone_key(1); + if ((tx_key_dah) && (key_tx)) {digitalWrite(tx_key_dah,tx_key_dit_and_dah_pins_active_state);} + loop_element_lengths((float(4.0)*(float(configuration.weighting)/50)),configuration.keying_compensation,configuration.wpm); + if ((tx_key_dah) && (key_tx)) {digitalWrite(tx_key_dah,tx_key_dit_and_dah_pins_inactive_state);} + tx_and_sidetone_key(0); + loop_element_lengths((4.0-(3.0*(float(configuration.weighting)/50))),(-1.0*configuration.keying_compensation),configuration.wpm); + break; + + case '=': + being_sent = SENDING_DAH; + tx_and_sidetone_key(1); + if ((tx_key_dah) && (key_tx)) {digitalWrite(tx_key_dah,tx_key_dit_and_dah_pins_active_state);} + loop_element_lengths((float(5.0)*(float(configuration.weighting)/50)),configuration.keying_compensation,configuration.wpm); + if ((tx_key_dah) && (key_tx)) {digitalWrite(tx_key_dah,tx_key_dit_and_dah_pins_inactive_state);} + tx_and_sidetone_key(0); + loop_element_lengths((4.0-(3.0*(float(configuration.weighting)/50))),(-1.0*configuration.keying_compensation),configuration.wpm); + break; + + case '&': + loop_element_lengths((4.0-(3.0*(float(configuration.weighting)/50))),(-1.0*configuration.keying_compensation),configuration.wpm); + break; + #endif //FEATURE_AMERICAN_MORSE + default: + //return; + x = 12; + break; + } + + if ((dit_buffer || dah_buffer || sending_mode == AUTOMATIC_SENDING_INTERRUPTED) && (keyer_machine_mode != BEACON)){ + dit_buffer = 0; + dah_buffer = 0; + //debug_serial_port->println(F("send_the_dits_and_dahs: AUTOMATIC_SENDING_INTERRUPTED")); + //return; + x = 12; + } + #if defined(FEATURE_SERIAL) + check_serial(); + #if !defined(OPTION_DISABLE_SERIAL_PORT_CHECKING_WHILE_SENDING_CW) + if (dump_current_character_flag){ + x = 12; + } + #endif + #endif + + #ifdef OPTION_WATCHDOG_TIMER + wdt_reset(); + #endif //OPTION_WATCHDOG_TIMER + + } // for (int x = 0;x < 12;x++) + + +} + +//------------------------------------------------------------------------------------------------------- + +void send_char(byte cw_char, byte omit_letterspace) +{ + #ifdef DEBUG_SEND_CHAR + debug_serial_port->print(F("send_char: called with cw_char:")); + debug_serial_port->print((byte)cw_char); + if (omit_letterspace) { + debug_serial_port->print(F(" OMIT_LETTERSPACE")); + } + debug_serial_port->println(); + #endif + + + #ifdef FEATURE_SLEEP + last_activity_time = millis(); + #endif //FEATURE_SLEEP + #ifdef FEATURE_LCD_BACKLIGHT_AUTO_DIM + last_active_time = millis(); + #endif //FEATURE_LCD_BACKLIGHT_AUTO_DIM + + if ((cw_char == 10) || (cw_char == 13)) { return; } // don't attempt to send carriage return or line feed + + sending_mode = AUTOMATIC_SENDING; + + if (char_send_mode == CW) { + switch (cw_char) { + case 'A': send_the_dits_and_dahs(".-"); break; + case 'B': send_the_dits_and_dahs("-..."); break; + case 'C': send_the_dits_and_dahs("-.-."); break; + case 'D': send_the_dits_and_dahs("-.."); break; + case 'E': send_the_dits_and_dahs("."); break; + case 'F': send_the_dits_and_dahs("..-."); break; + case 'G': send_the_dits_and_dahs("--."); break; + case 'H': send_the_dits_and_dahs("...."); break; + case 'I': send_the_dits_and_dahs(".."); break; + case 'J': send_the_dits_and_dahs(".---"); break; + case 'K': send_the_dits_and_dahs("-.-"); break; + case 'L': send_the_dits_and_dahs(".-.."); break; + case 'M': send_the_dits_and_dahs("--"); break; + case 'N': send_the_dits_and_dahs("-."); break; + case 'O': send_the_dits_and_dahs("---"); break; + case 'P': send_the_dits_and_dahs(".--."); break; + case 'Q': send_the_dits_and_dahs("--.-"); break; + case 'R': send_the_dits_and_dahs(".-."); break; + case 'S': send_the_dits_and_dahs("..."); break; + case 'T': send_the_dits_and_dahs("-"); break; + case 'U': send_the_dits_and_dahs("..-"); break; + case 'V': send_the_dits_and_dahs("...-"); break; + case 'W': send_the_dits_and_dahs(".--"); break; + case 'X': send_the_dits_and_dahs("-..-"); break; + case 'Y': send_the_dits_and_dahs("-.--"); break; + case 'Z': send_the_dits_and_dahs("--.."); break; + + case '0': send_the_dits_and_dahs("-----");break; + case '1': send_the_dits_and_dahs(".----");break; + case '2': send_the_dits_and_dahs("..---");break; + case '3': send_the_dits_and_dahs("...--");break; + case '4': send_the_dits_and_dahs("....-");break; + case '5': send_the_dits_and_dahs(".....");break; + case '6': send_the_dits_and_dahs("-....");break; + case '7': send_the_dits_and_dahs("--...");break; + case '8': send_the_dits_and_dahs("---..");break; + case '9': send_the_dits_and_dahs("----.");break; + + case '=': send_the_dits_and_dahs("-...-"); break; + case '/': send_the_dits_and_dahs("-..-."); break; + case '*': send_the_dits_and_dahs("-...-.-"); break; + case '.': send_the_dits_and_dahs(".-.-.-"); break; + case ',': send_the_dits_and_dahs("--..--"); break; + case '!': send_the_dits_and_dahs("--..--"); break; //sp5iou 20180328 + //case '!': send_the_dits_and_dahs("-.-.--");break;//sp5iou 20180328 + case '\'': send_the_dits_and_dahs(".----."); break; // apostrophe + case '(': send_the_dits_and_dahs("-.--."); break; + case ')': send_the_dits_and_dahs("-.--.-"); break; + case '&': send_the_dits_and_dahs(".-..."); break; + //case '&': send_dit(); loop_element_lengths(3); send_dits(3); break; + + case '+': send_the_dits_and_dahs(".-.-."); break; + case '-': send_the_dits_and_dahs("-....-"); break; + case '_': send_the_dits_and_dahs("..--.-"); break; + case '"': send_the_dits_and_dahs(".-..-."); break; + case '$': send_the_dits_and_dahs("...-..-"); break; + case '@': send_the_dits_and_dahs(".--.-."); break; + case '<': send_the_dits_and_dahs(".-.-."); break; // AR + case '>': send_the_dits_and_dahs("...-.-"); break; // SK + + + + + + #if defined(OPTION_WINKEY_PROSIGN_COMPATIBILITY) + case 0x5C: send_the_dits_and_dahs("-..-."); break; // Backslash + case '[': send_the_dits_and_dahs(".-..."); break; + case ':': send_the_dits_and_dahs("-.--."); break; + case ';': send_the_dits_and_dahs(".-.-"); break; + case ']': send_the_dits_and_dahs("-.--."); break; + #else + case ':': send_the_dits_and_dahs("---..."); break; + case ';': send_the_dits_and_dahs("-.-.-."); break; + + #endif + + + case ' ': + loop_element_lengths((configuration.length_wordspace-length_letterspace-2),0,configuration.wpm); + break; + + #ifdef OPTION_RUSSIAN_LANGUAGE_SEND_CLI // Contributed by Павел Бирюков, UA1AQC + case 192: send_the_dits_and_dahs(".-");break; //Ð + case 193: send_the_dits_and_dahs("-...");break; //Б + case 194: send_the_dits_and_dahs(".--");break; //Ð’ + case 195: send_the_dits_and_dahs("--.");break; //Г + case 196: send_the_dits_and_dahs("-..");break; //Д + case 197: send_the_dits_and_dahs(".");break; //Е + case 168: send_the_dits_and_dahs(".");break; //Ð + case 184: send_the_dits_and_dahs(".");break; //Ñ‘ + case 198: send_the_dits_and_dahs("...-");break; //Ж + case 199: send_the_dits_and_dahs("--..");break; //З + case 200: send_the_dits_and_dahs("..");break; //И + case 201: send_the_dits_and_dahs(".---");break; //Й + case 202: send_the_dits_and_dahs("-.-");break; //К + case 203: send_the_dits_and_dahs(".-..");break; //Л + case 204: send_the_dits_and_dahs("--");break; //Ðœ + case 205: send_the_dits_and_dahs("-.");break; //Ð + case 206: send_the_dits_and_dahs("---");break; //О + case 207: send_the_dits_and_dahs(".--.");break; //П + case 208: send_the_dits_and_dahs(".-.");break; //Р + case 209: send_the_dits_and_dahs("...");break; //С + case 210: send_the_dits_and_dahs("-");break; //Т + case 211: send_the_dits_and_dahs("..-");break; //У + case 212: send_the_dits_and_dahs("..-.");break; //Ф + case 213: send_the_dits_and_dahs("....");break; //Ð¥ + case 214: send_the_dits_and_dahs("-.-.");break; //Ц + case 215: send_the_dits_and_dahs("---.");break; //Ч + case 216: send_the_dits_and_dahs("----");break; //Ш + case 217: send_the_dits_and_dahs("--.-");break; //Щ + case 218: send_the_dits_and_dahs("--.--");break; //Ъ + case 219: send_the_dits_and_dahs("-.--");break; //Ы + case 220: send_the_dits_and_dahs("-..-");break; //Ь + case 221: send_the_dits_and_dahs("..-..");break; //Э + case 222: send_the_dits_and_dahs("..--");break; //Ю + case 223: send_the_dits_and_dahs(".-.-");break; //Я + case 255: send_the_dits_and_dahs(".-.-");break; //Ñ + #endif //OPTION_RUSSIAN_LANGUAGE_SEND_CLI + + case '\n': break; + case '\r': break; + + #if defined(OPTION_PROSIGN_SUPPORT) + case PROSIGN_AA: send_the_dits_and_dahs(".-.-");break; + case PROSIGN_AS: send_the_dits_and_dahs(".-...");break; + case PROSIGN_BK: send_the_dits_and_dahs("-...-.-");break; + case PROSIGN_CL: send_the_dits_and_dahs("-.-..-..");break; + case PROSIGN_CT: send_the_dits_and_dahs("-.-.-");break; + case PROSIGN_KN: send_the_dits_and_dahs("-.--.");break; + case PROSIGN_NJ: send_the_dits_and_dahs("-..---");break; + case PROSIGN_SK: send_the_dits_and_dahs("...-.-");break; + case PROSIGN_SN: send_the_dits_and_dahs("...-.");break; + case PROSIGN_HH: send_the_dits_and_dahs("........");break; // iz0rus + #endif + + #ifdef OPTION_NON_ENGLISH_EXTENSIONS + case 192: send_the_dits_and_dahs(".--.-");break;// 'À' + case 194: send_the_dits_and_dahs(".-.-");break;// 'Â' + case 197: send_the_dits_and_dahs(".--.-");break;// 'Ã…' + case 196: send_the_dits_and_dahs(".-.-");break;// 'Ä' + case 198: send_the_dits_and_dahs(".-.-");break;// 'Æ' + case 199: send_the_dits_and_dahs("-.-..");break;// 'Ç' + case 208: send_the_dits_and_dahs("..--.");break;// 'Ã' + case 138: send_the_dits_and_dahs("----");break;// 'Å ' + case 200: send_the_dits_and_dahs(".-..-");break;// 'È' + case 201: send_the_dits_and_dahs("..-..");break;// 'É' + case 142: send_the_dits_and_dahs("--..-.");break;// 'Ž' + case 209: send_the_dits_and_dahs("--.--");break;// 'Ñ' + case 214: send_the_dits_and_dahs("---.");break;// 'Ö' + case 216: send_the_dits_and_dahs("---.");break;// 'Ø' + case 211: send_the_dits_and_dahs("---.");break;// 'Ó' + case 220: send_the_dits_and_dahs("..--");break;// 'Ãœ' + case 223: send_the_dits_and_dahs("------");break;// 'ß' + + // for English/Japanese font LCD controller which has a few European characters also (HD44780UA00) (LA3ZA code) + case 225: send_the_dits_and_dahs(".-.-");break;// 'ä' LA3ZA + case 239: send_the_dits_and_dahs("---.");break;// 'ö' LA3ZA + case 242: send_the_dits_and_dahs("---.");break;// 'ø' LA3ZA + case 245: send_the_dits_and_dahs("..--");break;// 'ü' LA3ZA + case 246: send_the_dits_and_dahs("----");break;// almost 'Š' or rather sigma LA3ZA + case 252: send_the_dits_and_dahs(".--.-");break;// Ã¥ (sort of) LA3ZA + case 238: send_the_dits_and_dahs("--.--");break;// 'ñ' LA3ZA + case 226: send_the_dits_and_dahs("------");break;// 'ß' LA3ZA + #endif //OPTION_NON_ENGLISH_EXTENSIONS + + case '|': + #if !defined(OPTION_WINKEY_DO_NOT_SEND_7C_BYTE_HALF_SPACE) + loop_element_lengths(0.5,0,configuration.wpm); + #endif + return; + break; + + #if defined(OPTION_DO_NOT_SEND_UNKNOWN_CHAR_QUESTION) + case '?': send_the_dits_and_dahs("..--..");break; + #endif + + default: + #if !defined(OPTION_DO_NOT_SEND_UNKNOWN_CHAR_QUESTION) + send_the_dits_and_dahs("..--.."); + #endif + break; + + } + if (omit_letterspace != OMIT_LETTERSPACE) { + + loop_element_lengths((length_letterspace-1),0,configuration.wpm); //this is minus one because send_dit and send_dah have a trailing element space + + } + + #ifdef FEATURE_FARNSWORTH + // Farnsworth Timing : http://www.arrl.org/files/file/Technology/x9004008.pdf + if (configuration.wpm_farnsworth > configuration.wpm){ + float additional_intercharacter_time_ms = ((( (1.0 * farnsworth_timing_calibration) * ((60.0 * float(configuration.wpm_farnsworth) ) - (37.2 * float(configuration.wpm) ))/( float(configuration.wpm) * float(configuration.wpm_farnsworth) ))/19.0)*1000.0) - (1200.0/ float(configuration.wpm_farnsworth) ); + loop_element_lengths(1,additional_intercharacter_time_ms,0);} + #endif + + } else { + if (char_send_mode == HELL){ + #ifdef FEATURE_HELL + transmit_hell_char(cw_char); + #endif + } else { + if (char_send_mode == AMERICAN_MORSE){ + #ifdef FEATURE_AMERICAN_MORSE + + /* + + ~ long dah (4 units) + + = very long dah (5 units) + + & an extra space (1 unit) + + */ + + + switch (cw_char){ // THIS SECTION IS AMERICAN MORSE CODE - DO NOT TOUCH IT ! + + case 'A': send_the_dits_and_dahs(".-");break; + case 'B': send_the_dits_and_dahs("-...");break; + case 'C': send_the_dits_and_dahs("..&.");break; + case 'D': send_the_dits_and_dahs("-..");break; + case 'E': send_the_dits_and_dahs(".");break; + case 'F': send_the_dits_and_dahs(".-.");break; + case 'G': send_the_dits_and_dahs("--.");break; + case 'H': send_the_dits_and_dahs("....");break; + case 'I': send_the_dits_and_dahs("..");break; + case 'J': send_the_dits_and_dahs("-.-.");break; + case 'K': send_the_dits_and_dahs("-.-");break; + case 'L': send_the_dits_and_dahs("~");break; + case 'M': send_the_dits_and_dahs("--");break; + case 'N': send_the_dits_and_dahs("-.");break; + case 'O': send_the_dits_and_dahs(".&.");break; + case 'P': send_the_dits_and_dahs(".....");break; + case 'Q': send_the_dits_and_dahs("..-.");break; + case 'R': send_the_dits_and_dahs(".&..");break; + case 'S': send_the_dits_and_dahs("...");break; + case 'T': send_the_dits_and_dahs("-");break; + case 'U': send_the_dits_and_dahs("..-");break; + case 'V': send_the_dits_and_dahs("...-");break; + case 'W': send_the_dits_and_dahs(".--");break; + case 'X': send_the_dits_and_dahs(".-..");break; + case 'Y': send_the_dits_and_dahs("..&..");break; + case 'Z': send_the_dits_and_dahs("...&.");break; + + // THIS SECTION IS AMERICAN MORSE CODE - DO NOT TOUCH IT ! + + case '&': send_the_dits_and_dahs(".&...");break; + + case '0': send_the_dits_and_dahs("=");break; + case '1': send_the_dits_and_dahs(".---.");break; + case '2': send_the_dits_and_dahs("..--..");break; + case '3': send_the_dits_and_dahs("...-.");break; + case '4': send_the_dits_and_dahs("....-");break; + case '5': send_the_dits_and_dahs("---");break; + case '6': send_the_dits_and_dahs("......");break; + case '7': send_the_dits_and_dahs("--..");break; + case '8': send_the_dits_and_dahs("-....");break; + case '9': send_the_dits_and_dahs("-..-");break; + + // THIS SECTION IS AMERICAN MORSE CODE - DO NOT TOUCH IT ! + + case ',': send_the_dits_and_dahs(".-.-");break; + case '.': send_the_dits_and_dahs("..--..");break; + case '?': send_the_dits_and_dahs("-..-.");break; + case '!': send_the_dits_and_dahs("---.");break; + case ':': send_the_dits_and_dahs("-.-&.&.");break; + case ';': send_the_dits_and_dahs("...&..");break; + case '-': send_the_dits_and_dahs("....&.-..");break; + + } //switch (cw_char) + + #endif + } + } + } + +} + +//------------------------------------------------------------------------------------------------------- + +int uppercase (int charbytein) +{ + if (((charbytein > 96) && (charbytein < 123)) || ((charbytein > 223) && (charbytein < 255))) { + charbytein = charbytein - 32; + } + if (charbytein == 158) { charbytein = 142; } // ž -> Ž + if (charbytein == 154) { charbytein = 138; } // Å¡ -> Å  + + return charbytein; +} + +//------------------------------------------------------------------------------------------------------- +#if defined(FEATURE_SERIAL) +#ifdef FEATURE_COMMAND_LINE_INTERFACE +void serial_qrss_mode() +{ + byte looping = 1; + byte incoming_serial_byte; + byte numbers[4]; + byte numberindex = 0; + String numberstring; + byte error =0; + + while (looping) { + if (primary_serial_port->available() == 0) { // wait for the next keystroke + if (keyer_machine_mode == KEYER_NORMAL) { // might as well do something while we're waiting + check_paddles(); + service_dit_dah_buffers(); + //check_the_memory_buttons(); + } + } else { + + incoming_serial_byte = primary_serial_port->read(); + if ((incoming_serial_byte > 47) && (incoming_serial_byte < 58)) { // ascii 48-57 = "0" - "9") + numberstring = numberstring + incoming_serial_byte; + numbers[numberindex] = incoming_serial_byte; +// primary_serial_port->write("numberindex:"); +// primary_serial_port->print(numberindex,DEC); +// primary_serial_port->write(" numbers:"); +// primary_serial_port->println(numbers[numberindex],DEC); + numberindex++; + if (numberindex > 2) + { + looping = 0; + error = 1; + } + } else { + if (incoming_serial_byte == 13) { // carriage return - get out + looping = 0; + } else { // bogus input - error out + looping = 0; + error = 1; + } + } + } + } + + if (error) { + primary_serial_port->println(F("Error...")); + while (primary_serial_port->available() > 0) { incoming_serial_byte = primary_serial_port->read(); } // clear out buffer + return; + } else { + primary_serial_port->print(F("Setting keyer to QRSS Mode. Dit length: ")); + primary_serial_port->print(numberstring); + primary_serial_port->println(F(" seconds")); + int y = 1; + int set_dit_length = 0; + for (int x = (numberindex - 1); x >= 0 ; x = x - 1) { + set_dit_length = set_dit_length + ((numbers[x]-48) * y); + y = y * 10; + } + qrss_dit_length = set_dit_length; + speed_mode = SPEED_QRSS; + } + +} +#endif +#endif +//------------------------------------------------------------------------------------------------------- + +void service_send_buffer(byte no_print) +{ + // send one character out of the send buffer + + + #ifdef DEBUG_LOOP + debug_serial_port->println(F("loop: entering service_send_buffer")); + #endif + + static unsigned long timed_command_end_time; + static byte timed_command_in_progress = 0; + + #if defined(DEBUG_SERVICE_SEND_BUFFER) + + byte no_bytes_flag = 0; + + if (send_buffer_bytes > 0){ + debug_serial_port->print("service_send_buffer: enter:"); + for (int x = 0;x < send_buffer_bytes;x++){ + debug_serial_port->write(send_buffer_array[x]); + debug_serial_port->print("["); + debug_serial_port->print(send_buffer_array[x]); + debug_serial_port->print("]"); + } + debug_serial_port->println(); + } else { + no_bytes_flag = 1; + } + Serial.flush(); + #endif + + + if (service_tx_inhibit_and_pause() == 1){ + #if defined(DEBUG_SERVICE_SEND_BUFFER) + debug_serial_port->println("service_send_buffer: tx_inhib"); + #endif + return; + + } + + + #ifdef FEATURE_MEMORIES + play_memory_prempt = 0; + #endif + + + + + if (send_buffer_status == SERIAL_SEND_BUFFER_NORMAL) { + if ((send_buffer_bytes) && (pause_sending_buffer == 0)) { + #ifdef FEATURE_SLEEP + last_activity_time = millis(); + #endif //FEATURE_SLEEP + #ifdef FEATURE_LCD_BACKLIGHT_AUTO_DIM + last_active_time = millis(); + #endif //FEATURE_LCD_BACKLIGHT_AUTO_DIM + + if ((send_buffer_array[0] > SERIAL_SEND_BUFFER_SPECIAL_START) && (send_buffer_array[0] < SERIAL_SEND_BUFFER_SPECIAL_END)) { + + #if defined(DEBUG_SERVICE_SEND_BUFFER) + debug_serial_port->println("service_send_buffer: SERIAL_SEND_BUFFER_SPECIAL"); + #endif + + + if (send_buffer_array[0] == SERIAL_SEND_BUFFER_HOLD_SEND) { + + #if defined(DEBUG_SERVICE_SEND_BUFFER) + debug_serial_port->println("service_send_buffer: SERIAL_SEND_BUFFER_HOLD_SEND"); + #endif + + send_buffer_status = SERIAL_SEND_BUFFER_HOLD; + remove_from_send_buffer(); + } + + if (send_buffer_array[0] == SERIAL_SEND_BUFFER_HOLD_SEND_RELEASE) { + + #if defined(DEBUG_SERVICE_SEND_BUFFER) + debug_serial_port->println("service_send_buffer: SERIAL_SEND_BUFFER_HOLD_SEND_RELEASE"); + #endif + + remove_from_send_buffer(); + } + + if (send_buffer_array[0] == SERIAL_SEND_BUFFER_MEMORY_NUMBER) { + + #if defined(DEBUG_SERVICE_SEND_BUFFER) + debug_serial_port->println("service_send_buffer: SERIAL_SEND_BUFFER_MEMORY_NUMBER"); + #endif + + #ifdef DEBUG_SEND_BUFFER + debug_serial_port->println(F("service_send_buffer: SERIAL_SEND_BUFFER_MEMORY_NUMBER")); + #endif + #ifdef FEATURE_WINKEY_EMULATION + if (winkey_sending && winkey_host_open) { + #if !defined(OPTION_WINKEY_UCXLOG_SUPRESS_C4_STATUS_BYTE) + winkey_port_write(0xc0|winkey_sending|winkey_xoff,0); + #endif + winkey_interrupted = 1; + } + #endif + remove_from_send_buffer(); + if (send_buffer_bytes) { + if (send_buffer_array[0] < number_of_memories) { + #ifdef FEATURE_MEMORIES + play_memory(send_buffer_array[0]); + #endif + } + remove_from_send_buffer(); + } + } + + if (send_buffer_array[0] == SERIAL_SEND_BUFFER_WPM_CHANGE) { // two bytes for wpm + //remove_from_send_buffer(); + #if defined(DEBUG_SERVICE_SEND_BUFFER) + debug_serial_port->println("service_send_buffer: SERIAL_SEND_BUFFER_WPM_CHANGE"); + #endif + if (send_buffer_bytes > 2) { + #if defined(DEBUG_SERVICE_SEND_BUFFER) + debug_serial_port->println("service_send_buffer: SERIAL_SEND_BUFFER_WPM_CHANGE: send_buffer_bytes>2"); + #endif + remove_from_send_buffer(); + #ifdef FEATURE_WINKEY_EMULATION + if ((winkey_host_open) && (winkey_speed_state == WINKEY_UNBUFFERED_SPEED)){ + winkey_speed_state = WINKEY_BUFFERED_SPEED; + #if defined(DEBUG_SERVICE_SEND_BUFFER) + debug_serial_port->println("service_send_buffer: winkey_speed_state = WINKEY_BUFFERED_SPEED"); + #endif + winkey_last_unbuffered_speed_wpm = configuration.wpm; + } + #endif + configuration.wpm = send_buffer_array[0] * 256; + remove_from_send_buffer(); + configuration.wpm = configuration.wpm + send_buffer_array[0]; + remove_from_send_buffer(); + + #ifdef FEATURE_LED_RING + update_led_ring(); + #endif //FEATURE_LED_RING + + } else { + #if defined(DEBUG_SERVICE_SEND_BUFFER) + debug_serial_port->println("service_send_buffer:SERIAL_SEND_BUFFER_WPM_CHANGE < 2"); + #endif + } + + #if defined(DEBUG_SERVICE_SEND_BUFFER) + debug_serial_port->print("service_send_buffer: SERIAL_SEND_BUFFER_WPM_CHANGE: exit send_buffer_bytes:"); + debug_serial_port->println(send_buffer_bytes); + #endif + + } + + if (send_buffer_array[0] == SERIAL_SEND_BUFFER_TX_CHANGE) { // one byte for transmitter # + + + #if defined(DEBUG_SERVICE_SEND_BUFFER) + debug_serial_port->println("service_send_buffer: SERIAL_SEND_BUFFER_TX_CHANGE"); + #endif + + remove_from_send_buffer(); + if (send_buffer_bytes > 1) { + // if ((send_buffer_array[0] > 0) && (send_buffer_array[0] < 7)){ + // switch_to_tx_silent(send_buffer_array[0]); + // } + #ifdef FEATURE_SO2R_BASE + if ((send_buffer_array[0] > 0) && (send_buffer_array[0] < 3)){ + if (ptt_line_activated) { + so2r_pending_tx = send_buffer_array[0]; + } + else { + so2r_tx = send_buffer_array[0]; + so2r_set_tx(); + } + } + #else + if ((send_buffer_array[0] > 0) && (send_buffer_array[0] < 7)){ + switch_to_tx_silent(send_buffer_array[0]); + } + #endif //FEATURE_SO2R_BASE + + remove_from_send_buffer(); + } + } + + if (send_buffer_array[0] == SERIAL_SEND_BUFFER_NULL) { + + #if defined(DEBUG_SERVICE_SEND_BUFFER) + debug_serial_port->println("service_send_buffer: SERIAL_SEND_BUFFER_NULL"); + #endif + + remove_from_send_buffer(); + } + + if (send_buffer_array[0] == SERIAL_SEND_BUFFER_PROSIGN) { + + #if defined(DEBUG_SERVICE_SEND_BUFFER) + debug_serial_port->println("service_send_buffer: SERIAL_SEND_BUFFER_PROSIGN"); + #endif + + remove_from_send_buffer(); + if (send_buffer_bytes) { + send_char(send_buffer_array[0],OMIT_LETTERSPACE); + #ifdef FEATURE_WINKEY_EMULATION + if (winkey_host_open){ + // Must echo back PROSIGN characters sent N6TV + winkey_port_write(0xc4|winkey_sending|winkey_xoff,0); // N6TV + winkey_port_write(send_buffer_array[0],0); // N6TV + } + #endif //FEATURE_WINKEY_EMULATION + remove_from_send_buffer(); + } + if (send_buffer_bytes) { + send_char(send_buffer_array[0],KEYER_NORMAL); + #ifdef FEATURE_WINKEY_EMULATION + if (winkey_host_open){ + // Must echo back PROSIGN characters sent N6TV + winkey_port_write(0xc4|winkey_sending|winkey_xoff,0); // N6TV + winkey_port_write(send_buffer_array[0],0); // N6TV + } + #endif //FEATURE_WINKEY_EMULATION + remove_from_send_buffer(); + } + } + + if (send_buffer_array[0] == SERIAL_SEND_BUFFER_TIMED_KEY_DOWN) { + + #if defined(DEBUG_SERVICE_SEND_BUFFER) + debug_serial_port->println("service_send_buffer: SERIAL_SEND_BUFFER_TIMED_KEY_DOWN"); + #endif + + remove_from_send_buffer(); + if (send_buffer_bytes) { + send_buffer_status = SERIAL_SEND_BUFFER_TIMED_COMMAND; + sending_mode = AUTOMATIC_SENDING; + tx_and_sidetone_key(1); + timed_command_end_time = millis() + (send_buffer_array[0] * 1000); + timed_command_in_progress = SERIAL_SEND_BUFFER_TIMED_KEY_DOWN; + remove_from_send_buffer(); + } + } + + if (send_buffer_array[0] == SERIAL_SEND_BUFFER_TIMED_WAIT) { + + + #if defined(DEBUG_SERVICE_SEND_BUFFER) + debug_serial_port->println("service_send_buffer: SERIAL_SEND_BUFFER_TIMED_WAIT"); + #endif + + remove_from_send_buffer(); + if (send_buffer_bytes) { + send_buffer_status = SERIAL_SEND_BUFFER_TIMED_COMMAND; + timed_command_end_time = millis() + (send_buffer_array[0] * 1000); + timed_command_in_progress = SERIAL_SEND_BUFFER_TIMED_WAIT; + remove_from_send_buffer(); + } + } + + if (send_buffer_array[0] == SERIAL_SEND_BUFFER_PTT_ON) { + + #if defined(DEBUG_SERVICE_SEND_BUFFER) + debug_serial_port->println("service_send_buffer: SERIAL_SEND_BUFFER_PTT_ON"); + #endif + + remove_from_send_buffer(); + manual_ptt_invoke = 1; + ptt_key(); + } + + if (send_buffer_array[0] == SERIAL_SEND_BUFFER_PTT_OFF) { + + #if defined(DEBUG_SERVICE_SEND_BUFFER) + debug_serial_port->println("service_send_buffer: SERIAL_SEND_BUFFER_PTT_OFF"); + #endif + + remove_from_send_buffer(); + manual_ptt_invoke = 0; + ptt_unkey(); + } + + } else { // if ((send_buffer_array[0] > SERIAL_SEND_BUFFER_SPECIAL_START) && (send_buffer_array[0] < SERIAL_SEND_BUFFER_SPECIAL_END)) + + // #ifdef FEATURE_WINKEY_EMULATION + // if ((primary_serial_port_mode == SERIAL_WINKEY_EMULATION) && (winkey_serial_echo) && (winkey_host_open) && (!no_print) && (!cw_send_echo_inhibit)){ + // #if defined(OPTION_WINKEY_ECHO_7C_BYTE) + // if (send_buffer_array[0] > 30) {winkey_port_write(send_buffer_array[0],0);} + // #else + // if ((send_buffer_array[0]!= 0x7C) && (send_buffer_array[0] > 30)) {winkey_port_write(send_buffer_array[0],0);} + // #endif + // } + // #endif //FEATURE_WINKEY_EMULATION + + + #if defined(FEATURE_COMMAND_LINE_INTERFACE) + if ((!no_print) && (!cw_send_echo_inhibit)){ + if (primary_serial_port_mode == SERIAL_CLI) {primary_serial_port->write(send_buffer_array[0]);}; + #ifdef FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + secondary_serial_port->write(send_buffer_array[0]); + #endif //FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + if (send_buffer_array[0] == 13) { + if (primary_serial_port_mode == SERIAL_CLI) {primary_serial_port->write(10);} // if we got a carriage return, also send a line feed + #ifdef FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + secondary_serial_port->write(10); + #endif //FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + } + } + #endif //FEATURE_COMMAND_LINE_INTERFACE + + #ifdef FEATURE_DISPLAY + if (lcd_send_echo) { + display_scroll_print_char(send_buffer_array[0]); + service_display(); + } + #endif //FEATURE_DISPLAY + + #if defined(DEBUG_SERVICE_SEND_BUFFER) + debug_serial_port->print("service_send_buffer: send_char:"); + debug_serial_port->write(send_buffer_array[0]); + debug_serial_port->println(); + Serial.flush(); + #endif + + send_char(send_buffer_array[0],KEYER_NORMAL); //**************** + + #ifdef FEATURE_WINKEY_EMULATION + if ((primary_serial_port_mode == SERIAL_WINKEY_EMULATION) && (winkey_serial_echo) && (winkey_host_open) && (!no_print) && (!cw_send_echo_inhibit)){ + #if defined(OPTION_WINKEY_ECHO_7C_BYTE) + if (send_buffer_array[0] > 30) {winkey_port_write(send_buffer_array[0],0);} + #else + if ((send_buffer_array[0]!= 0x7C) && (send_buffer_array[0] > 30)) {winkey_port_write(send_buffer_array[0],0);} + #endif + } + #endif //FEATURE_WINKEY_EMULATION + + if (!pause_sending_buffer){ + + #if defined(DEBUG_SERVICE_SEND_BUFFER) + debug_serial_port->println("service_send_buffer: after send_char: remove_from_send_buffer"); + if (no_bytes_flag){ + debug_serial_port->println("service_send_buffer: no_bytes_flag"); + } + Serial.flush(); + #endif + + if (!((send_buffer_array[0] > SERIAL_SEND_BUFFER_SPECIAL_START) && (send_buffer_array[0] < SERIAL_SEND_BUFFER_SPECIAL_END))){ // this is a friggin hack to fix something I can't explain with SO2R - Goody 20191217 + remove_from_send_buffer(); + + #if defined(DEBUG_SERVICE_SEND_BUFFER) + debug_serial_port->println("service_send_buffer: after send_char: remove_from_send_buffer"); + if (no_bytes_flag){ + debug_serial_port->println("service_send_buffer: no_bytes_flag"); + } + Serial.flush(); + #endif + + + } else { + + #if defined(DEBUG_SERVICE_SEND_BUFFER) + debug_serial_port->println("service_send_buffer: snagged errant remove_from_send_buffer"); + Serial.flush(); + #endif + } + + } + } + } //if ((send_buffer_bytes) && (pause_sending_buffer == 0)) + + } else { //if (send_buffer_status == SERIAL_SEND_BUFFER_NORMAL) + + if (send_buffer_status == SERIAL_SEND_BUFFER_TIMED_COMMAND) { // we're in a timed command + + if ((timed_command_in_progress == SERIAL_SEND_BUFFER_TIMED_KEY_DOWN) && (millis() > timed_command_end_time)) { + sending_mode = AUTOMATIC_SENDING; + tx_and_sidetone_key(0); + timed_command_in_progress = 0; + send_buffer_status = SERIAL_SEND_BUFFER_NORMAL; + + #if defined(DEBUG_SERVICE_SEND_BUFFER) + debug_serial_port->println("service_send_buffer: SERIAL_SEND_BUFFER_TIMED_KEY_DOWN->SERIAL_SEND_BUFFER_NORMAL"); + #endif + + } + + if ((timed_command_in_progress == SERIAL_SEND_BUFFER_TIMED_WAIT) && (millis() > timed_command_end_time)) { + timed_command_in_progress = 0; + send_buffer_status = SERIAL_SEND_BUFFER_NORMAL; + + #if defined(DEBUG_SERVICE_SEND_BUFFER) + debug_serial_port->println("service_send_buffer: SERIAL_SEND_BUFFER_TIMED_WAIT->SERIAL_SEND_BUFFER_NORMAL"); + #endif + + } + + } + + if (send_buffer_status == SERIAL_SEND_BUFFER_HOLD) { // we're in a send hold ; see if there's a SERIAL_SEND_BUFFER_HOLD_SEND_RELEASE in the buffer + if (send_buffer_bytes == 0) { + send_buffer_status = SERIAL_SEND_BUFFER_NORMAL; // this should never happen, but what the hell, we'll catch it here if it ever does happen + } else { + for (int z = 0; z < send_buffer_bytes; z++) { + if (send_buffer_array[z] == SERIAL_SEND_BUFFER_HOLD_SEND_RELEASE) { + send_buffer_status = SERIAL_SEND_BUFFER_NORMAL; + z = send_buffer_bytes; + } + } + } + } + + } //if (send_buffer_status == SERIAL_SEND_BUFFER_NORMAL) + + //if the paddles are hit, dump the buffer + check_paddles(); + if (((dit_buffer || dah_buffer) && (send_buffer_bytes)) && (keyer_machine_mode != BEACON)) { + + #if defined(DEBUG_SERVICE_SEND_BUFFER) + debug_serial_port->println("service_send_buffer: buffer dump"); + #endif + + clear_send_buffer(); + send_buffer_status = SERIAL_SEND_BUFFER_NORMAL; + dit_buffer = 0; + dah_buffer = 0; + #ifdef FEATURE_MEMORIES + repeat_memory = 255; + #endif + #ifdef FEATURE_WINKEY_EMULATION + if (winkey_sending && winkey_host_open) { + winkey_port_write(0xc2|winkey_sending|winkey_xoff,0); // 0xc2 - BREAKIN bit set high + winkey_interrupted = 1; + } + #endif + } + +} + +//------------------------------------------------------------------------------------------------------- +void clear_send_buffer() +{ + #ifdef FEATURE_WINKEY_EMULATION + winkey_xoff=0; + #endif + send_buffer_bytes = 0; +} + +//------------------------------------------------------------------------------------------------------- +void remove_from_send_buffer() +{ + + #ifdef FEATURE_WINKEY_EMULATION + if ((send_buffer_bytes < winkey_xon_threshold) && winkey_xoff && winkey_host_open) { + winkey_xoff=0; + winkey_port_write(0xc0|winkey_sending|winkey_xoff,0); //send status /XOFF + } + #endif + + if (send_buffer_bytes) { + send_buffer_bytes--; + } + if (send_buffer_bytes) { + for (int x = 0;x < send_buffer_bytes;x++) { + send_buffer_array[x] = send_buffer_array[x+1]; + } + #if defined(FEATURE_WINKEY_EMULATION) && defined(OPTION_WINKEY_FREQUENT_STATUS_REPORT) + winkey_port_write(0xc0|winkey_sending|winkey_xoff,0); + #endif + } + + #if defined(DEBUG_SERVICE_SEND_BUFFER) + debug_serial_port->print("remove_from_send_buffer: send_buffer_bytes:"); + debug_serial_port->println(send_buffer_bytes); + debug_serial_port->print("send_buffer:"); + for (int x = 0;x < send_buffer_bytes;x++){ + debug_serial_port->write(send_buffer_array[x]); + debug_serial_port->print("["); + debug_serial_port->print(send_buffer_array[x]); + debug_serial_port->print("]"); + } + debug_serial_port->println(); + #endif + +} + +//------------------------------------------------------------------------------------------------------- + +void add_to_send_buffer(byte incoming_serial_byte) +{ + + if (send_buffer_bytes < send_buffer_size) { + if (incoming_serial_byte != 127) { + send_buffer_bytes++; + send_buffer_array[send_buffer_bytes - 1] = incoming_serial_byte; + + #ifdef FEATURE_WINKEY_EMULATION + if ((send_buffer_bytes>winkey_xoff_threshold) && winkey_host_open) { + winkey_xoff=1; + winkey_port_write(0xc0|winkey_sending|winkey_xoff,0); //send XOFF status + } + #endif + + } else { // we got a backspace + if (send_buffer_bytes){ + send_buffer_bytes--; + } + } + + #if defined(DEBUG_SERVICE_SEND_BUFFER) + debug_serial_port->print("add_to_send_buffer: "); + debug_serial_port->write(incoming_serial_byte); + debug_serial_port->print(" ["); + debug_serial_port->print(incoming_serial_byte); + debug_serial_port->println("]"); + debug_serial_port->print("send_buffer:"); + for (int x = 0;x < send_buffer_bytes;x++){ + debug_serial_port->write(send_buffer_array[x]); + debug_serial_port->print("["); + debug_serial_port->print(send_buffer_array[x]); + debug_serial_port->print("]"); + } + debug_serial_port->println(); + #endif + + + } else { + + #if defined(DEBUG_SERVICE_SEND_BUFFER) + debug_serial_port->println("add_to_send_buffer: !send_buffer_bytes < send_buffer_size"); + #endif + + } + +} +//------------------------------------------------------------------------------------------------------- + +/* + +10 CLS +20 PRINT "HELLO" +30 GOTO 20 + +*/ + + +//------------------------------------------------------------------------------------------------------- + +#ifdef FEATURE_WINKEY_EMULATION +void winkey_unbuffered_speed_command(byte incoming_serial_byte) { + + if (incoming_serial_byte == 0) { + #ifdef FEATURE_POTENTIOMETER + configuration.pot_activated = 1; + #endif + } else { + configuration.wpm = incoming_serial_byte; + winkey_speed_state = WINKEY_UNBUFFERED_SPEED; + winkey_last_unbuffered_speed_wpm = configuration.wpm; + //calculate_element_length(); + #ifdef OPTION_WINKEY_STRICT_EEPROM_WRITES_MAY_WEAR_OUT_EEPROM + config_dirty = 1; + #endif + + #ifdef FEATURE_LED_RING + update_led_ring(); + #endif //FEATURE_LED_RING + + } + +} +#endif //FEATURE_WINKEY_EMULATION + +//------------------------------------------------------------------------------------------------------- +#ifdef FEATURE_WINKEY_EMULATION +void winkey_farnsworth_command(byte incoming_serial_byte) { + + #ifdef FEATURE_FARNSWORTH + if ((incoming_serial_byte > 9) && (incoming_serial_byte < 100)) { + configuration.wpm_farnsworth = incoming_serial_byte; + } + #else + (void)incoming_serial_byte; // to get rid of compiler warning about unused variable + #endif //FEATURE_FFARNSWORTH + +} +#endif //FEATURE_WINKEY_EMULATION + +//------------------------------------------------------------------------------------------------------- +#ifdef FEATURE_WINKEY_EMULATION +void winkey_keying_compensation_command(byte incoming_serial_byte) { + + configuration.keying_compensation = incoming_serial_byte; +} +#endif //FEATURE_WINKEY_EMULATION + +//------------------------------------------------------------------------------------------------------- +#ifdef FEATURE_WINKEY_EMULATION +void winkey_first_extension_command(byte incoming_serial_byte) { + + first_extension_time = incoming_serial_byte; + #ifdef DEBUG_WINKEY_PROTOCOL_USING_CW + send_char('X',KEYER_NORMAL); + #endif +} +#endif //FEATURE_WINKEY_EMULATION + +//------------------------------------------------------------------------------------------------------- +#ifdef FEATURE_WINKEY_EMULATION +void winkey_dah_to_dit_ratio_command(byte incoming_serial_byte) { + + if ((incoming_serial_byte > 32) && (incoming_serial_byte < 67)) { + configuration.dah_to_dit_ratio = (300*(float(incoming_serial_byte)/50)); + #ifdef OPTION_WINKEY_STRICT_EEPROM_WRITES_MAY_WEAR_OUT_EEPROM + config_dirty = 1; + #endif + } + +} +#endif //FEATURE_WINKEY_EMULATION + +//------------------------------------------------------------------------------------------------------- +#ifdef FEATURE_WINKEY_EMULATION +void winkey_weighting_command(byte incoming_serial_byte) { + + if ((incoming_serial_byte > 9) && (incoming_serial_byte < 91)) { + configuration.weighting = incoming_serial_byte; + } + +} +#endif //FEATURE_WINKEY_EMULATION +//------------------------------------------------------------------------------------------------------- +#ifdef FEATURE_WINKEY_EMULATION +void winkey_ptt_times_parm1_command(byte incoming_serial_byte) { + #if !defined(DEBUG_WINKEY_DISABLE_LEAD_IN_TIME_SETTING) + configuration.ptt_lead_time[configuration.current_tx-1] = (incoming_serial_byte*10); + #else + configuration.ptt_lead_time[configuration.current_tx-1] = 0; + #endif + #ifdef DEBUG_WINKEY_PROTOCOL_USING_CW + send_char('P',KEYER_NORMAL); + send_char('1',KEYER_NORMAL); + #endif +} +#endif //FEATURE_WINKEY_EMULATION +//------------------------------------------------------------------------------------------------------- +#ifdef FEATURE_WINKEY_EMULATION +void winkey_ptt_times_parm2_command(byte incoming_serial_byte) { + + configuration.ptt_tail_time[configuration.current_tx-1] = (3*int(1200/configuration.wpm)) + (incoming_serial_byte*10); + winkey_session_ptt_tail = incoming_serial_byte; + #ifdef DEBUG_WINKEY_PROTOCOL_USING_CW + send_char('P',KEYER_NORMAL); + send_char('2',KEYER_NORMAL); + #endif +} +#endif //FEATURE_WINKEY_EMULATION +//------------------------------------------------------------------------------------------------------- +#ifdef FEATURE_WINKEY_EMULATION +void winkey_set_pot_parm1_command(byte incoming_serial_byte) { + + pot_wpm_low_value = incoming_serial_byte; + +} +#endif //FEATURE_WINKEY_EMULATION +//------------------------------------------------------------------------------------------------------- +#ifdef FEATURE_WINKEY_EMULATION +void winkey_set_pot_parm2_command(byte incoming_serial_byte) { + + #ifdef FEATURE_POTENTIOMETER + pot_wpm_high_value = (pot_wpm_low_value + incoming_serial_byte); + #else + (void)incoming_serial_byte; // to get rid of compiler warning about unused variable + #endif +} +#endif //FEATURE_WINKEY_EMULATION +//------------------------------------------------------------------------------------------------------- +#ifdef FEATURE_WINKEY_EMULATION +void winkey_set_pot_parm3_command (byte incoming_serial_byte) { + + #ifdef FEATURE_POTENTIOMETER + #ifdef OPTION_WINKEY_2_SUPPORT + pot_full_scale_reading = 1031; + #else //OPTION_WINKEY_2_SUPPORT + if (incoming_serial_byte == 255) { + pot_full_scale_reading = 1031; + } else { + if (incoming_serial_byte == 127) { + pot_full_scale_reading = 515; + } + } + #endif //OPTION_WINKEY_2_SUPPORT + configuration.pot_activated = 1; + #else + (void)incoming_serial_byte; // to get rid of compiler warning about unused variable + #endif +} +#endif //FEATURE_WINKEY_EMULATION + +//------------------------------------------------------------------------------------------------------- +#ifdef FEATURE_WINKEY_EMULATION +void winkey_setmode_command(byte incoming_serial_byte) { + + + config_dirty = 1; + + if (incoming_serial_byte & 4) { //serial echo enable + #ifdef DEBUG_WINKEY_PROTOCOL_USING_CW + send_char('S',KEYER_NORMAL); + #endif + winkey_serial_echo = 1; + } else { + winkey_serial_echo = 0; + } + if (incoming_serial_byte & 8) { //paddle_swap + configuration.paddle_mode = PADDLE_REVERSE; + } else { + configuration.paddle_mode = PADDLE_NORMAL; + } + switch (incoming_serial_byte & 48) { + case 0: configuration.keyer_mode = IAMBIC_B; + #ifdef DEBUG_WINKEY_PROTOCOL_USING_CW + send_char('B',KEYER_NORMAL); + #endif + break; + case 16: configuration.keyer_mode = IAMBIC_A; + #ifdef DEBUG_WINKEY_PROTOCOL_USING_CW + send_char('A',KEYER_NORMAL); + #endif + break; + case 32: configuration.keyer_mode = ULTIMATIC; + #ifdef DEBUG_WINKEY_PROTOCOL_USING_CW + send_char('U',KEYER_NORMAL); + #endif + break; + case 48: configuration.keyer_mode = BUG; + #ifdef DEBUG_WINKEY_PROTOCOL_USING_CW + send_char('G',KEYER_NORMAL); + #endif + break; + } + #ifdef FEATURE_DEAD_OP_WATCHDOG + if ((incoming_serial_byte & 128) == 128) { //1xxxxxxx = paddle watchdog (1 = disable) + dead_op_watchdog_active = 0; + } else { + dead_op_watchdog_active = 1; + } + #endif + #ifdef FEATURE_AUTOSPACE + if ((incoming_serial_byte & 2) == 2) { //xxxxxx1x = autospace + configuration.autospace_active = 1; + #ifdef DEBUG_WINKEY_PROTOCOL_USING_CW + send_char('T',KEYER_NORMAL); + #endif + } else { + configuration.autospace_active = 0; + } + #endif + if ((incoming_serial_byte & 1) == 1) { //xxxxxxx1 = contest wordspace + configuration.length_wordspace = 6; + } else { + configuration.length_wordspace = 7; + } + + if ((incoming_serial_byte & 64) == 64) { //x1xxxxxx = paddle echo + winkey_paddle_echo_activated = 1; + } else { + winkey_paddle_echo_activated = 0; + } + +} + +#endif //FEATURE_WINKEY_EMULATION + +//------------------------------------------------------------------------------------------------------- +#ifdef FEATURE_WINKEY_EMULATION +void winkey_sidetone_freq_command(byte incoming_serial_byte) { + + #ifdef OPTION_WINKEY_2_SUPPORT + if (incoming_serial_byte & 128) { + if (configuration.sidetone_mode == SIDETONE_ON) {configuration.sidetone_mode = SIDETONE_PADDLE_ONLY;} + wk2_paddle_only_sidetone = 1; + } else { + if (configuration.sidetone_mode == SIDETONE_PADDLE_ONLY) {configuration.sidetone_mode = SIDETONE_ON;} + wk2_paddle_only_sidetone = 0; + } + #endif + + switch (incoming_serial_byte & 15) { + case 1: configuration.hz_sidetone = WINKEY_SIDETONE_1; break; + case 2: configuration.hz_sidetone = WINKEY_SIDETONE_2; break; + case 3: configuration.hz_sidetone = WINKEY_SIDETONE_3; break; + case 4: configuration.hz_sidetone = WINKEY_SIDETONE_4; break; + case 5: configuration.hz_sidetone = WINKEY_SIDETONE_5; break; + case 6: configuration.hz_sidetone = WINKEY_SIDETONE_6; break; + case 7: configuration.hz_sidetone = WINKEY_SIDETONE_7; break; + case 8: configuration.hz_sidetone = WINKEY_SIDETONE_8; break; + case 9: configuration.hz_sidetone = WINKEY_SIDETONE_9; break; + case 10: configuration.hz_sidetone = WINKEY_SIDETONE_10; break; + } + #ifdef OPTION_WINKEY_STRICT_EEPROM_WRITES_MAY_WEAR_OUT_EEPROM + config_dirty = 1; + #endif + +} +#endif //FEATURE_WINKEY_EMULATION + +//------------------------------------------------------------------------------------------------------- + +#ifdef FEATURE_WINKEY_EMULATION +void winkey_set_pinconfig_command(byte incoming_serial_byte) { + + if (incoming_serial_byte & 1) { + configuration.ptt_buffer_hold_active = 1; + winkey_pinconfig_ptt_bit = 1; + } else { + configuration.ptt_buffer_hold_active = 0; + #ifdef OPTION_WINKEY_2_SUPPORT + winkey_pinconfig_ptt_bit = 0; + #endif + } + + if (incoming_serial_byte & 2) { + #ifdef OPTION_WINKEY_2_SUPPORT + if (wk2_paddle_only_sidetone) { + configuration.sidetone_mode = SIDETONE_PADDLE_ONLY; + } else { + #endif + configuration.sidetone_mode = SIDETONE_ON; + #ifdef OPTION_WINKEY_2_SUPPORT + } + #endif + } else { + configuration.sidetone_mode = SIDETONE_OFF; + } + + #ifndef OPTION_NO_ULTIMATIC + switch (incoming_serial_byte & 192) { + case 0: ultimatic_mode = ULTIMATIC_NORMAL; break; + case 64: ultimatic_mode = ULTIMATIC_DAH_PRIORITY; break; + case 128: ultimatic_mode = ULTIMATIC_DIT_PRIORITY; break; + } + #endif + + switch(incoming_serial_byte & 48) { + case 0: ptt_hang_time_wordspace_units = WINKEY_HANG_TIME_1_0; break; + case 16: ptt_hang_time_wordspace_units = WINKEY_HANG_TIME_1_33; break; + case 32: ptt_hang_time_wordspace_units = WINKEY_HANG_TIME_1_66; break; + case 48: ptt_hang_time_wordspace_units = WINKEY_HANG_TIME_2_0; break; + } + #ifndef FEATURE_SO2R_BASE + switch(incoming_serial_byte & 12) { + case 0: + key_tx = 0; + #ifdef OPTION_WINKEY_2_SUPPORT + wk2_both_tx_activated = 0; + #endif + break; + case 4: + key_tx = 1; + configuration.current_ptt_line = ptt_tx_1; + current_tx_key_line = tx_key_line_1; + configuration.current_tx = 1; + #ifdef OPTION_WINKEY_2_SUPPORT + wk2_both_tx_activated = 0; + #endif + break; + case 8: + key_tx = 1; + if (ptt_tx_2) { + configuration.current_ptt_line = ptt_tx_2; + } else { + configuration.current_ptt_line = ptt_tx_1; + } + if (tx_key_line_2) { + current_tx_key_line = tx_key_line_2; + } else { + current_tx_key_line = tx_key_line_1; + } + #ifdef OPTION_WINKEY_2_SUPPORT + wk2_both_tx_activated = 0; + #endif + break; + case 12: + key_tx = 1; + configuration.current_ptt_line = ptt_tx_1; + current_tx_key_line = tx_key_line_1; + configuration.current_tx = 1; + #ifdef OPTION_WINKEY_2_SUPPORT + wk2_both_tx_activated = 1; + #endif + break; + } + + config_dirty = 1; + #endif //FEATURE_SO2R_BASE + +} +#endif + +//------------------------------------------------------------------------------------------------------- + +#ifdef FEATURE_WINKEY_EMULATION +void winkey_load_settings_command(byte winkey_status,byte incoming_serial_byte) { + + switch(winkey_status) { + case WINKEY_LOAD_SETTINGS_PARM_1_COMMAND: + #ifdef DEBUG_WINKEY + debug_serial_port->println("winkey_load_settings_command: WINKEY_LOAD_SETTINGS_PARM_1_COMMAND"); + #endif //DEBUG_WINKEY + winkey_setmode_command(incoming_serial_byte); + break; + case WINKEY_LOAD_SETTINGS_PARM_2_COMMAND: + #ifdef DEBUG_WINKEY + debug_serial_port->println("winkey_load_settings_command: WINKEY_LOAD_SETTINGS_PARM_2_COMMAND"); + #endif //DEBUG_WINKEY + winkey_unbuffered_speed_command(incoming_serial_byte); + break; + case WINKEY_LOAD_SETTINGS_PARM_3_COMMAND: + #ifdef DEBUG_WINKEY + debug_serial_port->println("winkey_load_settings_command: WINKEY_LOAD_SETTINGS_PARM_3_COMMAND"); + #endif //DEBUG_WINKEY + winkey_sidetone_freq_command(incoming_serial_byte); + break; + case WINKEY_LOAD_SETTINGS_PARM_4_COMMAND: + #ifdef DEBUG_WINKEY + debug_serial_port->println("winkey_load_settings_command: WINKEY_LOAD_SETTINGS_PARM_4_COMMAND"); + #endif //DEBUG_WINKEY + winkey_weighting_command(incoming_serial_byte); + break; + case WINKEY_LOAD_SETTINGS_PARM_5_COMMAND: + #ifdef DEBUG_WINKEY + debug_serial_port->println("winkey_load_settings_command: WINKEY_LOAD_SETTINGS_PARM_5_COMMAND"); + #endif //DEBUG_WINKEY + winkey_ptt_times_parm1_command(incoming_serial_byte); + break; + case WINKEY_LOAD_SETTINGS_PARM_6_COMMAND: + #ifdef DEBUG_WINKEY + debug_serial_port->println("winkey_load_settings_command: WINKEY_LOAD_SETTINGS_PARM_6_COMMAND"); + #endif //DEBUG_WINKEY + winkey_ptt_times_parm2_command(incoming_serial_byte); + break; + case WINKEY_LOAD_SETTINGS_PARM_7_COMMAND: + #ifdef DEBUG_WINKEY + debug_serial_port->println("winkey_load_settings_command: WINKEY_LOAD_SETTINGS_PARM_7_COMMAND"); + #endif //DEBUG_WINKEY + winkey_set_pot_parm1_command(incoming_serial_byte); + break; + case WINKEY_LOAD_SETTINGS_PARM_8_COMMAND: + #ifdef DEBUG_WINKEY + debug_serial_port->println("winkey_load_settings_command: WINKEY_LOAD_SETTINGS_PARM_8_COMMAND"); + #endif //DEBUG_WINKEY + winkey_set_pot_parm2_command(incoming_serial_byte); + break; + case WINKEY_LOAD_SETTINGS_PARM_9_COMMAND: + #ifdef DEBUG_WINKEY + debug_serial_port->println("winkey_load_settings_command: WINKEY_LOAD_SETTINGS_PARM_9_COMMAND"); + #endif //DEBUG_WINKEY + winkey_first_extension_command(incoming_serial_byte); + break; + case WINKEY_LOAD_SETTINGS_PARM_10_COMMAND: + #ifdef DEBUG_WINKEY + debug_serial_port->println("winkey_load_settings_command: WINKEY_LOAD_SETTINGS_PARM_10_COMMAND"); + #endif //DEBUG_WINKEY + winkey_keying_compensation_command(incoming_serial_byte); + break; + case WINKEY_LOAD_SETTINGS_PARM_11_COMMAND: + #ifdef DEBUG_WINKEY + debug_serial_port->println("winkey_load_settings_command: WINKEY_LOAD_SETTINGS_PARM_11_COMMAND"); + #endif //DEBUG_WINKEY + winkey_farnsworth_command(incoming_serial_byte); + break; + case WINKEY_LOAD_SETTINGS_PARM_12_COMMAND: // paddle switchpoint - don't need to support + #ifdef DEBUG_WINKEY + debug_serial_port->println("winkey_load_settings_command: WINKEY_LOAD_SETTINGS_PARM_12_COMMAND"); + #endif //DEBUG_WINKEY + break; + case WINKEY_LOAD_SETTINGS_PARM_13_COMMAND: + #ifdef DEBUG_WINKEY + debug_serial_port->println("winkey_load_settings_command: WINKEY_LOAD_SETTINGS_PARM_13_COMMAND"); + #endif //DEBUG_WINKEY + winkey_dah_to_dit_ratio_command(incoming_serial_byte); + break; + case WINKEY_LOAD_SETTINGS_PARM_14_COMMAND: + #ifdef DEBUG_WINKEY + debug_serial_port->println("winkey_load_settings_command: WINKEY_LOAD_SETTINGS_PARM_14_COMMAND"); + #endif //DEBUG_WINKEY + winkey_set_pinconfig_command(incoming_serial_byte); + break; + case WINKEY_LOAD_SETTINGS_PARM_15_COMMAND: + #ifdef DEBUG_WINKEY + debug_serial_port->println("winkey_load_settings_command: WINKEY_LOAD_SETTINGS_PARM_15_COMMAND"); + #endif //DEBUG_WINKEY + winkey_set_pot_parm3_command(incoming_serial_byte); + break; + } +} +#endif + +//------------------------------------------------------------------------------------------------------- + +#ifdef FEATURE_WINKEY_EMULATION +void winkey_admin_get_values_command() { + + byte byte_to_send; + + // 1 - mode register + byte_to_send = 0; + if (configuration.length_wordspace != default_length_wordspace) { + byte_to_send = byte_to_send | 1; + } + #ifdef FEATURE_AUTOSPACE + if (configuration.autospace_active) { + byte_to_send = byte_to_send | 2; + } + #endif + if (winkey_serial_echo) { + byte_to_send = byte_to_send | 4; + } + if (configuration.paddle_mode == PADDLE_REVERSE) { + byte_to_send = byte_to_send | 8; + } + switch (configuration.keyer_mode) { + case IAMBIC_A: byte_to_send = byte_to_send | 16; break; + case ULTIMATIC: byte_to_send = byte_to_send | 32; break; + case BUG: byte_to_send = byte_to_send | 48; break; + } + if (winkey_paddle_echo_activated) { + byte_to_send = byte_to_send | 64; + } + #ifdef FEATURE_DEAD_OP_WATCHDOG + if (!dead_op_watchdog_active) { + byte_to_send = byte_to_send | 128; + } + #endif //FEATURE_DEAD_OP_WATCHDOG + winkey_port_write(byte_to_send,1); + + // 2 - speed + if (configuration.wpm > 99) { + winkey_port_write(99,1); + } else { + byte_to_send = configuration.wpm; + winkey_port_write(byte_to_send,1); + } + + // 3 - sidetone + switch(configuration.hz_sidetone) { + case WINKEY_SIDETONE_1 : winkey_port_write(1,1); break; + case WINKEY_SIDETONE_2 : winkey_port_write(2,1); break; + case WINKEY_SIDETONE_3 : winkey_port_write(3,1); break; + case WINKEY_SIDETONE_4 : winkey_port_write(4,1); break; + case WINKEY_SIDETONE_5 : winkey_port_write(5,1); break; + case WINKEY_SIDETONE_6 : winkey_port_write(6,1); break; + case WINKEY_SIDETONE_7 : winkey_port_write(7,1); break; + case WINKEY_SIDETONE_8 : winkey_port_write(8,1); break; + case WINKEY_SIDETONE_9 : winkey_port_write(9,1); break; + case WINKEY_SIDETONE_10 : winkey_port_write(10,1); break; + default: winkey_port_write(5,1); break; + } + + // 4 - weight + winkey_port_write(configuration.weighting,1); + + // 5 - ptt lead + if (configuration.ptt_lead_time[configuration.current_tx-1] < 256){ + winkey_port_write(configuration.ptt_lead_time[configuration.current_tx-1]/10,1); + } else { + winkey_port_write(255,1); + } + + // 6 - ptt tail + //if (configuration.ptt_tail_time[configuration.current_tx-1] < 256){ + //winkey_port_write((configuration.ptt_tail_time[configuration.current_tx-1] - (3*int(1200/configuration.wpm)))/10,1); + winkey_port_write(winkey_session_ptt_tail,1); + // } else { + // winkey_port_write(winkey_port_write(255,1); + // } + + // 7 - pot min wpm + #ifdef FEATURE_POTENTIOMETER + winkey_port_write(pot_wpm_low_value,1); + #else + winkey_port_write(15,1); + #endif + + // 8 - pot wpm range + #ifdef FEATURE_POTENTIOMETER + winkey_port_write(pot_wpm_high_value - pot_wpm_low_value,1); + #else + winkey_port_write(20,1); + #endif + + // 9 - 1st extension + winkey_port_write(first_extension_time,1); + + // 10 - compensation + winkey_port_write(configuration.keying_compensation,1); + + // 11 - farnsworth wpm + #ifdef FEATURE_FARNSWORTH + winkey_port_write(configuration.wpm_farnsworth,1); + #else + winkey_port_write(zero,1); + #endif + + // 12 - paddle setpoint + winkey_port_write(50,1); // default value + + // 13 - dah to dit ratio + winkey_port_write(50,1); // TODO -backwards calculate + + // 14 - pin config + #ifdef OPTION_WINKEY_2_SUPPORT + byte_to_send = 0; + if (configuration.current_ptt_line != 0) {byte_to_send = byte_to_send | 1;} + if ((configuration.sidetone_mode == SIDETONE_ON) || (configuration.sidetone_mode == SIDETONE_PADDLE_ONLY)) {byte_to_send = byte_to_send | 2;} + if (current_tx_key_line == tx_key_line_1) {byte_to_send = byte_to_send | 4;} + if (current_tx_key_line == tx_key_line_2) {byte_to_send = byte_to_send | 8;} + #ifndef FEATURE_SO2R_BASE + if (wk2_both_tx_activated) {byte_to_send = byte_to_send | 12;} + #endif + + #ifndef OPTION_NO_ULTIMATIC + if (ultimatic_mode == ULTIMATIC_DIT_PRIORITY) {byte_to_send = byte_to_send | 128;} + if (ultimatic_mode == ULTIMATIC_DAH_PRIORITY) {byte_to_send = byte_to_send | 64;} + #endif + + if (ptt_hang_time_wordspace_units == 1.33) {byte_to_send = byte_to_send | 16;} + if (ptt_hang_time_wordspace_units == 1.66) {byte_to_send = byte_to_send | 32;} + if (ptt_hang_time_wordspace_units == 2.0) {byte_to_send = byte_to_send | 48;} + winkey_port_write(byte_to_send,1); + #else + winkey_port_write(5,1); // default value + #endif + + // 15 - pot range + #ifdef OPTION_WINKEY_2_SUPPORT + winkey_port_write(zero,1); + #else + winkey_port_write(0xFF,1); + #endif + +} +#endif + + +/* + +Chapter One + +It was late on a rainy Sunday evening. Static crashes on the direct conversion receiver signaled a distant thunderstorm, due to arrive in an hour or so. Colin knew he would have to disconnect the little microcontroller circuit from the receiver and all the station antennas soon, but it was getting late and he had to get his sleep for work the next day. + +The contraption was a tangled mess on his desk, something only a radio amateur or mad scientist could appreciate. Alligator clips connected the I and Q audio from the simple receiver to the microcontroller. Colin had been learning about fast Fourier analysis. This was his first attempt at actually running the code in an effort to decode RTTY signals. The microcontroller probably lacked the horsepower to do it, and Colin knew expecting any sort of performance from his creation was a long shot. + +Colin tuned to some RTTY signals but couldn't copy anything, despite carefully and slowly tuning the receiver in hopes of hitting that sweet spot where perhaps the microcontroller would blurt out some intelligence, some discernable word or text. Just one recognizable snippet would give him the feeling of accomplishment or even victory, even if his design never proved to be usable in his nightly hobby. + +The static crashes grew stronger and more frequent. Colin had resigned himself to the fact that success would not be achieved this evening. The approaching storm along with his growing fatigue convinced him to shut things down and head upstairs to bed. Just then a burst of noise, different from the thunderstorm static crashes, but a type that you normally hear on 80 meters each night blurted out. The microcontroller sent out its serial port a string of random characters, in a vain attempt to decode the sounds: + +GEHZCVFNOVTZBEBA + +Colin went about the process of disconnecting the power to everything and disconnecting antennas and went to bed. + +The next evening after supper with his family, Colin went to his basement radio room again, determined to work again on his project but perhaps less eager than before due to the increasing futility of his efforts. The microcontroller sat connected to the receiver, and the controller to the computer. He listened to the receiver in the background while responding to emails. There was a QSO in progress, an old man talking about his dog itching a lot. The two old men in the conversation droned on forever, with Colin chuckling to himself, but too caught up in his email to reach over and tune the rig to another frequency in hopes of finding a more interesting conversation. + +A burst of noise came through the rig again, much like the night before, though much stronger. The simple receiver lacked automatic gain control and the strong signal produced a rather loud, annoying noise emanating from the rig, prompting Colin to reach over and turn the volume down. Colin noticed on his serial terminal program another random string of characters which the microcontroller dutifully decoded: + +GEHZCVFNOVTZBEBA + +The string looked familiar to Colin. He copied and pasted the string into search on his computer. The search produced one hit, the terminal program log from the previous evening. Colin opened the file and saw the matching string, 16 characters. "What are the chances of that happening?", he thought. He looked through his code again, looking for some sort of mistake, pattern in the code algorithm, or some plausible explanation. The receiver belched again: + +GEHZCVFNOVTZBEBA + +At this point Colin had no plausible explanation why the same random string of characters would be decoded last night and this evening, from mere noise bursts. Frustrated, he decided to post a message on an Internet group describing the strange behavior and the random characters, and then walked away from his radios to watch TV with the family. After almost an hour of watching mindless sitcoms, it was time for the kids to go to bed. After they were tucked into bed, Colin came back to his desk to catch up on email. + +The receiver, still powered up with the random noise of the universe coming out of the speaker at a low level, and the connected microcontroller circuit sat idle, waiting for some signal to decode. An AM roundtable comes up on frequency and he listens awhile, while he continues to web surf, looking for something to occupy his mind. A static crash comes through the speaks and the microcontroller terminal comes alive again, spewing characters: + +COLINMEETME@40-10-45.5&75-10-52.6@SAT1200Z + +"Wow" Colin exclaims, almost involuntarily. He pauses for a moment, hoping his wife in the next room hasn't heard him. She doesn't respond, continuing to watch TV.  "That's my name....coordinates, and a day and time.  That can't be a coincidence.  What in the world have I stumbled upon?" he thinks. Nervously he brings up Google Earth and enters the coordinates.  It's a coffee shop, about an hour and twenty minutes south. " Whoever sent this wants to meet me?" + +Chapter Two + +Colin barely slept the rest of the nights that week thinking about the message. He stays out of his radio room which is very unlike him. His wife is out of town this weekend, and Colin rationalizes that there's no excuse to not go to the coffee shop. Early Saturday morning he quickly gets up, and nervously gets dressed. He worries if he's given himself enough time to get there. It's near the city and the surrounding suburban area where the coffee shop is located is notorious for bad traffic. He decides to take a toll road and exit where he can take back roads to avoid the main thoroughfares. + +He arrived at the coffee with a few minutes to spare, takes an out of the way parking spot towards the rear of the restaurant, backing in so he can see anyone pulling in or out, and the side entrance of the coffee shop. He sits in the vehicle and surveys the parking lot. Opening the glove compartment he pulls out a pistol in a holster. Although licensed for carrying a sidearm, Colin rarely, if ever actual wore it in public. He strapped it on to his belt and double-checked that his jacket concealed it. His hands shook nervously, but he reassured himself he was somewhat prepared in case the proverbial "men in black" attempted to swoop down and throw him into a black van and drive off. + +Looking up, Colin sees an old man in the parking lot looking his way. They make eye contact. Colin looks away but it's clear the old man is has somehow identified him. Colin sighs. "Perhaps he saw all the antennas on my vehicle, or my callsign plate." He gets out of the vehicle, locks it, and walks over to the old man. + +"Hello" he says in a somewhat frail voice.  "You Colin?" + +"Yes" replies Colin, nervously. + +The old man nods and his face lightens up. "Come inside, let's talk." + +They go inside and get in line.  The old man orders a coffee, and Colin, never acquiring a taste for coffee, get a hot chocolate.  They grab a table towards the back, away from everyone else.  The old man looks around to make sure they're out of earshot of others. + +The old man leans inward, "So you copied my transmission the other day?" + +"Yes."  Colin tells him the story of how he came upon the transmission. + +"Well, congratulations.  You've stumbled upon something I think you're going to be very happy about.  You're in amateur radio?"  Colin nods.  "You've come upon a secret society.  We've been around a long time, since World War II.  Some of us are hams, others aren't.  We're everywhere.  You've heard us anytime you've turned on a radio, you just didn't know it. We're the people you don't normally find on the air....the academics, scientists, progressives, politicians, famous people...activists...introverts...geniuses...people close to world leaders.  We communicate via encrypted messaging.  Those noise bursts you heard were transmissions from me.  Some of our communications are noise bursts.  Sometime we communicate with pure noise, indistinguishable from the normal noise you hear on your receiver everyday.  We hide out in the open." + +"But how do you do this?"  Colin's technical curiosity emerges.  "How do you communicate with noise?" + +The old man takes a sip from his coffee.  "We use a pseudo-random bit stream and quadrature modulate a digital signal taken from a special alphabet, somewhat like ASCII.  It's amazingly simple but nearly impossible to break without the bit stream.  You were just lucky to receive it.  Apparently the buggy code in your microcontroller digital signal processing generates part of the pseudo random stream under the right conditions.  Everyone thinks 80 meters is noisy.  It's really not, there's just a lot of us talking on it.  You ever turn on your radio and it's S9 noise everywhere?" + +"Yes" replies Colin. + +"Sometimes that's us.  We sometimes modulate wideband noise when we have a particularly large message to send out, something important.  The technology is really interesting.  It pushes the limits of Shannon's Equation." he pauses.  "You ever hear of long delay echos?" + +"I've never experienced one, but I've read about them and heard they're somewhat common." Colin says. + +He smiles.  "That's us.  Sometime we communicate by receiving someone's signal on the air, we delay it, modulate the noise on it, and re-transmit it.  We do that for fun.  People seem to get a kick out of it." + +"Why does this society exist?" asks Colin. + +"We serve a higher purpose." pointing above, he says.  "It came out of the Resistance in World War II and was originally intended to prevent atrocities like the Holocaust from happening again, but since then it's grown to encompass other things.  Many of us started off as radio amateurs and got bored with it.  We dropped out.  We're the radio guys you don't see at hamfests or on the Internet.  Those of us who are licensed amateurs usually lay low and don't get on the air, at least in a way you can hear us.  Amateur radio is to us as CB is to amateur radio.  Few of us fit in with them. Members communicate about important stuff, like scientific discoveries or secret information from governments that could save lives or change the world.  We've provided information that has ended wars, and started some.  Some say we provided the information that started the fall of the USSR.  We operate without borders or recognition of nationality.   I'm not sure how many of us there are, but it's perhaps in the thousands, worldwide." + +Colin asks "Are you spies?" + +"We're not spies, we're communicators." he replies. + +"Does the government know of this network?" + +"Perhaps, but not at a high level or in any official capacity that we know of.  We definitely have members close to people high up, advisers of sorts.  Undoubtedly there are members in intelligence agencies in various governments.  But they don't dare divulge knowledge of the network.  It's too valuable.  To them it's a tool, and they know they would be denied that tool, purged from the network, should they let others know of it.  But they are free to use the information they receive, as they see fit.  But they know they have a responsibility to use it for the greater good." + +The old man clears his throat and takes another gulp of coffee.  "Communications is a weapon, more powerful than any weapon you can carry.  That phone," he said, pointing to my iPhone lying on the table, " is just as powerful as the weapon you have on your belt, just in a different way." + +Colin tries to hide a puzzled look, wondering how the old man knew of his weapon.  Changing the subject, he asks "How do people get into this?" + +"Membership is by invitation only.  We have 16 character identity strings.  You received mine.  An identity string is what you would call a callsign in amateur radio.  You're the first person I've ever heard of receiving the signal without knowledge of the code.  There's no process for someone like you to join.  But I'm getting old and I need to hand off my encryption stream to someone before I die, to keep it going.  You seem to be a nice enough guy, qualified to join, from what I have seen and heard about you." + +"But.... this sounds like a network of rather smart and powerful people.  I'm just an ordinary guy who likes to play with radios and occasionally build something.  I'm not a scientist or someone powerful.  Is there some role I will have, something I need to do?" Colin asks. + +"Some members just have fun with this, somewhat like a hobby.  They don't have roles, for now.  You will have a role, you just don't know what it is yet.  Do not seek out a role.  Do not try to make yourself important or identify some great thing to do.  Those who invent things to do, create crises, or give themselves power get purged from the network.  Your role will become known in due time and you will know it when you encounter it.  Trust me."  + +He goes on, "You're going to receive more information.  It will explain the encryption algorithm.  You know how to program, so with a little bit of work you should be able to write the software for a transceiver that will work reliably.  I'll also give you an identity string.  It's derived from mine and you'll eventually be able to trace it back mathematically to previous identity strings and others in the hierarchy.  The more you communicate, your identity string will establish a trust relationship with other identity strings, other operators.  The more operators you gain trust with, you will get more of the algorithm and more of the bit stream.  With more of the algorithm and bit stream, the more signals you will be able to receive and you will be able to communicate with more people in the network hierarchy.  With perseverance and patience you'll get to know some high level members, perhaps even people you see on the news." + +"I said before that there are thousands of operators.  The truth is I don't know how many operators there are.  No one does.  As more of the bit stream is revealed, more members appear.  For all we know there could be millions of members.  There could be extra-terrestrials in the network."  He chuckles.  "Some have theorized that some of the noise we receive from outer space could be actually intelligence encrypted in the noise, like we do.  We just don't have the information or computing power yet to decode it." + +The smile leaves old man's face.  "You have to keep this a secret.  If you reveal this to the wrong people, the results would be disastrous.  Those who reveal the code of the noise are purged from the network, sometimes not seen again." +Before Colin could ask his next question, the old man got up, handed him a card with characters written in bold black marker: + +8^fGwq9(:lLDPu6$ + +"Congratulations.  This is your identity string.  Memorize it.  Guard it with your life."  He offers his right hand and they shake hands. + +Colin follows the old man out the door, wanting to ask more questions. "Where will I would get the information on the algorithm, how do I build a transceiver?"  he frantically asks. + +"You have to listen to the noise."  he said as he walked to his car, got in, and drove off. + +Colin drove home in somewhat of a daze, not sure what to make of all this.  Was the old man crazy, or was all this real?  Colin went about my business for a few days, thinking about the old man and wondering what would be next.  "Would I get something in the mail?  Perhaps an email?  Would he contact me again?" + +A few days later while watching the local news, a story came on about the death of a prominent researcher.  Colin was shocked to see a grainy photo of the old man he had met at the coffee shop, the photo perhaps from the 60s as he looked younger, more Colin's age today.  Walter was his name.  He had worked at Bell Labs in New Jersey as a physicist and had made many discoveries in communications which were patented in the 60s and 70s.  Walter was a quiet man but was known for his community work.  He fled Germany with his family as a young boy prior to World War II breaking out.  His father was a poor potato farmer who later helped the allies in cryptography after he devised a code based on the patterns of eyes on potatoes.  His wife had passed before him several years earlier.  Walter died alone at his home, of unknown causes and his death was under investigation.  Investigators doubted there was foul play, but there was a rather odd paper he was writing with codes on it found next to him.  He was survived by two children and some grandchildren residing in Florida.  Colin thought perhaps he could contact his family, but he knew he couldn't risk revealing what he had heard from the man if what he said was true.  Colin sat dumbfounded, wondering if he had lost his one connection to the secret network. + +Later that night Colin once again turned on his receiver to 80 meters.  The little circuit sat idle with alligator clips connecting the rig audio to it.  His original goal of copying a RTTY signal now seemed pointless and insignificant in the grand scheme of things with the new knowledge he had.  He wanted to write more code and figure out the algorithm, all of it.  But Colin had no idea what next step to take, no clue what the algorithm was that would grant him access to a whole new world.  He pulled the card out of his wallet with his identity string and stared at the seemingly random 16 characters.  It contained uppercase, lowercase, numbers, symbols, just about everything.  Perhaps it was a base 64 character set?  What secrets were in it?  His thoughts were a disorganized jumble, and feeling a headache coming on he stopped himself from thinking further about it. + +He was no longer interested in listening to Morse code signals or voice conversations.  That was merely just meaningless noise, a distraction from what he was really looking for.  Every little pop and crackle on the receiver caught his attention.  Was it just random atmospheric noise leftover from the Big Bang or some noisy electrical appliance, or was there intelligence in each seemingly random sounds?  For hours he scanned through the band, hoping to catch the right signal in hopes that his little contraption might pick up some clue that would lead him to the next step, perhaps someone else in the network since his contact had passed away.  BZZZZZT bursts from the receiver and the microcontroller terminal screen came alive: + + 8^fGwq9(:lLDPu6$ : KEEP LISTENING TO THE NOISE AND AWAIT FURTHER INFO. + +*/ + + +//------------------------------------------------------------------------------------------------------- + +#if defined(FEATURE_SERIAL) && defined(FEATURE_WINKEY_EMULATION) && defined(OPTION_WINKEY_2_SUPPORT) + +void winkey_eeprom_download() { + + byte zero = 0; + unsigned int x = 0; + unsigned int bytes_sent = 0; + + winkey_port_write(0xA5,1); // 01 magic byte + winkey_admin_get_values_command(); // 02-16 + + winkey_port_write(byte(configuration.wpm),1); // 17 cmdwpm + bytes_sent = 17; + + //pad the rest with zeros + for (x = 0;x < (256-bytes_sent); x++) { + winkey_port_write(zero,1); + } +} +#endif //defined(FEATURE_SERIAL) && defined(FEATURE_WINKEY_EMULATION) && defined(OPTION_WINKEY_2_SUPPORT) + + +//------------------------------------------------------------------------------------------------------- + +#ifdef FEATURE_WINKEY_EMULATION +void winkey_port_write(byte byte_to_send,byte override_filter){ + + #ifdef DEBUG_WINKEY_PORT_WRITE + if ((byte_to_send > 4) && (byte_to_send < 31)){ + boop(); + delay(500); + boop(); + delay(500); + boop(); + //return; + } + #endif + + if (((byte_to_send > 4) && (byte_to_send < 31)) && (!override_filter)){ + #ifdef DEBUG_WINKEY + debug_serial_port->print("Winkey Port TX: FILTERED: "); + if ((byte_to_send > 31) && (byte_to_send < 127)){ + debug_serial_port->write(byte_to_send); + } else { + debug_serial_port->print("."); + } + debug_serial_port->print(" ["); + debug_serial_port->print(byte_to_send); + debug_serial_port->print("] [0x"); + debug_serial_port->print(byte_to_send,HEX); + debug_serial_port->println("]"); + #endif + return; + } + + primary_serial_port->write(byte_to_send); + #ifdef DEBUG_WINKEY + debug_serial_port->print("Winkey Port TX: "); + if ((byte_to_send > 31) && (byte_to_send < 127)){ + debug_serial_port->write(byte_to_send); + } else { + debug_serial_port->print("."); + } + debug_serial_port->print(" ["); + debug_serial_port->print(byte_to_send); + debug_serial_port->print("] [0x"); + debug_serial_port->print(byte_to_send,HEX); + debug_serial_port->println("]"); + #endif +} + +#endif //FEATURE_WINKEY_EMULATION + +//------------------------------------------------------------------------------------------------------- + +#ifdef FEATURE_WINKEY_EMULATION +void service_winkey(byte action) { + + static byte winkey_status = WINKEY_NO_COMMAND_IN_PROGRESS; + static int winkey_parmcount = 0; + static unsigned long winkey_last_activity; + byte status_byte_to_send; + static byte winkey_paddle_echo_space_sent = 1; + + #ifdef OPTION_WINKEY_DISCARD_BYTES_AT_STARTUP + static byte winkey_discard_bytes_init_done = 0; + if (!winkey_discard_bytes_init_done) { + if (primary_serial_port->available()) { + for (int z = winkey_discard_bytes_startup;z > 0;z--) { + while (primary_serial_port->available() == 0) {} + primary_serial_port->read(); + } + winkey_discard_bytes_init_done = 1; + } + } + #endif //OPTION_WINKEY_DISCARD_BYTES_AT_STARTUP + + #ifdef DEBUG_WINKEY_SEND_ERRANT_BYTE + byte i_sent_it = 0; + + if ((millis() > 30000) && (!i_sent_it)){ + winkey_port_write(30,1); + i_sent_it = 1; + } + + #endif + + + #ifdef OPTION_WINKEY_IGNORE_FIRST_STATUS_REQUEST + static byte ignored_first_status_request = 0; + #endif //OPTION_WINKEY_IGNORE_FIRST_STATUS_REQUEST + + if (action == WINKEY_HOUSEKEEPING) { + if (winkey_last_unbuffered_speed_wpm == 0) { + winkey_last_unbuffered_speed_wpm = configuration.wpm; + } + + // Winkey interface emulation housekeeping items + // check to see if we were sending stuff and the buffer is clear + if (winkey_interrupted) { // if Winkey sending was interrupted by the paddle, look at PTT line rather than timing out to send 0xc0 + if (ptt_line_activated == 0) { + #ifdef DEBUG_WINKEY + debug_serial_port->println("\r\nservice_winkey:sending unsolicited status byte due to paddle interrupt"); + #endif //DEBUG_WINKEY + winkey_sending = 0; + clear_send_buffer(); + + #ifdef FEATURE_MEMORIES + //clear_memory_button_buffer(); + play_memory_prempt = 1; + repeat_memory = 255; + #endif + + winkey_interrupted = 0; + //winkey_port_write(0xc2|winkey_sending|winkey_xoff); + winkey_port_write(0xc6,0); //<- this alone makes N1MM logger get borked (0xC2 = paddle interrupt) + winkey_port_write(0xc0,0); // so let's send a 0xC0 to keep N1MM logger happy + winkey_buffer_counter = 0; + winkey_buffer_pointer = 0; + } + } else { //if (winkey_interrupted) + //if ((winkey_host_open) && (winkey_sending) && (send_buffer_bytes < 1) && ((millis() - winkey_last_activity) > winkey_c0_wait_time)) { + if ((primary_serial_port->available() == 0) && (winkey_host_open) && (winkey_sending) && (send_buffer_bytes < 1) && ((millis() - winkey_last_activity) > winkey_c0_wait_time)) { + #ifdef OPTION_WINKEY_SEND_WORDSPACE_AT_END_OF_BUFFER + send_char(' ',KEYER_NORMAL); + #endif + //add_to_send_buffer(' '); // this causes a 0x20 to get echoed back to host - doesn't seem to effect N1MM program + #ifdef DEBUG_WINKEY + debug_serial_port->println("\r\nservice_winkey:sending unsolicited status byte"); + #endif //DEBUG_WINKEY + winkey_sending = 0; + winkey_port_write(0xc0|winkey_sending|winkey_xoff,0); // tell the host we've sent everything + winkey_buffer_counter = 0; + winkey_buffer_pointer = 0; + } + } // if (winkey_interrupted) + + // failsafe check - if we've been in some command status for awhile waiting for something, clear things out + if ((winkey_status != WINKEY_NO_COMMAND_IN_PROGRESS) && ((millis() - winkey_last_activity) > winkey_command_timeout_ms)) { + #ifdef DEBUG_WINKEY + debug_serial_port->print("\r\nservice_winkey:cmd tout!->WINKEY_NO_COMMAND_IN_PROGRESS cmd was:"); + debug_serial_port->println(winkey_status); + #endif //DEBUG_WINKEY + winkey_status = WINKEY_NO_COMMAND_IN_PROGRESS; + winkey_buffer_counter = 0; + winkey_buffer_pointer = 0; + winkey_port_write(0xc0|winkey_sending|winkey_xoff,0); //send a status byte back for giggles + } + + if ((winkey_host_open) && (winkey_paddle_echo_buffer) && (winkey_paddle_echo_activated) && (millis() > winkey_paddle_echo_buffer_decode_time)) { + #ifdef DEBUG_WINKEY + debug_serial_port->println("\r\nservice_winkey:sending paddle echo char"); + #endif //DEBUG_WINKEY + winkey_port_write(byte(convert_cw_number_to_ascii(winkey_paddle_echo_buffer)),0); + winkey_paddle_echo_buffer = 0; + winkey_paddle_echo_buffer_decode_time = millis() + (float(600/configuration.wpm)*length_letterspace); + winkey_paddle_echo_space_sent = 0; + } + + if ((winkey_host_open) && (winkey_paddle_echo_buffer == 0) && (winkey_paddle_echo_activated) && (millis() > (winkey_paddle_echo_buffer_decode_time + (float(1200/configuration.wpm)*(configuration.length_wordspace-length_letterspace)))) && (!winkey_paddle_echo_space_sent)) { + #ifdef DEBUG_WINKEY + debug_serial_port->println("\r\nservice_winkey:sending paddle echo space"); + #endif //DEBUG_WINKEY + winkey_port_write(' ',0); + winkey_paddle_echo_space_sent = 1; + } + } // if (action == WINKEY_HOUSEKEEPING) + + if (action == SERVICE_SERIAL_BYTE){ + #ifdef DEBUG_WINKEY + debug_serial_port->print("Winkey Port RX: "); + if ((incoming_serial_byte > 31) && (incoming_serial_byte < 127)){ + debug_serial_port->write(incoming_serial_byte); + } else { + debug_serial_port->print("."); + } + debug_serial_port->print(" ["); + debug_serial_port->print(incoming_serial_byte); + debug_serial_port->print("]"); + debug_serial_port->print(" [0x"); + if (incoming_serial_byte < 16){debug_serial_port->print("0");} + debug_serial_port->print(incoming_serial_byte,HEX); + debug_serial_port->println("]"); + #endif //DEBUG_WINKEY + + winkey_last_activity = millis(); + + if (winkey_status == WINKEY_NO_COMMAND_IN_PROGRESS){ + #if defined(FEATURE_SO2R_BASE) + if (incoming_serial_byte >= 128) { + so2r_command(); + } + #endif //FEATURE_SO2R_BASE + + #if defined(OPTION_WINKEY_EXTENDED_COMMANDS_WITH_DOLLAR_SIGNS) + + #if !defined(OPTION_WINKEY_IGNORE_LOWERCASE) + if ((incoming_serial_byte > 31) && (incoming_serial_byte != 36)) { // ascii 36 = '$' + #else + if ((((incoming_serial_byte > 31) && (incoming_serial_byte < 97)) || (incoming_serial_byte == 124)) && (incoming_serial_byte != 36)) { // 124 = ascii | = half dit + #endif + + #else + + #if !defined(OPTION_WINKEY_IGNORE_LOWERCASE) + if (incoming_serial_byte > 31) { + #else + if (((incoming_serial_byte > 31) && (incoming_serial_byte < 97)) || (incoming_serial_byte == 124)) { // 124 = ascii | = half dit + #endif + + #endif + + #if !defined(OPTION_WINKEY_IGNORE_LOWERCASE) + if ((incoming_serial_byte > 96) && (incoming_serial_byte < 123)){incoming_serial_byte = incoming_serial_byte - 32;} + #endif //!defined(OPTION_WINKEY_IGNORE_LOWERCASE) + + byte serial_buffer_position_to_overwrite; + + if (winkey_buffer_pointer > 0) { + serial_buffer_position_to_overwrite = send_buffer_bytes - (winkey_buffer_counter - winkey_buffer_pointer) - 1; + if ((send_buffer_bytes) && (serial_buffer_position_to_overwrite < send_buffer_bytes )) { + + #ifdef DEBUG_WINKEY + debug_serial_port->print("service_winkey:serial_buffer_position_to_overwrite:"); + debug_serial_port->print(serial_buffer_position_to_overwrite); + debug_serial_port->print(":"); + debug_serial_port->write(incoming_serial_byte); + debug_serial_port->println(); + #endif //DEBUG_WINKEY + send_buffer_array[serial_buffer_position_to_overwrite] = incoming_serial_byte; + } + winkey_buffer_pointer++; + } else { + + add_to_send_buffer(incoming_serial_byte); + + #ifdef DEBUG_WINKEY + debug_serial_port->print("service_winkey:add_to_send_buffer:"); + debug_serial_port->write(incoming_serial_byte); + debug_serial_port->print(" send_buffer_bytes:"); + debug_serial_port->println(send_buffer_bytes); + #endif //DEBUG_WINKEY + + #if defined(OPTION_WINKEY_INTERRUPTS_MEMORY_REPEAT) && defined(FEATURE_MEMORIES) + play_memory_prempt = 1; + repeat_memory = 255; + #endif + winkey_buffer_counter++; + + } + + if (!winkey_sending) { + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:status byte:starting to send"); + #endif //DEBUG_WINKEY + winkey_sending=0x04; + #if !defined(OPTION_WINKEY_UCXLOG_SUPRESS_C4_STATUS_BYTE) + winkey_port_write(0xc4|winkey_sending|winkey_xoff,0); // tell the client we're starting to send + #endif //OPTION_WINKEY_UCXLOG_SUPRESS_C4_STATUS_BYTE + #ifdef FEATURE_MEMORIES + repeat_memory = 255; + #endif + } + } else { + + #ifdef OPTION_WINKEY_STRICT_HOST_OPEN + if ((winkey_host_open) || (incoming_serial_byte == 0)) { + #endif + + switch (incoming_serial_byte) { + case 0x00: + winkey_status = WINKEY_ADMIN_COMMAND; + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:ADMIN_CMD"); + #endif //DEBUG_WINKEY + break; + case 0x01: + winkey_status = WINKEY_SIDETONE_FREQ_COMMAND; + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:WINKEY_SIDETONE_FREQ_COMMAND"); + #endif //DEBUG_WINKEY + break; + case 0x02: // speed command - unbuffered + winkey_status = WINKEY_UNBUFFERED_SPEED_COMMAND; + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:WINKEY_UNBUFFERED_SPEED_COMMAND"); + #endif //DEBUG_WINKEY + break; + case 0x03: // weighting + winkey_status = WINKEY_WEIGHTING_COMMAND; + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:WINKEY_WEIGHTING_COMMAND"); + #endif //DEBUG_WINKEY + break; + case 0x04: // PTT lead and tail time + winkey_status = WINKEY_PTT_TIMES_PARM1_COMMAND; + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:WINKEY_PTT_TIMES_PARM1_COMMAND"); + #endif //DEBUG_WINKEY + break; + case 0x05: // speed pot set + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:WINKEY_SET_POT_PARM1_COMMAND"); + #endif //DEBUG_WINKEY + winkey_status = WINKEY_SET_POT_PARM1_COMMAND; + break; + case 0x06: + winkey_status = WINKEY_PAUSE_COMMAND; + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:WINKEY_PAUSE_COMMAND"); + #endif //DEBUG_WINKEY + break; + case 0x07: + #ifdef FEATURE_POTENTIOMETER + winkey_port_write(((pot_value_wpm()-pot_wpm_low_value)|128),0); + #else + winkey_port_write((byte(configuration.wpm-pot_wpm_low_value)|128),0); + #endif + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:report pot"); + #endif //DEBUG_WINKEY + break; + case 0x08: // backspace command + if (send_buffer_bytes) { + send_buffer_bytes--; + } + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:backspace"); + #endif //DEBUG_WINKEY + break; + case 0x09: + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:WINKEY_SET_PINCONFIG_COMMAND"); + #endif //DEBUG_WINKEY + winkey_status = WINKEY_SET_PINCONFIG_COMMAND; + break; + case 0x0a: // 0A - clear buffer - no parms + // #ifdef DEBUG_WINKEY + // debug_serial_port->println("service_winkey:0A clear buff"); + // #endif //DEBUG_WINKEY + clear_send_buffer(); + if (winkey_sending) { + //clear_send_buffer(); + winkey_sending = 0; + winkey_port_write(0xc0|winkey_sending|winkey_xoff,0); + } + pause_sending_buffer = 0; + winkey_buffer_counter = 0; + winkey_buffer_pointer = 0; + #if !defined(OPTION_DISABLE_SERIAL_PORT_CHECKING_WHILE_SENDING_CW) + loop_element_lengths_breakout_flag = 0; + #endif + #ifdef FEATURE_MEMORIES + repeat_memory = 255; + #endif + sending_mode = AUTOMATIC_SENDING; + manual_ptt_invoke = 0; + tx_and_sidetone_key(0); + winkey_speed_state = WINKEY_UNBUFFERED_SPEED; + configuration.wpm = winkey_last_unbuffered_speed_wpm; + #ifdef DEBUG_WINKEY + debug_serial_port->print("service_winkey:0A clearbuff send_buffer_bytes:"); + debug_serial_port->println(send_buffer_bytes); + #endif //DEBUG_WINKEY + break; + case 0x0b: + winkey_status = WINKEY_KEY_COMMAND; + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:WINKEY_KEY_COMMAND"); + #endif //DEBUG_WINKEY + break; + case 0x0c: + winkey_status = WINKEY_HSCW_COMMAND; + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:WINKEY_HSCW_COMMAND"); + #endif //DEBUG_WINKEY + break; + case 0x0d: + winkey_status = WINKEY_FARNSWORTH_COMMAND; + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:WINKEY_FARNSWORTH_COMMAND"); + #endif //DEBUG_WINKEY + break; + case 0x0e: + winkey_status = WINKEY_SETMODE_COMMAND; + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:WINKEY_SETMODE_COMMAND"); + #endif //DEBUG_WINKEY + break; + case 0x0f: // bulk load of defaults + winkey_status = WINKEY_LOAD_SETTINGS_PARM_1_COMMAND; + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:WINKEY_LOAD_SETTINGS_PARM_1_COMMAND"); + #endif //DEBUG_WINKEY + break; + case 0x10: + winkey_status = WINKEY_FIRST_EXTENSION_COMMAND; + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:WINKEY_FIRST_EXTENSION_COMMAND"); + #endif //DEBUG_WINKEY + break; + case 0x11: + winkey_status = WINKEY_KEYING_COMPENSATION_COMMAND; + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:WINKEY_KEYING_COMPENSATION_COMMAND"); + #endif //DEBUG_WINKEY + break; + case 0x12: + winkey_status = WINKEY_UNSUPPORTED_COMMAND; + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:0x12unsupport"); + #endif //DEBUG_WINKEY + winkey_parmcount = 1; + break; + case 0x13: // NULL command + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:0x13null"); + #endif //DEBUG_WINKEY + break; + case 0x14: + winkey_status = WINKEY_SOFTWARE_PADDLE_COMMAND; + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:WINKEY_SOFTWARE_PADDLE_COMMAND"); + #endif //DEBUG_WINKEY + break; + case 0x15: // report status + #ifndef OPTION_WINKEY_IGNORE_FIRST_STATUS_REQUEST //-------------------- + status_byte_to_send = 0xc0|winkey_sending|winkey_xoff; + if (send_buffer_status == SERIAL_SEND_BUFFER_TIMED_COMMAND) { + status_byte_to_send = status_byte_to_send | 16; + } + winkey_port_write(status_byte_to_send,0); + #ifdef DEBUG_WINKEY + debug_serial_port->print("service_winkey:0x15 rpt status:"); + debug_serial_port->println(status_byte_to_send); + #endif //DEBUG_WINKEY + #else //OPTION_WINKEY_IGNORE_FIRST_STATUS_REQUEST ------------------------ + if (ignored_first_status_request){ + status_byte_to_send = 0xc0|winkey_sending|winkey_xoff; + if (send_buffer_status == SERIAL_SEND_BUFFER_TIMED_COMMAND) { + status_byte_to_send = status_byte_to_send | 16; + } + winkey_port_write(status_byte_to_send,0); + #ifdef DEBUG_WINKEY + debug_serial_port->print("service_winkey:0x15 rpt status:"); + debug_serial_port->println(status_byte_to_send); + #endif //DEBUG_WINKEY + } else { + ignored_first_status_request = 1; + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:ignored first 0x15 status request"); + #endif //DEBUG_WINKEY + } + #endif //OPTION_WINKEY_IGNORE_FIRST_STATUS_REQUEST -------------------- + + break; + case 0x16: // Pointer operation + winkey_status = WINKEY_POINTER_COMMAND; + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:WINKEY_POINTER_COMMAND"); + #endif //DEBUG_WINKEY + break; + case 0x17: // dit to dah ratio + winkey_status = WINKEY_DAH_TO_DIT_RATIO_COMMAND; + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:WINKEY_DAH_TO_DIT_RATIO_COMMAND"); + #endif //DEBUG_WINKEY + break; + // start of buffered commands ------------------------------ + case 0x18: //buffer PTT on/off + winkey_status = WINKEY_BUFFFERED_PTT_COMMMAND; + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:WINKEY_BUFFFERED_PTT_COMMMAND"); + #endif //DEBUG_WINKEY + break; + case 0x19: + winkey_status = WINKEY_KEY_BUFFERED_COMMAND; + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:WINKEY_KEY_BUFFERED_COMMAND"); + #endif //DEBUG_WINKEY + break; + case 0x1a: + winkey_status = WINKEY_WAIT_BUFFERED_COMMAND; + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:WINKEY_WAIT_BUFFERED_COMMAND"); + #endif //DEBUG_WINKEY + break; + case 0x1b: + winkey_status = WINKEY_MERGE_COMMAND; + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:WINKEY_MERGE_COMMAND"); + #endif //DEBUG_WINKEY + break; + case 0x1c: // speed command - buffered + winkey_status = WINKEY_BUFFERED_SPEED_COMMAND; + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:WINKEY_BUFFERED_SPEED_COMMAND"); + #endif //DEBUG_WINKEY + break; + case 0x1d: + winkey_status = WINKEY_BUFFERED_HSCW_COMMAND; + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:WINKEY_BUFFERED_HSCW_COMMAND"); + #endif //DEBUG_WINKEY + break; + case 0x1e: // cancel buffered speed command - buffered + + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:WINKEY_CANCEL_BUFFERED_SPEED_COMMAND"); + #endif //DEBUG_WINKEY + + if (winkey_speed_state == WINKEY_BUFFERED_SPEED){ + add_to_send_buffer(SERIAL_SEND_BUFFER_WPM_CHANGE); + add_to_send_buffer(0); + add_to_send_buffer((byte)winkey_last_unbuffered_speed_wpm); + winkey_speed_state = WINKEY_UNBUFFERED_SPEED; + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:winkey_speed_state = WINKEY_UNBUFFERED_SPEED"); + #endif //DEBUG_WINKEY + } else { + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:WINKEY_CANCEL_BUFFERED_SPEED_COMMAND: no action"); + #endif //DEBUG_WINKEY + } + winkey_status = WINKEY_NO_COMMAND_IN_PROGRESS; + break; + case 0x1f: // buffered NOP - no need to do anything + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:1F NOP"); + #endif //DEBUG_WINKEY + break; + + #ifdef OPTION_WINKEY_EXTENDED_COMMANDS_WITH_DOLLAR_SIGNS + case 36: + winkey_status = WINKEY_EXTENDED_COMMAND; + beep(); + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:WINKEY_EXTENDED_COMMAND"); + #endif //DEBUG_WINKEY + break; + #endif //OPTION_WINKEY_EXTENDED_COMMANDS_WITH_DOLLAR_SIGNS + } //switch (incoming_serial_byte) + + #ifdef OPTION_WINKEY_STRICT_HOST_OPEN + } //if ((winkey_host_open) || (incoming_serial_byte == 0)) + #endif + + } + } else { //if (winkey_status == WINKEY_NO_COMMAND_IN_PROGRESS) + + if (winkey_status == WINKEY_UNSUPPORTED_COMMAND) { + winkey_parmcount--; + #ifdef DEBUG_WINKEY + debug_serial_port->print("service_winkey:WINKEY_UNSUPPORTED_COMMAND winkey_parmcount:"); + debug_serial_port->println(winkey_parmcount); + #endif //DEBUG_WINKEY + if (winkey_parmcount == 0) { + winkey_port_write(0xc0|winkey_sending|winkey_xoff,0); + winkey_status = WINKEY_NO_COMMAND_IN_PROGRESS; + #ifdef DEBUG_WINKEY + debug_serial_port->print("service_winkey:WINKEY_UNSUPPORTED_COMMAND:WINKEY_NO_COMMAND_IN_PROGRESS"); + debug_serial_port->println(winkey_parmcount); + #endif //DEBUG_WINKEY + } + + } + + //WINKEY_LOAD_SETTINGS_PARM_1_COMMAND IS 101 + if ((winkey_status > 100) && (winkey_status < 116)) { // Load Settings Command - this has 15 parameters, so we handle it a bit differently + winkey_load_settings_command(winkey_status,incoming_serial_byte); + winkey_status++; + if (winkey_status > 115) { + winkey_status = WINKEY_NO_COMMAND_IN_PROGRESS; + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:WINKEY_LOAD_SETTINGS_PARM_15->NOCMD"); + #endif //DEBUG_WINKEY + } + } + + #ifdef OPTION_WINKEY_EXTENDED_COMMANDS_WITH_DOLLAR_SIGNS + if (winkey_status == WINKEY_EXTENDED_COMMAND) { + //if (incoming_serial_byte != 36){ + //beep(); + //} else { + boop(); + beep(); + winkey_status = WINKEY_NO_COMMAND_IN_PROGRESS; + //} + } + #endif //OPTION_WINKEY_EXTENDED_COMMANDS_WITH_DOLLAR_SIGNS + + if (winkey_status == WINKEY_SET_PINCONFIG_COMMAND) { + winkey_set_pinconfig_command(incoming_serial_byte); + winkey_status = WINKEY_NO_COMMAND_IN_PROGRESS; + } + + if (winkey_status == WINKEY_MERGE_COMMAND) { + #ifdef FEATURE_MEMORIES + repeat_memory = 255; + #endif + add_to_send_buffer(SERIAL_SEND_BUFFER_PROSIGN); + add_to_send_buffer(incoming_serial_byte); + winkey_status = WINKEY_MERGE_PARM_2_COMMAND; + } else { + if (winkey_status == WINKEY_MERGE_PARM_2_COMMAND) { + add_to_send_buffer(incoming_serial_byte); + winkey_status = WINKEY_NO_COMMAND_IN_PROGRESS; + } + } + + if (winkey_status == WINKEY_UNBUFFERED_SPEED_COMMAND) { + winkey_unbuffered_speed_command(incoming_serial_byte); + winkey_status = WINKEY_NO_COMMAND_IN_PROGRESS; + } + + if (winkey_status == WINKEY_FARNSWORTH_COMMAND) { + winkey_farnsworth_command(incoming_serial_byte); + winkey_status = WINKEY_NO_COMMAND_IN_PROGRESS; + } + + if (winkey_status == WINKEY_HSCW_COMMAND) { + if (incoming_serial_byte == 0) { + #ifdef FEATURE_POTENTIOMETER + configuration.pot_activated = 1; + #endif + } else { + configuration.wpm = ((incoming_serial_byte*100)/5); + winkey_last_unbuffered_speed_wpm = configuration.wpm; + #ifdef OPTION_WINKEY_STRICT_EEPROM_WRITES_MAY_WEAR_OUT_EEPROM + config_dirty = 1; + #endif + } + winkey_status = WINKEY_NO_COMMAND_IN_PROGRESS; + } + + if (winkey_status == WINKEY_BUFFERED_SPEED_COMMAND) { + #ifdef DEBUG_WINKEY + debug_serial_port->print("service_winkey:BUFFERED_SPEED_CMD:send_buffer_bytes:"); + debug_serial_port->println(send_buffer_bytes); + #endif //DEBUG_WINKEY + add_to_send_buffer(SERIAL_SEND_BUFFER_WPM_CHANGE); + add_to_send_buffer(0); + add_to_send_buffer(incoming_serial_byte); + #ifdef DEBUG_WINKEY + debug_serial_port->print("service_winkey:BUFFERED_SPEED_CMD:send_buffer_bytes:"); + debug_serial_port->println(send_buffer_bytes); + #endif //DEBUG_WINKEY + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:WINKEY_BUFFERED_SPEED_COMMAND->NOCMD"); + #endif //DEBUG_WINKEY + winkey_status = WINKEY_NO_COMMAND_IN_PROGRESS; + } + + if (winkey_status == WINKEY_BUFFERED_HSCW_COMMAND) { + if (incoming_serial_byte > 1){ // the HSCW command is overloaded; 0 = buffered TX 1, 1 = buffered TX 2, > 1 = HSCW WPM + unsigned int send_buffer_wpm = ((incoming_serial_byte*100)/5); + add_to_send_buffer(SERIAL_SEND_BUFFER_WPM_CHANGE); + add_to_send_buffer(highByte(send_buffer_wpm)); + add_to_send_buffer(lowByte(send_buffer_wpm)); + } else { + add_to_send_buffer(SERIAL_SEND_BUFFER_TX_CHANGE); + add_to_send_buffer(incoming_serial_byte+1); + } + winkey_status = WINKEY_NO_COMMAND_IN_PROGRESS; + } + + if (winkey_status == WINKEY_KEY_BUFFERED_COMMAND) { + add_to_send_buffer(SERIAL_SEND_BUFFER_TIMED_KEY_DOWN); + add_to_send_buffer(incoming_serial_byte); + winkey_status = WINKEY_NO_COMMAND_IN_PROGRESS; + } + + if (winkey_status == WINKEY_WAIT_BUFFERED_COMMAND) { + add_to_send_buffer(SERIAL_SEND_BUFFER_TIMED_WAIT); + add_to_send_buffer(incoming_serial_byte); + winkey_status = WINKEY_NO_COMMAND_IN_PROGRESS; + } + + if (winkey_status == WINKEY_BUFFFERED_PTT_COMMMAND) { + if (incoming_serial_byte) { + add_to_send_buffer(SERIAL_SEND_BUFFER_PTT_ON); + } else { + add_to_send_buffer(SERIAL_SEND_BUFFER_PTT_OFF); + } + winkey_status = WINKEY_NO_COMMAND_IN_PROGRESS; + } + + if (winkey_status == WINKEY_POINTER_01_COMMAND) { // move input pointer to new positon in overwrite mode + winkey_buffer_pointer = incoming_serial_byte; + winkey_status = WINKEY_NO_COMMAND_IN_PROGRESS; + #ifdef DEBUG_WINKEY + debug_serial_port->print("service_winkey:PTR1_CMD->NOCMD winkey_buffer_pointer:"); + debug_serial_port->print(winkey_buffer_pointer); + debug_serial_port->print("send_buffer_bytes:"); + debug_serial_port->println(send_buffer_bytes); + #endif //DEBUG_WINKEY + } + + if (winkey_status == WINKEY_POINTER_02_COMMAND) { // move input pointer to new position in append mode + winkey_buffer_pointer = 0; + winkey_status = WINKEY_NO_COMMAND_IN_PROGRESS; + #ifdef DEBUG_WINKEY + debug_serial_port->print("service_winkey:PTR2_CMD->NOCMD send_buffer_bytes:"); + debug_serial_port->print(send_buffer_bytes); + debug_serial_port->print(" winkey_buffer_counter:"); + debug_serial_port->print(winkey_buffer_counter); + debug_serial_port->print(" winkey_buffer_pointer:"); + debug_serial_port->println(winkey_buffer_pointer); + #endif //DEBUG_WINKEY + } + + if (winkey_status == WINKEY_POINTER_03_COMMAND) { // add multiple nulls to buffer + #ifdef DEBUG_WINKEY + debug_serial_port->print("service_winkey:PTR3_CMD send_buffer_bytes:"); + debug_serial_port->print(send_buffer_bytes); + debug_serial_port->print(" winkey_buffer_counter:"); + debug_serial_port->print(winkey_buffer_counter); + debug_serial_port->print(" winkey_buffer_pointer:"); + debug_serial_port->println(winkey_buffer_pointer); + #endif //DEBUG_WINKEY + byte serial_buffer_position_to_overwrite; + for (byte x = incoming_serial_byte; x > 0; x--) { + if (winkey_buffer_pointer > 0) { + serial_buffer_position_to_overwrite = send_buffer_bytes - (winkey_buffer_counter - winkey_buffer_pointer) - 1; + if ((send_buffer_bytes) && (serial_buffer_position_to_overwrite < send_buffer_bytes )) { + send_buffer_array[serial_buffer_position_to_overwrite] = SERIAL_SEND_BUFFER_NULL; + } + winkey_buffer_pointer++; + } else { + add_to_send_buffer(SERIAL_SEND_BUFFER_NULL); + winkey_buffer_counter++; + } + } + winkey_status = WINKEY_NO_COMMAND_IN_PROGRESS; + #ifdef DEBUG_WINKEY + debug_serial_port->print("service_winkey:PTR3_CMD->NO_CMD send_buffer_bytes:"); + debug_serial_port->print(send_buffer_bytes); + debug_serial_port->print(" winkey_buffer_counter:"); + debug_serial_port->print(winkey_buffer_counter); + debug_serial_port->print(" winkey_buffer_pointer:"); + debug_serial_port->println(winkey_buffer_pointer); + #endif //DEBUG_WINKEY + } + + if (winkey_status == WINKEY_POINTER_COMMAND) { + switch (incoming_serial_byte) { + case 0x00: + winkey_buffer_counter = 0; + winkey_buffer_pointer = 0; + winkey_status = WINKEY_NO_COMMAND_IN_PROGRESS; + #ifdef DEBUG_WINKEY + debug_serial_port->print("service_winkey:PTR_CMD->NOCMD send_buffer_bytes:"); + debug_serial_port->print(send_buffer_bytes); + debug_serial_port->print(" winkey_buffer_counter:"); + debug_serial_port->print(winkey_buffer_counter); + debug_serial_port->print(" winkey_buffer_pointer:"); + debug_serial_port->println(winkey_buffer_pointer); + #endif //DEBUG_WINKEY + break; + case 0x01: + winkey_status = WINKEY_POINTER_01_COMMAND; + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:PTR1_CMD"); + #endif //DEBUG_WINKEY + break; + case 0x02: + winkey_status = WINKEY_POINTER_02_COMMAND; // move to new position in append mode + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:PTR2_CMD"); + #endif //DEBUG_WINKEY + break; + case 0x03: + winkey_status = WINKEY_POINTER_03_COMMAND; + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:PTR3_CMD"); + #endif //DEBUG_WINKEY + break; + default: + winkey_status = WINKEY_NO_COMMAND_IN_PROGRESS; + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:PTR_CMD->NOCMD"); + #endif //DEBUG_WINKEY + break; + } + } + + #ifdef OPTION_WINKEY_2_SUPPORT + if (winkey_status == WINKEY_SEND_MSG) { + if ((incoming_serial_byte > 0) && (incoming_serial_byte < 7)) { + add_to_send_buffer(SERIAL_SEND_BUFFER_MEMORY_NUMBER); + add_to_send_buffer(incoming_serial_byte - 1); + #ifdef FEATURE_MEMORIES + repeat_memory = 255; + #endif + } + winkey_status = WINKEY_NO_COMMAND_IN_PROGRESS; + } + #endif //OPTION_WINKEY_2_SUPPORT + + if (winkey_status == WINKEY_ADMIN_COMMAND) { + switch (incoming_serial_byte) { + case 0x00: + winkey_status = WINKEY_UNSUPPORTED_COMMAND; + winkey_parmcount = 1; + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:calib cmd UNSUPPORTED_COMMAND await 1 parm"); + #endif //DEBUG_WINKEY + break; // calibrate command + case 0x01: + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:WINKEY_ADMIN_COMMAND 0x01"); + #endif //DEBUG_WINKEY + #if defined(__AVR__) //#ifndef ARDUINO_SAM_DUE + asm volatile ("jmp 0"); /*wdt_enable(WDTO_30MS); while(1) {};*/ + #else + setup(); + #endif //__AVR__ + break; // reset command + case 0x02: // host open command - send version back to host + #ifdef OPTION_WINKEY_2_SUPPORT + winkey_port_write(WINKEY_2_REPORT_VERSION_NUMBER,1); + #else //OPTION_WINKEY_2_SUPPORT + winkey_port_write(WINKEY_1_REPORT_VERSION_NUMBER,1); + #endif //OPTION_WINKEY_2_SUPPORT + winkey_status = WINKEY_NO_COMMAND_IN_PROGRESS; + manual_ptt_invoke = 0; + winkey_host_open = 1; + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:ADMIN_CMD hostopen"); + #endif //DEBUG_WINKEY + #if defined(OPTION_WINKEY_BLINK_PTT_ON_HOST_OPEN) + blink_ptt_dits_and_dahs(".."); + #else + boop_beep(); + #endif + break; + case 0x03: // host close command + winkey_status = WINKEY_NO_COMMAND_IN_PROGRESS; + manual_ptt_invoke = 0; + winkey_host_open = 0; + #ifdef OPTION_WINKEY_SEND_VERSION_ON_HOST_CLOSE + #ifdef OPTION_WINKEY_2_SUPPORT + winkey_port_write(WINKEY_2_REPORT_VERSION_NUMBER,1); + #else //OPTION_WINKEY_2_SUPPORT + winkey_port_write(WINKEY_1_REPORT_VERSION_NUMBER,1); + #endif //OPTION_WINKEY_2_SUPPORT + #endif //OPTION_WINKEY_SEND_VERSION_ON_HOST_CLOSE + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:ADMIN_CMD hostclose"); + #endif //DEBUG_WINKEY + beep_boop(); + config_dirty = 1; + #if defined(OPTION_WINKEY_2_SUPPORT) && !defined(OPTION_WINKEY_2_HOST_CLOSE_NO_SERIAL_PORT_RESET) + primary_serial_port->end(); + primary_serial_port->begin(1200); + #endif + break; + case 0x04: // echo command + winkey_status = WINKEY_ADMIN_COMMAND_ECHO; + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:ADMIN_CMD_ECHO"); + #endif //DEBUG_WINKEY + break; + case 0x05: // paddle A2D + winkey_port_write(WINKEY_RETURN_THIS_FOR_ADMIN_PADDLE_A2D,0); + winkey_status = WINKEY_NO_COMMAND_IN_PROGRESS; + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:ADMIN_CMD paddleA2D"); + #endif //DEBUG_WINKEY + break; + case 0x06: // speed A2D + winkey_port_write(WINKEY_RETURN_THIS_FOR_ADMIN_SPEED_A2D,0); + winkey_status = WINKEY_NO_COMMAND_IN_PROGRESS; + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:ADMIN_CMD speedA2D"); + #endif //DEBUG_WINKEY + break; + case 0x07: // Get values + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:ADMIN_CMD winkey_admin_get_values"); + #endif //DEBUG_WINKEY + winkey_admin_get_values_command(); + winkey_status = WINKEY_NO_COMMAND_IN_PROGRESS; + break; + case 0x08: // reserved + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:ADMIN_CMD0x08reserved-WTF"); + #endif //DEBUG_WINKEY + winkey_status = WINKEY_NO_COMMAND_IN_PROGRESS; + break; + case 0x09: // get cal + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:ADMIN_CMDgetcal"); + #endif //DEBUG_WINKEY + winkey_port_write(WINKEY_RETURN_THIS_FOR_ADMIN_GET_CAL,0); + winkey_status = WINKEY_NO_COMMAND_IN_PROGRESS; + break; + #ifdef OPTION_WINKEY_2_SUPPORT + case 0x0a: // set wk1 mode (10) + wk2_mode = 1; + winkey_status = WINKEY_NO_COMMAND_IN_PROGRESS; + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:ADMIN_CMD wk2_mode1"); + #endif //DEBUG_WINKEY + break; + case 0x0b: // set wk2 mode (11) + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:ADMIN_CMD wk2_mode2"); + #endif //DEBUG_WINKEY + beep(); + beep(); + wk2_mode = 2; + winkey_status = WINKEY_NO_COMMAND_IN_PROGRESS; + break; + case 0x0c: // download EEPPROM 256 bytes (12) + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:ADMIN_CMD winkey_eeprom_download"); + #endif //DEBUG_WINKEY + winkey_eeprom_download(); + winkey_status = WINKEY_NO_COMMAND_IN_PROGRESS; + break; + case 0x0d: // upload EEPROM 256 bytes (13) + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:ADMIN_CMD uploadEEPROM"); + #endif //DEBUG_WINKEY + winkey_status = WINKEY_UNSUPPORTED_COMMAND; // upload EEPROM 256 bytes + winkey_parmcount = 256; + break; + case 0x0e: //(14) + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:ADMIN_CMD WINKEY_SEND_MSG"); + #endif //DEBUG_WINKEY + winkey_status = WINKEY_SEND_MSG; + break; + case 0x0f: // load xmode (15) + winkey_status = WINKEY_UNSUPPORTED_COMMAND; + winkey_parmcount = 1; + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:ADMIN_CMD loadxmode"); + #endif //DEBUG_WINKEY + break; + case 0x10: // reserved (16) + winkey_port_write(zero,0); + winkey_status = WINKEY_NO_COMMAND_IN_PROGRESS; + break; + case 0x11: // set high baud rate (17) + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:ADMIN_CMD sethighbaudrate"); + #endif //DEBUG_WINKEY + primary_serial_port->end(); + primary_serial_port->begin(9600); + winkey_status = WINKEY_NO_COMMAND_IN_PROGRESS; + break; + case 0x12: // set low baud rate (18) + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:ADMIN_CMD setlowbaudrate"); + #endif //DEBUG_WINKEY + primary_serial_port->end(); + primary_serial_port->begin(1200); + winkey_status = WINKEY_NO_COMMAND_IN_PROGRESS; + break; + #endif //OPTION_WINKEY_2_SUPPORT + + #ifdef FEATURE_SO2R_BASE + case 0xF0: // Send SO2R device information + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:ADMIN_CMD getSO2Rdeviceinfo"); + #endif + + static const uint8_t device_info[] = { 0xAA, 0x55, 0xCC, 0x33, // Header + 1, 0, 0, // SO2R Major, minor, patch + 1, 0, // Protocol major, minor + 0, // capabilities - bit 0 is stereo reverse, others undefined + }; + + for (uint8_t i=0; iprintln("service_winkey:NO-OP"); + winkey_status = WINKEY_NO_COMMAND_IN_PROGRESS; + break; + + #endif //FEATURE_SO2R_BASE + + default: + winkey_status = WINKEY_NO_COMMAND_IN_PROGRESS; + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:ADMIN_CMD->NOCMD"); + #endif //DEBUG_WINKEY + break; + } + } else { + if (winkey_status == WINKEY_ADMIN_COMMAND_ECHO) { + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:ADMIN_CMD echoabyte."); + #endif //DEBUG_WINKEY + winkey_port_write(incoming_serial_byte,1); + winkey_status = WINKEY_NO_COMMAND_IN_PROGRESS; + } + } + + if (winkey_status == WINKEY_KEYING_COMPENSATION_COMMAND) { + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:ADMIN_CMD WINKEY_KEYING_COMPENSATION_COMMAND byte"); + #endif //DEBUG_WINKEY + configuration.keying_compensation = incoming_serial_byte; + winkey_status = WINKEY_NO_COMMAND_IN_PROGRESS; + } + + if (winkey_status == WINKEY_FIRST_EXTENSION_COMMAND) { + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:ADMIN_COMMAND WINKEY_FIRST_EXTENSION_COMMAND byte"); + #endif //DEBUG_WINKEY + first_extension_time = incoming_serial_byte; + #ifdef DEBUG_WINKEY_PROTOCOL_USING_CW + send_char('X',KEYER_NORMAL); + #endif + winkey_status = WINKEY_NO_COMMAND_IN_PROGRESS; + } + + if (winkey_status == WINKEY_PAUSE_COMMAND) { + if (incoming_serial_byte) { + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:ADMIN_CMD WINKEY_PAUSE_COMMANDpause"); + #endif //DEBUG_WINKEY + pause_sending_buffer = 1; + } else { + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey:ADMIN_CMD WINKEY_PAUSE_COMMANDunpause"); + #endif //DEBUG_WINKEY + pause_sending_buffer = 0; + } + winkey_status = WINKEY_NO_COMMAND_IN_PROGRESS; + } + + if (winkey_status == WINKEY_KEY_COMMAND) { + #ifdef FEATURE_MEMORIES + repeat_memory = 255; + #endif + sending_mode = AUTOMATIC_SENDING; + if (incoming_serial_byte) { + tx_and_sidetone_key(1); + } else { + tx_and_sidetone_key(0); + } + winkey_status = WINKEY_NO_COMMAND_IN_PROGRESS; + } + + if (winkey_status == WINKEY_DAH_TO_DIT_RATIO_COMMAND) { + winkey_dah_to_dit_ratio_command(incoming_serial_byte); + winkey_status = WINKEY_NO_COMMAND_IN_PROGRESS; + } + + if (winkey_status == WINKEY_WEIGHTING_COMMAND) { + winkey_weighting_command(incoming_serial_byte); + winkey_status = WINKEY_NO_COMMAND_IN_PROGRESS; + } + + if (winkey_status == WINKEY_PTT_TIMES_PARM1_COMMAND) { + winkey_ptt_times_parm1_command(incoming_serial_byte); + winkey_status = WINKEY_PTT_TIMES_PARM2_COMMAND; + } else { + if (winkey_status == WINKEY_PTT_TIMES_PARM2_COMMAND) { + winkey_ptt_times_parm2_command(incoming_serial_byte); + winkey_status = WINKEY_NO_COMMAND_IN_PROGRESS; + } + } + + if (winkey_status == WINKEY_SET_POT_PARM1_COMMAND) { + winkey_set_pot_parm1_command(incoming_serial_byte); + winkey_status = WINKEY_SET_POT_PARM2_COMMAND; + } else { + if (winkey_status == WINKEY_SET_POT_PARM2_COMMAND) { + winkey_set_pot_parm2_command(incoming_serial_byte); + winkey_status = WINKEY_SET_POT_PARM3_COMMAND; + } else { + if (winkey_status == WINKEY_SET_POT_PARM3_COMMAND) { // third parm is max read value from pot, depending on wiring + winkey_set_pot_parm3_command(incoming_serial_byte); // WK2 protocol just ignores this third parm + winkey_status = WINKEY_NO_COMMAND_IN_PROGRESS; // this is taken care of in winkey_set_pot_parm3() + } + } + } + + if (winkey_status == WINKEY_SETMODE_COMMAND) { + winkey_setmode_command(incoming_serial_byte); + winkey_status = WINKEY_NO_COMMAND_IN_PROGRESS; + } + + if (winkey_status == WINKEY_SOFTWARE_PADDLE_COMMAND) { + #ifdef FEATURE_MEMORIES + repeat_memory = 255; + #endif + switch (incoming_serial_byte) { + case 0: winkey_dit_invoke = 0; winkey_dah_invoke = 0; break; + case 1: winkey_dit_invoke = 0; winkey_dah_invoke = 1; break; + case 2: winkey_dit_invoke = 1; winkey_dah_invoke = 0; break; + case 3: winkey_dah_invoke = 1; winkey_dit_invoke = 1; break; + } + winkey_status = WINKEY_NO_COMMAND_IN_PROGRESS; + } + + if (winkey_status == WINKEY_SIDETONE_FREQ_COMMAND) { + winkey_sidetone_freq_command(incoming_serial_byte); + winkey_status = WINKEY_NO_COMMAND_IN_PROGRESS; + } + + } // else (winkey_status == WINKEY_NO_COMMAND_IN_PROGRESS) + } // if (action == SERVICE_SERIAL_BYTE + + +} +#endif //FEATURE_WINKEY_EMULATION + +//------------------------------------------------------------------------------------------------------- +#ifdef FEATURE_COMMAND_LINE_INTERFACE +void service_command_line_interface(PRIMARY_SERIAL_CLS * port_to_use) { + + static byte cli_wait_for_cr_flag = 0; + + if (serial_backslash_command == 0) { + incoming_serial_byte = uppercase(incoming_serial_byte); + if ((incoming_serial_byte != 92) && (incoming_serial_byte != 27)) { // we do not have a backslash or ESC + + + #if !defined(OPTION_EXCLUDE_MILL_MODE) + if (configuration.cli_mode == CLI_NORMAL_MODE){ + if (cli_prosign_flag) { + add_to_send_buffer(SERIAL_SEND_BUFFER_PROSIGN); + cli_prosign_flag = 0; + } + if (cli_wait_for_cr_to_send_cw) { + if (cli_wait_for_cr_flag == 0) { + if (incoming_serial_byte > 31) { + #ifdef DEBUG_CHECK_SERIAL + port_to_use->println(F("check_serial: add_to_send_buffer(SERIAL_SEND_BUFFER_HOLD_SEND)")); + #endif + add_to_send_buffer(SERIAL_SEND_BUFFER_HOLD_SEND); + cli_wait_for_cr_flag = 1; + } + } else { + if (incoming_serial_byte == 13) { + #ifdef DEBUG_CHECK_SERIAL + port_to_use->println(F("check_serial: add_to_send_buffer(SERIAL_SEND_BUFFER_HOLD_SEND_RELEASE)")); + #endif + add_to_send_buffer(SERIAL_SEND_BUFFER_HOLD_SEND_RELEASE); + cli_wait_for_cr_flag = 0; + } + } + } + add_to_send_buffer(incoming_serial_byte); + } else { // configuration.cli_mode != CLI_NORMAL_MODE + if (configuration.cli_mode == CLI_MILL_MODE_KEYBOARD_RECEIVE){ + port_to_use->write(incoming_serial_byte); + if (incoming_serial_byte == 13){port_to_use->println();} + #ifdef FEATURE_SD_CARD_SUPPORT + sd_card_log("",incoming_serial_byte); + #endif + + } else { // configuration.cli_mode == CLI_MILL_MODE_PADDLE_SEND + port_to_use->println(); + port_to_use->println(); + if (incoming_serial_byte == 13){port_to_use->println();} + port_to_use->write(incoming_serial_byte); + configuration.cli_mode = CLI_MILL_MODE_KEYBOARD_RECEIVE; + #ifdef FEATURE_SD_CARD_SUPPORT + sd_card_log("\r\nRX:",0); + sd_card_log("",incoming_serial_byte); + #endif + } + } //if (configuration.cli_mode == CLI_NORMAL_MODE) + + #else //!defined(OPTION_EXCLUDE_MILL_MODE) + + if (cli_prosign_flag) { + add_to_send_buffer(SERIAL_SEND_BUFFER_PROSIGN); + cli_prosign_flag = 0; + } + if (cli_wait_for_cr_to_send_cw) { + if (cli_wait_for_cr_flag == 0) { + if (incoming_serial_byte > 31) { + #ifdef DEBUG_CHECK_SERIAL + port_to_use->println(F("check_serial: add_to_send_buffer(SERIAL_SEND_BUFFER_HOLD_SEND)")); + #endif + add_to_send_buffer(SERIAL_SEND_BUFFER_HOLD_SEND); + cli_wait_for_cr_flag = 1; + } + } else { + if (incoming_serial_byte == 13) { + #ifdef DEBUG_CHECK_SERIAL + port_to_use->println(F("check_serial: add_to_send_buffer(SERIAL_SEND_BUFFER_HOLD_SEND_RELEASE)")); + #endif + add_to_send_buffer(SERIAL_SEND_BUFFER_HOLD_SEND_RELEASE); + cli_wait_for_cr_flag = 0; + } + } + } + add_to_send_buffer(incoming_serial_byte); + + #endif // !defined(OPTION_EXCLUDE_MILL_MODE) + + #ifdef FEATURE_MEMORIES + if ((incoming_serial_byte != 13) && (incoming_serial_byte != 10)) {repeat_memory = 255;} + #endif + } else { //if ((incoming_serial_byte != 92) && (incoming_serial_byte != 27)) -- we got a backslash or ESC + if (incoming_serial_byte == 92){ // backslash + serial_backslash_command = 1; + port_to_use->write(incoming_serial_byte); + } else { // escape + clear_send_buffer(); + #ifdef FEATURE_MEMORIES + play_memory_prempt = 1; + repeat_memory = 255; + #endif + } + } + } else { // (serial_backslash_command == 0) -- we already got a backslash + incoming_serial_byte = uppercase(incoming_serial_byte); + port_to_use->write(incoming_serial_byte); + process_serial_command(port_to_use); + serial_backslash_command = 0; + port_to_use->println(); + } +} +#endif //FEATURE_COMMAND_LINE_INTERFACE + +//------------------------------------------------------------------------------------------------------- + +#if defined(FEATURE_SERIAL) +void check_serial(){ + + + #if defined(FEATURE_TRAINING_COMMAND_LINE_INTERFACE) + if (check_serial_override){return;} + #endif + + #ifdef DEBUG_SERIAL_SEND_CW_CALLOUT + byte debug_serial_send_cw[2]; + byte previous_tx = 0; + byte previous_sidetone = 0; + #endif + + #ifdef DEBUG_LOOP + debug_serial_port->println(F("loop: entering check_serial")); + #endif + + + #ifdef FEATURE_WINKEY_EMULATION + if (primary_serial_port_mode == SERIAL_WINKEY_EMULATION) { + service_winkey(WINKEY_HOUSEKEEPING); + } + #endif + + + while (primary_serial_port->available() > 0) { + incoming_serial_byte = primary_serial_port->read(); + #ifdef FEATURE_SLEEP + last_activity_time = millis(); + #endif //FEATURE_SLEEP + #ifdef FEATURE_LCD_BACKLIGHT_AUTO_DIM + last_active_time = millis(); + #endif //FEATURE_LCD_BACKLIGHT_AUTO_DIM + #ifdef DEBUG_SERIAL_SEND_CW_CALLOUT + debug_serial_send_cw[0] = (incoming_serial_byte & 0xf0)>>4; + debug_serial_send_cw[1] = incoming_serial_byte & 0x0f; + for (byte x = 0;x < 2;x++) { + if (debug_serial_send_cw[x] < 10) { + debug_serial_send_cw[x] = debug_serial_send_cw[x] + 48; + } else { + debug_serial_send_cw[x] = debug_serial_send_cw[x] + 55; + } + } + previous_tx = key_tx; + key_tx = 0; + previous_sidetone = configuration.sidetone_mode; + configuration.sidetone_mode = SIDETONE_ON; + send_char(debug_serial_send_cw[0],0); + send_char(debug_serial_send_cw[1],0); + key_tx = previous_tx; + configuration.sidetone_mode = previous_sidetone; + #endif + + #if !defined(FEATURE_WINKEY_EMULATION) && !defined(FEATURE_COMMAND_LINE_INTERFACE) + primary_serial_port->println(F("No serial features enabled...")); + #endif + + #if defined(FEATURE_WINKEY_EMULATION) && defined(FEATURE_COMMAND_LINE_INTERFACE) + if (primary_serial_port_mode == SERIAL_WINKEY_EMULATION) { + service_winkey(SERVICE_SERIAL_BYTE); + } else { + service_command_line_interface(primary_serial_port); + } + #else //defined(FEATURE_WINKEY_EMULATION) && defined(FEATURE_COMMAND_LINE_INTERFACE) + #ifdef FEATURE_COMMAND_LINE_INTERFACE + service_command_line_interface(primary_serial_port); + #endif //FEATURE_COMMAND_LINE_INTERFACE + #ifdef FEATURE_WINKEY_EMULATION + service_winkey(SERVICE_SERIAL_BYTE); + #endif //FEATURE_WINKEY_EMULATION + #endif //defined(FEATURE_WINKEY_EMULATION) && defined(FEATURE_COMMAND_LINE_INTERFACE) + + } //while (primary_serial_port->available() > 0) + + #ifdef FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + while (secondary_serial_port->available() > 0) { + incoming_serial_byte = secondary_serial_port->read(); + #ifdef FEATURE_SLEEP + last_activity_time = millis(); + #endif //FEATURE_SLEEP + #ifdef FEATURE_LCD_BACKLIGHT_AUTO_DIM + last_active_time = millis(); + #endif //FEATURE_LCD_BACKLIGHT_AUTO_DIM + #ifdef DEBUG_SERIAL_SEND_CW_CALLOUT + debug_serial_send_cw[0] = (incoming_serial_byte & 0xf0)>>4; + debug_serial_send_cw[1] = incoming_serial_byte & 0x0f; + for (byte x = 0;x < 2;x++) { + if (debug_serial_send_cw[x] < 10) { + debug_serial_send_cw[x] = debug_serial_send_cw[x] + 48; + } else { + debug_serial_send_cw[x] = debug_serial_send_cw[x] + 55; + } + } + previous_tx = key_tx; + key_tx = 0; + previous_sidetone = configuration.sidetone_mode; + configuration.sidetone_mode = SIDETONE_ON; + send_char(debug_serial_send_cw[0],0); + send_char(debug_serial_send_cw[1],0); + key_tx = previous_tx; + configuration.sidetone_mode = previous_sidetone; + #endif //DEBUG_SERIAL_SEND_CW_CALLOUT + service_command_line_interface(secondary_serial_port); + } // while (secondary_serial_port->available() > 0) + #endif //FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + + +} +#endif //defined(FEATURE_SERIAL) + +//--------------------------------------------------------------------- + +#if defined(FEATURE_SERIAL_HELP) && defined(FEATURE_SERIAL) && defined(FEATURE_COMMAND_LINE_INTERFACE) +void serial_page_pause(PRIMARY_SERIAL_CLS * port_to_use,byte seconds_timeout){ + + + unsigned long pause_start_time = millis(); + + port_to_use->println("\r\nPress enter..."); + while ((!port_to_use->available()) && (((millis()-pause_start_time)/1000) < seconds_timeout)){} + while (port_to_use->available()){port_to_use->read();} + + +} +#endif //defined(FEATURE_SERIAL_HELP) && defined(FEATURE_SERIAL) && defined(FEATURE_COMMAND_LINE_INTERFACE) + +//--------------------------------------------------------------------- + +#if defined(FEATURE_SERIAL_HELP) && defined(FEATURE_SERIAL) && defined(FEATURE_COMMAND_LINE_INTERFACE) +void print_serial_help(PRIMARY_SERIAL_CLS * port_to_use,byte paged_help){ + + port_to_use->println(F("\n\rK3NG Keyer Help\n\r")); + port_to_use->println(F("CLI commands:")); + port_to_use->println(F("\\#\t\t: Play memory # x")); //Upper case to first letter only(WD9DMP) + port_to_use->println(F("\\A\t\t: Iambic A")); + port_to_use->println(F("\\B\t\t: Iambic B")); + port_to_use->println(F("\\C\t\t: Single paddle")); //Upper case to first letter only(WD9DMP) + #ifndef OPTION_NO_ULTIMATIC + port_to_use->println(F("\\D\t\t: Ultimatic")); + #endif + port_to_use->println(F("\\E####\t\t: Set serial number to ####")); + port_to_use->println(F("\\F####\t\t: Set sidetone to #### hz")); + port_to_use->println(F("\\G\t\t: Switch to bug mode")); //Upper case to first letter only(WD9DMP) + #ifdef FEATURE_HELL + port_to_use->println(F("\\H\t\t: Toggle CW / Hell mode")); + #endif + port_to_use->println(F("\\I\t\t: TX line disable/enable")); + port_to_use->println(F("\\J###\t\t: Set dah to dit ratio")); //Upper case to first letter only(WD9DMP) + #ifdef FEATURE_TRAINING_COMMAND_LINE_INTERFACE + port_to_use->println(F("\\K\t\t: Training")); + #endif + port_to_use->println(F("\\L##\t\t: Set weighting (50 = normal)")); + #ifdef FEATURE_FARNSWORTH + port_to_use->println(F("\\M###\t\t: Set Farnsworth speed")); //Upper case to first letter only(WD9DMP) + #endif + if (paged_help) {serial_page_pause(port_to_use,10);} + port_to_use->println(F("\\N\t\t: Toggle paddle reverse")); //Upper case to first letter only(WD9DMP) + port_to_use->println(F("\\O\t\t: Toggle sidetone on/off")); //Added missing command (WD9DMP) + port_to_use->println(F("\\Px\t: Program memory #x with ")); //Upper case to first letter only(WD9DMP) + port_to_use->println(F("\\Q#[#]\t\t: Switch to QRSS mode with ## second dit length")); + port_to_use->println(F("\\R\t\t: Switch to regular speed (wpm) mode")); + port_to_use->println(F("\\S\t\t: Status report")); //Upper case to first letter only(WD9DMP) + port_to_use->println(F("\\T\t\t: Tune mode")); + port_to_use->println(F("\\U\t\t: PTT toggle")); + #ifdef FEATURE_POTENTIOMETER + port_to_use->println(F("\\V\t\t: Potentiometer activate/deactivate")); + #endif //FEATURE_POTENTIOMETER + port_to_use->println(F("\\W#[#][#]\t: Change WPM to ###")); + port_to_use->println(F("\\X#\t\t: Switch to transmitter #")); + port_to_use->println(F("\\Y#\t\t: Change wordspace to #")); + #ifdef FEATURE_AUTOSPACE + port_to_use->println(F("\\Z\t\t: Autospace on/off")); + #endif //FEATURE_AUTOSPACE + port_to_use->println(F("\\+\t\t: Create prosign")); //Changed description to match change log at top (WD9DMP) + port_to_use->println(F("\\!##\t\t: Repeat play memory")); //Added missing command(WD9DMP) + port_to_use->println(F("\\|####\t\t: Set memory repeat (milliseconds)")); //Added missing command(WD9DMP) + port_to_use->println(F("\\*\t\t: Toggle paddle echo")); //Added missing command(WD9DMP) + port_to_use->println(F("\\`\t\t: Toggle straight key echo")); //Added missing command(WD9DMP) + port_to_use->println(F("\\^\t\t: Toggle wait for carriage return to send CW / send CW immediately")); //Added missing command(WD9DMP) + #ifdef FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING + port_to_use->println(F("\\&\t\t: Toggle CMOS Super Keyer timing on/off")); //Upper case to first letter only(WD9DMP) + port_to_use->println(F("\\%##\t\t: Set CMOS Super Keyer timing %")); //Upper case to first letter only(WD9DMP) + #endif //FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING + port_to_use->println(F("\\.\t\t: Toggle dit buffer on/off")); + port_to_use->println(F("\\-\t\t: Toggle dah buffer on/off")); + port_to_use->println(F("\\~\t\t: Reset unit")); //Added missing command(WD9DMP) + port_to_use->println(F("\\:\t\t: Toggle cw send echo")); //Added missing command(WD9DMP) + port_to_use->println(F("\\{\t\t: QLF mode on/off")); //Added missing command(WD9DMP) + port_to_use->println(F("\\>\t\t: Send serial number, then increment")); //Added missing command(WD9DMP) + port_to_use->println(F("\\<\t\t: Send current serial number")); //Added missing command(WD9DMP) + port_to_use->println(F("\\(\t\t: Send current serial number in cut numbers")); //Added missing command(WD9DMP) + port_to_use->println(F("\\)\t\t: Send serial number with cut numbers, then increment")); //Added missing command(WD9DMP) + port_to_use->println(F("\\[\t\t: Set quiet paddle interruption - 0 to 20 element lengths; 0 = off")); //Added missing command(WD9DMP) + port_to_use->println(F("\\]\t\t: PTT disable/enable")); + #ifdef FEATURE_AMERICAN_MORSE + port_to_use->println(F("\\=\t\t: Toggle American Morse mode")); //Added missing command(WD9DMP) + #endif + #ifdef FEATURE_POTENTIOMETER + port_to_use->println(F("\\}####\t\t: Set Potentiometer range")); + #endif //FEATURE_POTENTIOMETER + #if !defined(OPTION_EXCLUDE_MILL_MODE) + port_to_use->println(F("\\@\t\t: Mill Mode")); + #endif + port_to_use->println(F("\\\\\t\t: Empty keyboard send buffer")); //Moved to end of command list (WD9DMP) + if (paged_help) {serial_page_pause(port_to_use,10);} + //Memory Macros below (WD9DMP) + #ifdef FEATURE_MEMORY_MACROS + port_to_use->println(F("\nMemory Macros:")); + port_to_use->println(F("\\#\t\t: Jump to memory #")); + port_to_use->println(F("\\C\t\t: Send serial number with cut numbers, then increment"));//Added "then increment" (WD9DMP) + port_to_use->println(F("\\D###\t\t: Delay for ### seconds")); + port_to_use->println(F("\\E\t\t: Send serial number, then increment"));//Added "then increment" (WD9DMP) + port_to_use->println(F("\\F####\t\t: Set sidetone to #### hz")); + #ifdef FEATURE_HELL + port_to_use->println(F("\\H\t\t: Switch to Hell mode")); + #endif //FEATURE_HELL + port_to_use->println(F("\\I\t\t: Insert memory #"));//Added missing macro (WD9DMP) + #ifdef FEATURE_HELL + port_to_use->println(F("\\L\t\t: Switch to CW (from Hell mode)")); + #endif //FEATURE_HELL + port_to_use->println(F("\\N\t\t: Decrement serial number - do not send"));//Added "do not send" (WD9DMP) + port_to_use->println(F("\\Q##\t\t: Switch to QRSS with ## second dit length")); + port_to_use->println(F("\\R\t\t: Switch to regular speed mode")); + port_to_use->println(F("\\S\t\t: Insert space"));//Added missing macro (WD9DMP) + port_to_use->println(F("\\T###\t\t: Transmit for ### seconds")); + port_to_use->println(F("\\U\t\t: Key PTT")); //Upper case to first letter only(WD9DMP) + port_to_use->println(F("\\V\t\t: Unkey PTT")); //Upper case to first letter only(WD9DMP) + port_to_use->println(F("\\W###\t\t: Change WPM to ###")); + port_to_use->println(F("\\X#\t\t: Switch to transmitter #")); + port_to_use->println(F("\\Y#\t\t: Increase speed # WPM")); + port_to_use->println(F("\\Z#\t\t: Decrease speed # WPM")); + port_to_use->println(F("\\+\t\t: Prosign the next two characters"));//Added "the next two characters" (WD9DMP) + #endif //FEATURE_MEMORY_MACROS + + #if !defined(OPTION_EXCLUDE_EXTENDED_CLI_COMMANDS) + if (paged_help) {serial_page_pause(port_to_use,10);} + port_to_use->println(F("\r\n\\:\tExtended CLLI commands")); + port_to_use->println(F("\t\teepromdump\t\t- do a byte dump of EEPROM for troubleshooting")); + port_to_use->println(F("\t\tsaveeeprom \t- store EEPROM in a file")); + port_to_use->println(F("\t\tloadeeprom \t- load into EEPROM from a file")); + port_to_use->println(F("\t\tprintlog\t\t- print the SD card log")); + port_to_use->println(F("\t\tclearlog\t\t- clear the SD card log")); + port_to_use->println(F("\t\tls \t\t- list files in SD card directory")); + port_to_use->println(F("\t\tcat \t\t- print filename on SD card")); + port_to_use->println(F("\t\tpl \t- Set PTT lead time")); + port_to_use->println(F("\t\tpt \t- Set PTT tail time")); + port_to_use->println(F("\t\tcomp \t- Set keying compensation time")); + #endif //OPTION_EXCLUDE_EXTENDED_CLI_COMMANDS + + + port_to_use->println(F("\r\n\\/\t\t: Paginated help")); + +} +#endif +//--------------------------------------------------------------------- + +#if defined(FEATURE_SERIAL) && defined(FEATURE_COMMAND_LINE_INTERFACE) +void process_serial_command(PRIMARY_SERIAL_CLS * port_to_use) { + + int user_input_temp = 0; + + #ifdef FEATURE_AMERICAN_MORSE + static int previous_dah_to_dit_ratio = 300; + #endif //FEATURE_AMERICAN_MORSE + + //port_to_use->println(); + switch (incoming_serial_byte) { + case '~': + #if defined(__AVR__) + asm volatile ("jmp 0"); /*wdt_enable(WDTO_30MS); while(1) {} ;*/ + #else //__AVR__ + setup(); + #endif //__AVR__ + break; // ~ - reset unit + case '*': // * - paddle echo on / off + if (cli_paddle_echo) { + cli_paddle_echo = 0; + } else { + cli_paddle_echo = 1; + } + break; + #if defined(FEATURE_STRAIGHT_KEY_ECHO) + case '`': + if (cli_straight_key_echo) { + cli_straight_key_echo = 0; + } else { + cli_straight_key_echo = 1; + } + break; + #endif //FEATURE_STRAIGHT_KEY_ECHO + case '+': cli_prosign_flag = 1; break; + #if defined(FEATURE_SERIAL_HELP) + case '?': print_serial_help(port_to_use,0); break; // ? = print help + case '/': print_serial_help(port_to_use,1); break; // / = paged help + #endif //FEATURE_SERIAL_HELP + case 'A': // A - Iambic A mode + configuration.keyer_mode = IAMBIC_A; + configuration.dit_buffer_off = 0; + configuration.dah_buffer_off = 0; + config_dirty = 1; + port_to_use->println(F("\r\nIambic A")); + break; + case 'B': // B - Iambic B mode + configuration.keyer_mode = IAMBIC_B; + configuration.dit_buffer_off = 0; + configuration.dah_buffer_off = 0; + config_dirty = 1; + port_to_use->println(F("\r\nIambic B")); + break; + case 'C': // C - single paddle mode + configuration.keyer_mode = SINGLE_PADDLE; + config_dirty = 1; port_to_use->println(F("\r\nSingle Paddle")); + break; + + #ifndef OPTION_NO_ULTIMATIC + case 'D': // D - Ultimatic mode + configuration.keyer_mode = ULTIMATIC; + configuration.dit_buffer_off = 1; + configuration.dah_buffer_off = 1; + config_dirty = 1; + port_to_use->println(F("\r\nUltimatic")); + break; + #endif + case 'E': serial_set_serial_number(port_to_use); break; // E - set serial number + case 'F': serial_set_sidetone_freq(port_to_use); break; // F - set sidetone frequency + case 'G': configuration.keyer_mode = BUG; config_dirty = 1; port_to_use->println(F("\r\nBug")); break; // G - Bug mode + #ifdef FEATURE_HELL + case 'H': // H - Hell mode + if ((char_send_mode == CW) || (char_send_mode == AMERICAN_MORSE)){ + char_send_mode = HELL; port_to_use->println(F("\r\nHell Mode")); + } else { + char_send_mode = CW; port_to_use->println(F("\r\nCW Mode")); + } + break; + #endif //FEATURE_HELL + #ifdef FEATURE_AMERICAN_MORSE + case '=': // = - American Morse + if ((char_send_mode == CW) || (char_send_mode == HELL)){ + char_send_mode = AMERICAN_MORSE; port_to_use->println(F("\r\nAmerican Morse Mode")); + previous_dah_to_dit_ratio = configuration.dah_to_dit_ratio; + configuration.dah_to_dit_ratio = 200; + } else { + char_send_mode = CW; port_to_use->println(F("\r\nInternational CW Mode")); + configuration.dah_to_dit_ratio = previous_dah_to_dit_ratio; + } + break; + #endif //FEATURE_AMERICAN_MORSE + + + case 'I': // I - transmit line on/off + //port_to_use->print(F("\r\nTX o")); //WD9DMP-1 + if (key_tx && keyer_machine_mode != KEYER_COMMAND_MODE) { //Added check that keyer is NOT in command mode or keyer might be enabled for paddle commands (WD9DMP-1) + key_tx = 0; + port_to_use->println(F("\r\nTX off")); //WD9DMP-1 + } else if (!key_tx && keyer_machine_mode != KEYER_COMMAND_MODE) { //Added check that keyer is NOT in command mode or keyer might be enabled for paddle commands (WD9DMP-1) + key_tx = 1; + port_to_use->println(F("\r\nTX on")); //WD9DMP-1 + } + else {port_to_use->print(F("\r\nERROR: Keyer in Command Mode\r\n"));} //Print error message if keyer in Command Mode and user tries to change tx line(s) on/off. (WD9DMP-1) + break; + #ifdef FEATURE_MEMORIES + case 33: repeat_play_memory(port_to_use); break; // ! - repeat play + case 124: serial_set_memory_repeat(port_to_use); break; // | - set memory repeat time + case 48: serial_play_memory(9); break; // 0 - play memory 10 + case 49: // 1-9 - play memory # + case 50: + case 51: + case 52: + case 53: + case 54: + case 55: + case 56: + case 57: serial_play_memory(incoming_serial_byte-49); break; + case 80: repeat_memory = 255; serial_program_memory(port_to_use); break; // P - program memory + #endif //FEATURE_MEMORIES + case 'Q': serial_qrss_mode(); break; // Q - activate QRSS mode + case 'R': speed_mode = SPEED_NORMAL; port_to_use->println(F("\r\nQRSS Off")); break; // R - activate regular timing mode + case 'S': serial_status(port_to_use); break; // S - Status command + case 'J': serial_set_dit_to_dah_ratio(port_to_use); break; // J - dit to dah ratio + #ifdef FEATURE_TRAINING_COMMAND_LINE_INTERFACE + case 'K': serial_cw_practice(port_to_use); break; // K - CW practice + #endif //FEATURE_TRAINING_COMMAND_LINE_INTERFACE + case 'L': serial_set_weighting(port_to_use); break; + #ifdef FEATURE_FARNSWORTH + case 'M': serial_set_farnsworth(port_to_use); break; // M - set Farnsworth speed + #endif + case 'N': // N - paddle reverse + port_to_use->print(F("\r\nPaddles ")); + if (configuration.paddle_mode == PADDLE_NORMAL) { + configuration.paddle_mode = PADDLE_REVERSE; + port_to_use->println(F("Reversed")); + } else { + configuration.paddle_mode = PADDLE_NORMAL; + port_to_use->println(F("Normal")); + } + config_dirty = 1; + break; + case 'O': // O - cycle through sidetone modes on, paddle only and off - enhanced by Marc-Andre, VE2EVN + port_to_use->print(F("\r\nSidetone ")); + if (configuration.sidetone_mode == SIDETONE_PADDLE_ONLY) { + configuration.sidetone_mode = SIDETONE_OFF; + boop(); + port_to_use->println(F("Off")); + } else if (configuration.sidetone_mode == SIDETONE_ON) { + configuration.sidetone_mode = SIDETONE_PADDLE_ONLY; + beep(); + delay(200); + beep(); + port_to_use->println(F("Paddle Only")); + } else { + configuration.sidetone_mode = SIDETONE_ON; + beep(); + port_to_use->println(F("On")); + } + config_dirty = 1; + break; + + + case 'T': // T - tune + #ifdef FEATURE_MEMORIES + repeat_memory = 255; + #endif + serial_tune_command(port_to_use); break; + case 'U': + port_to_use->print(F("\r\nPTT O")); + if (ptt_line_activated) { + manual_ptt_invoke = 0; + ptt_unkey(); + port_to_use->println(F("ff")); + } else { + manual_ptt_invoke = 1; + configuration.ptt_disabled = 0; + config_dirty = 1; + ptt_key(); + port_to_use->println(F("n")); + } + break; + #ifdef FEATURE_POTENTIOMETER + case 'V': // V - toggle pot activation + port_to_use->print(F("\r\nPotentiometer ")); + configuration.pot_activated = !configuration.pot_activated; + if (configuration.pot_activated) { + port_to_use->print(F("A")); + } else { + port_to_use->print(F("Dea")); + } + port_to_use->println(F("ctivated")); + config_dirty = 1; + break; + case '}': + serial_set_pot_low_high(port_to_use); + break; + #endif + case 'W': serial_wpm_set(port_to_use);break; // W - set WPM + case 'X': serial_switch_tx(port_to_use);break; // X - switch transmitter + case 'Y': serial_change_wordspace(port_to_use); break; + #ifdef FEATURE_AUTOSPACE + case 'Z': + port_to_use->print(F("\r\nAutospace O")); + if (configuration.autospace_active) { + configuration.autospace_active = 0; + config_dirty = 1; + port_to_use->println(F("ff")); + } else { + configuration.autospace_active = 1; + config_dirty = 1; + port_to_use->println(F("n")); + } + break; + #endif + + case 92: // \ - double backslash - clear serial send buffer + clear_send_buffer(); + #if !defined(OPTION_DISABLE_SERIAL_PORT_CHECKING_WHILE_SENDING_CW) + loop_element_lengths_breakout_flag = 0; + #endif + #ifdef FEATURE_MEMORIES + play_memory_prempt = 1; + repeat_memory = 255; + #endif + break; + + case '^': // ^ - toggle send CW send immediately + if (cli_wait_for_cr_to_send_cw) { + cli_wait_for_cr_to_send_cw = 0; + port_to_use->println(F("\r\nSend CW immediately")); + } else { + cli_wait_for_cr_to_send_cw = 1; + port_to_use->println(F("\r\nWait for CR to send CW")); + } + break; + #ifdef FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING + case '&': + port_to_use->print(F("\r\nCMOS Super Keyer Timing O")); + if (configuration.cmos_super_keyer_iambic_b_timing_on) { + configuration.cmos_super_keyer_iambic_b_timing_on = 0; + port_to_use->println(F("ff")); + } else { + configuration.cmos_super_keyer_iambic_b_timing_on = 1; + port_to_use->println(F("n")); + configuration.keyer_mode = IAMBIC_B; + } + config_dirty = 1; + break; + case '%': + user_input_temp = serial_get_number_input(2,-1,100,port_to_use, RAISE_ERROR_MSG); + if ((user_input_temp >= 0) && (user_input_temp < 100)) { + configuration.cmos_super_keyer_iambic_b_timing_percent = user_input_temp; + port_to_use->println(F("\r\nCMOS Super Keyer Timing Set.")); + } + config_dirty = 1; + break; + #endif //FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING + case '.': + port_to_use->print(F("\r\nDit Buffer O")); + if (configuration.dit_buffer_off) { + configuration.dit_buffer_off = 0; + port_to_use->println(F("n")); + } else { + configuration.dit_buffer_off = 1; + port_to_use->println(F("ff")); + } + config_dirty = 1; + break; + case '-': + port_to_use->print(F("\r\nDah Buffer O")); + if (configuration.dah_buffer_off) { + configuration.dah_buffer_off = 0; + port_to_use->println(F("n")); + } else { + configuration.dah_buffer_off = 1; + port_to_use->println(F("ff")); + } + config_dirty = 1; + break; + case ';': + if (cw_send_echo_inhibit){ + cw_send_echo_inhibit = 0; + } else { + cw_send_echo_inhibit = 1; + } + break; + #ifdef FEATURE_QLF + case '{': + port_to_use->print(F("\r\nQLF: O")); + if (qlf_active){ + qlf_active = 0; + port_to_use->println(F("ff")); + } else { + qlf_active = 1; + port_to_use->println(F("n")); + } + break; + #endif //FEATURE_QLF + + case '>': + send_serial_number(0,1,1); + break; + case '<': + send_serial_number(0,0,1); + break; + case '(': + send_serial_number(1,0,1); + break; + case ')': + send_serial_number(1,1,1); + break; + case '[': + user_input_temp = serial_get_number_input(2,-1,21,port_to_use,RAISE_ERROR_MSG); + if ((user_input_temp >= 0) && (user_input_temp < 21)) { + configuration.paddle_interruption_quiet_time_element_lengths = user_input_temp; + port_to_use->println(F("\r\nPaddle Interruption Quiet Time set.")); + } + config_dirty = 1; + break; + #if !defined(OPTION_EXCLUDE_MILL_MODE) + case '@': + switch(configuration.cli_mode){ + case CLI_NORMAL_MODE: + configuration.cli_mode = CLI_MILL_MODE_KEYBOARD_RECEIVE; + port_to_use->println(F("\r\nMill Mode On")); + break; + case CLI_MILL_MODE_PADDLE_SEND: + case CLI_MILL_MODE_KEYBOARD_RECEIVE: + configuration.cli_mode = CLI_NORMAL_MODE; + port_to_use->println(F("\r\nMill Mode Off")); + break; + } + config_dirty = 1; + break; + #endif // !defined(OPTION_EXCLUDE_MILL_MODE) + case '"': + port_to_use->print(F("\r\nPTT Buffered Character Hold O")); + if (configuration.ptt_buffer_hold_active){ + configuration.ptt_buffer_hold_active = 0; + port_to_use->println(F("ff")); + } else { + configuration.ptt_buffer_hold_active = 1; + port_to_use->println(F("n")); + } + config_dirty = 1; + break; + case ']': + port_to_use->print(F("\r\nPTT ")); + if (configuration.ptt_disabled){ + configuration.ptt_disabled = 0; + port_to_use->print(F("En")); + } else { + configuration.ptt_disabled = 1; + ptt_unkey(); + port_to_use->print(F("Dis")); + } + port_to_use->println(F("abled")); + config_dirty = 1; + break; + + #if defined(FEATURE_BEACON_SETTING) + case '_': + port_to_use->print(F("\r\nBeacon Mode At Boot Up ")); + if (!configuration.beacon_mode_on_boot_up){ + configuration.beacon_mode_on_boot_up = 1; + port_to_use->print(F("En")); + } else { + configuration.beacon_mode_on_boot_up = 0; + port_to_use->print(F("Dis")); + } + port_to_use->println(F("abled")); + config_dirty = 1; + break; + #endif + + + #if !defined(OPTION_EXCLUDE_EXTENDED_CLI_COMMANDS) + case ':': + cli_extended_commands(port_to_use); + break; + #endif + + default: port_to_use->println(F("\r\nUnknown command")); break; + } + +} +#endif //defined(FEATURE_SERIAL) && defined(FEATURE_COMMAND_LINE_INTERFACE) + + +//--------------------------------------------------------------------- + +#if defined(FEATURE_SERIAL) && defined(FEATURE_COMMAND_LINE_INTERFACE) && !defined(OPTION_EXCLUDE_EXTENDED_CLI_COMMANDS) +void cli_extended_commands(PRIMARY_SERIAL_CLS * port_to_use) +{ + byte incoming_serial_byte = 0; + byte looping = 1; + String userinput = ""; + + while (looping) { + if (port_to_use->available() == 0) { // wait for the next keystroke + if (keyer_machine_mode == KEYER_NORMAL) { // might as well do something while we're waiting + check_paddles(); + service_dit_dah_buffers(); + service_send_buffer(PRINTCHAR); + + check_ptt_tail(); + #ifdef FEATURE_POTENTIOMETER + if (configuration.pot_activated) { + check_potentiometer(); + } + #endif + + #ifdef FEATURE_ROTARY_ENCODER + check_rotary_encoder(); + #endif //FEATURE_ROTARY_ENCODER + } + } else { + incoming_serial_byte = port_to_use->read(); + port_to_use->write(incoming_serial_byte); + if ((incoming_serial_byte == 8) || (incoming_serial_byte == 127)){ // backspace / DEL + userinput.remove(userinput.length()-1,1); + } + incoming_serial_byte = uppercase(incoming_serial_byte); + if ((incoming_serial_byte > 31) && (incoming_serial_byte < 127)) { + userinput.concat((char)incoming_serial_byte); + } + if (incoming_serial_byte == 13 || incoming_serial_byte == 10) { // carriage return - get out + looping = 0; + } + } + } //while (looping) + + if (userinput.startsWith("EEPROMDUMP")){cli_eeprom_dump(port_to_use);return;} + if (userinput.startsWith("TIMING")){cli_timing_print(port_to_use);return;} + if (userinput.startsWith("PL ")){cli_timing_command(port_to_use,userinput.substring(3),COMMAND_PL);return;} + if (userinput.startsWith("PT ")){cli_timing_command(port_to_use,userinput.substring(3),COMMAND_PT);return;} + #if defined(FEATURE_SEQUENCER) + if (userinput.startsWith("PA ")){cli_timing_command(port_to_use,userinput.substring(3),COMMAND_PA);return;} + if (userinput.startsWith("PI ")){cli_timing_command(port_to_use,userinput.substring(3),COMMAND_PI);return;} + #endif //FEATURE_SEQUENCER + #if defined(FEATURE_SD_CARD_SUPPORT) + if (userinput.startsWith("SAVEEEPROM ")){sd_card_save_eeprom_to_file(port_to_use,userinput.substring(11));return;} + if (userinput.startsWith("LOADEEPROM ")){sd_card_load_eeprom_from_file(port_to_use,userinput.substring(11));return;} + if (userinput.startsWith("PRINTLOG")){sd_card_print_file(port_to_use,"/keyer/keyer.log");return;} + if (userinput.startsWith("CLEARLOG")){sd_card_clear_log_file(port_to_use,"/keyer/keyer.log");return;} + if (userinput.startsWith("LS ")){cli_sd_ls_command(port_to_use,userinput.substring(3));return;} + if (userinput.startsWith("CAT ")){sd_card_print_file(port_to_use,userinput.substring(4));return;} + #endif // defined(FEATURE_SD_CARD_SUPPORT) + + #if defined(FEATURE_PADDLE_ECHO) + if (userinput.startsWith("PF ")){cli_paddle_echo_factor(port_to_use,userinput.substring(3));return;} + #endif // defined(FEATURE_PADDLE_ECHO) + + #if defined(FEATURE_AUTOSPACE) + if (userinput.startsWith("AF ")){cli_autospace_timing_factor(port_to_use,userinput.substring(3));return;} + #endif // defined(FEATURE_AUTOSPACE) + + if (userinput.startsWith("COMP ")){cli_keying_compensation(port_to_use,userinput.substring(5));return;} + + port_to_use->println(F("\r\nError")); + +} +#endif + +//--------------------------------------------------------------------- + +#if defined(FEATURE_SERIAL) && defined(FEATURE_COMMAND_LINE_INTERFACE) && !defined(OPTION_EXCLUDE_EXTENDED_CLI_COMMANDS) +void cli_keying_compensation(PRIMARY_SERIAL_CLS * port_to_use,String command_arguments){ + + configuration.keying_compensation = command_arguments.toInt(); + config_dirty = 1; + port_to_use->print(F("\r\nKeying Compensation Set To: ")); + port_to_use->print(configuration.keying_compensation); + port_to_use->println(F(" mS")); + if (configuration.keying_compensation > (0.90 * (1200.0/(float)configuration.wpm))) { + port_to_use->println(F("WARNING: This is setting is probably too high for your current speed setting.")); + } + config_dirty = 1; + +} +#endif //defined(FEATURE_SERIAL) && defined(FEATURE_COMMAND_LINE_INTERFACE) && !defined(OPTION_EXCLUDE_EXTENDED_CLI_COMMANDS) && defined(FEATURE_AUTOSPACE) + + + +//--------------------------------------------------------------------- + +#if defined(FEATURE_SERIAL) && defined(FEATURE_COMMAND_LINE_INTERFACE) && !defined(OPTION_EXCLUDE_EXTENDED_CLI_COMMANDS) && defined(FEATURE_AUTOSPACE) +void cli_autospace_timing_factor(PRIMARY_SERIAL_CLS * port_to_use,String command_arguments){ + + configuration.autospace_timing_factor = command_arguments.toInt(); + config_dirty = 1; + port_to_use->print(F("\r\nAutospace Timing Factor Set To: ")); + port_to_use->println((float)configuration.autospace_timing_factor/(float)100); + + +} +#endif //defined(FEATURE_SERIAL) && defined(FEATURE_COMMAND_LINE_INTERFACE) && !defined(OPTION_EXCLUDE_EXTENDED_CLI_COMMANDS) && defined(FEATURE_AUTOSPACE) + + +//--------------------------------------------------------------------- + +#if defined(FEATURE_SERIAL) && defined(FEATURE_COMMAND_LINE_INTERFACE) && !defined(OPTION_EXCLUDE_EXTENDED_CLI_COMMANDS) +void cli_paddle_echo_factor(PRIMARY_SERIAL_CLS * port_to_use,String command_arguments){ + + configuration.cw_echo_timing_factor = command_arguments.toInt(); + config_dirty = 1; + port_to_use->print(F("\r\nPaddle Echo Factor Set To: ")); + port_to_use->println((float)configuration.cw_echo_timing_factor/(float)100); + + +} +#endif //defined(FEATURE_SERIAL) && defined(FEATURE_COMMAND_LINE_INTERFACE) && !defined(OPTION_EXCLUDE_EXTENDED_CLI_COMMANDS) + +//--------------------------------------------------------------------- + +#if defined(FEATURE_SERIAL) && defined(FEATURE_COMMAND_LINE_INTERFACE) && !defined(OPTION_EXCLUDE_EXTENDED_CLI_COMMANDS) +void cli_timing_command(PRIMARY_SERIAL_CLS * port_to_use,String command_arguments,byte command_called){ + + byte valid_command = 0; + unsigned int parm1 = 0; + unsigned int parm2 = 0; + String temp_string; + + temp_string = command_arguments.substring(0,1); + parm1 = temp_string.toInt(); + temp_string = command_arguments.substring(2); + parm2 = temp_string.toInt(); + if ((command_called == COMMAND_PL) || (command_called == COMMAND_PT)){ + if ((parm1 > 0) && (parm1 < 7)){ + if (command_called == COMMAND_PL){ + configuration.ptt_lead_time[parm1 - 1] = parm2; + } else { + configuration.ptt_tail_time[parm1 - 1] = parm2; + } + valid_command = 1; + } + } + + #if defined(FEATURE_SEQUENCER) + if ((command_called == COMMAND_PA) || (command_called == COMMAND_PI)){ + if ((parm1 > 0) && (parm1 < 6)){ + if (command_called == COMMAND_PA){ + configuration.ptt_active_to_sequencer_active_time[parm1 - 1] = parm2; + } else { + configuration.ptt_inactive_to_sequencer_inactive_time[parm1 - 1] = parm2; + } + valid_command = 1; + } + } + #endif //FEATURE_SEQUENCER + + if (!valid_command){ + port_to_use->println(F("\r\nError.")); + } else { + config_dirty = 1; + } + +} +#endif //defined(FEATURE_SERIAL) && defined(FEATURE_COMMAND_LINE_INTERFACE) && !defined(OPTION_EXCLUDE_EXTENDED_CLI_COMMANDS) +//--------------------------------------------------------------------- + +#if defined(FEATURE_SERIAL) && defined(FEATURE_COMMAND_LINE_INTERFACE) && !defined(OPTION_EXCLUDE_EXTENDED_CLI_COMMANDS) +void cli_timing_print(PRIMARY_SERIAL_CLS * port_to_use){ + + + port_to_use->println(F("\r\nTimings (mS)")); + port_to_use->println(F("\r\nPTT")); + port_to_use->println(F("TX\tLead\tTail")); + port_to_use->println(F("--\t----\t----")); + for (int x = 0; x < 6; x++){ + port_to_use->print(x+1); + port_to_use->print("\t"); + port_to_use->print(configuration.ptt_lead_time[x]); + port_to_use->print("\t"); + port_to_use->println(configuration.ptt_tail_time[x]); + } + #if defined(FEATURE_SEQUENCER) + port_to_use->println(F("\r\nSequencer")); + port_to_use->println(F("#\tPTT Active to Sequencer Active\tPTT Inactive to Sequencer Inactive")); + port_to_use->println(F("-\t------------------------------\t----------------------------------")); + for (int x = 0; x < 5; x++){ + port_to_use->print(x+1); + port_to_use->print("\t\t\t"); + port_to_use->print(configuration.ptt_active_to_sequencer_active_time[x]); + port_to_use->print("\t\t\t\t"); + port_to_use->println(configuration.ptt_inactive_to_sequencer_inactive_time[x]); + } + #endif //FEATURE_SEQUENCER + port_to_use->println(F("\r\nCommand Hints\r\n")); + port_to_use->println(F("pl \tSet PTT lead time")); + port_to_use->println(F("pt \tSet PTT tail time")); + #if defined(FEATURE_SEQUENCER) + port_to_use->println(F("pa <#> \t\tSet PTT active to Sequencer active time")); + port_to_use->println(F("pi <#> \t\tSet PTT inactive to Sequencer inactive time")); + #endif //FEATURE_SEQUENCER + +} +#endif //defined(FEATURE_SERIAL) && defined(FEATURE_COMMAND_LINE_INTERFACE) && !defined(OPTION_EXCLUDE_EXTENDED_CLI_COMMANDS) + +//--------------------------------------------------------------------- +#if defined(FEATURE_SERIAL) && defined(FEATURE_COMMAND_LINE_INTERFACE) && !defined(OPTION_EXCLUDE_EXTENDED_CLI_COMMANDS) && defined(FEATURE_SD_CARD_SUPPORT) +void cli_sd_ls_command(PRIMARY_SERIAL_CLS * port_to_use,String directory){ + + port_to_use->println(); + + File dir = SD.open(directory); + + while (true) { + File entry = dir.openNextFile(); + if (! entry) { + // no more files + break; + } + if (entry.isDirectory()) { + port_to_use->print("/"); + port_to_use->println(entry.name()); + } else { + // files have sizes, directories do not + port_to_use->print(entry.name()); + port_to_use->print("\t\t"); + port_to_use->println(entry.size(), DEC); + } + entry.close(); + } +} +#endif //defined(FEATURE_SERIAL) && defined(FEATURE_COMMAND_LINE_INTERFACE) && !defined(OPTION_EXCLUDE_EXTENDED_CLI_COMMANDS) && defined(FEATURE_SD_CARD_SUPPORT) + +//--------------------------------------------------------------------- +#if defined(FEATURE_SERIAL) && defined(FEATURE_COMMAND_LINE_INTERFACE) && !defined(OPTION_EXCLUDE_EXTENDED_CLI_COMMANDS) && defined(FEATURE_SD_CARD_SUPPORT) +void sd_card_clear_log_file(PRIMARY_SERIAL_CLS * port_to_use,String filename) { + + if(sd_card_log_state == SD_CARD_LOG_OPEN){ + sdlogfile.close(); + } + SD.remove(filename); + sdlogfile = SD.open(filename,FILE_WRITE); + sd_card_log_state = SD_CARD_LOG_NOT_OPEN; + if (!sdfile){ + port_to_use->println(F("Unable to open file ")); + sd_card_state = SD_CARD_ERROR; + sd_card_log_state = SD_CARD_LOG_ERROR; + } + sdlogfile.close(); + +} +#endif +//--------------------------------------------------------------------- +#if defined(FEATURE_SERIAL) && defined(FEATURE_COMMAND_LINE_INTERFACE) && !defined(OPTION_EXCLUDE_EXTENDED_CLI_COMMANDS) && defined(FEATURE_SD_CARD_SUPPORT) +void sd_card_print_file(PRIMARY_SERIAL_CLS * port_to_use,String filename) { + + sdfile = SD.open(filename); + if (!sdfile){ + port_to_use->print(F("Unable to open file ")); + port_to_use->println(filename); + } else { + port_to_use->println(F("\r\nSTART")); + while(sdfile.available()){ + port_to_use->write(sdfile.read()); + } + port_to_use->println(F("\r\nEND")); + } + +} +#endif +//--------------------------------------------------------------------- + + +#if defined(FEATURE_SERIAL) && defined(FEATURE_COMMAND_LINE_INTERFACE) && !defined(OPTION_EXCLUDE_EXTENDED_CLI_COMMANDS) && defined(FEATURE_SD_CARD_SUPPORT) +void sd_card_load_eeprom_from_file(PRIMARY_SERIAL_CLS * port_to_use,String filename) { + + uint8_t eeprom_byte_; + unsigned int x; + + + + sdfile = SD.open(filename, FILE_READ); + + if (sdfile) { + port_to_use->print(F("Loading eeprom from ")); + port_to_use->print(filename); + } else { + port_to_use->println(F("Error opening file. Exiting.")); + return; + } + + for (x = 0; x < memory_area_end; x++) { + if (sdfile.available()){ + EEPROM.write(x,sdfile.read()); + if ((x % 16) == 0){port_to_use->print("#");} + } else { + x = memory_area_end; + port_to_use->println(F("\r\nHit end of file before end of eeprom")); + } + } + + sdfile.close(); + port_to_use->println(F("\r\nReading settings from eeprom.")); + read_settings_from_eeprom(); + port_to_use->println(F("Done.")); + + + +} + +#endif //defined(FEATURE_SERIAL) && defined(FEATURE_COMMAND_LINE_INTERFACE) && !defined(OPTION_EXCLUDE_EXTENDED_CLI_COMMANDS) && defined(FEATURE_SD_CARD_SUPPORT) + +//--------------------------------------------------------------------- + + +#if defined(FEATURE_SERIAL) && defined(FEATURE_COMMAND_LINE_INTERFACE) && !defined(OPTION_EXCLUDE_EXTENDED_CLI_COMMANDS) && defined(FEATURE_SD_CARD_SUPPORT) +void sd_card_save_eeprom_to_file(PRIMARY_SERIAL_CLS * port_to_use,String filename) { + + uint8_t eeprom_byte_in; + unsigned int x; + + SD.remove(filename); + sdfile = SD.open(filename, FILE_WRITE); + + if (sdfile) { + port_to_use->print(F("Writing to ")); + port_to_use->print(filename); + } else { + port_to_use->println(F("Error opening file. Exiting.")); + return; + } + + for (x = 0; x < memory_area_end; x++) { + eeprom_byte_in = EEPROM.read(x); + sdfile.write(eeprom_byte_in); + if ((x % 16) == 0){port_to_use->print(".");} + } + + sdfile.close(); + port_to_use->println(F("Done.")); + +} + +#endif //defined(FEATURE_SERIAL) && defined(FEATURE_COMMAND_LINE_INTERFACE) && d!defined(OPTION_EXCLUDE_EXTENDED_CLI_COMMANDS) +//--------------------------------------------------------------------- + +#if defined(FEATURE_SERIAL) && defined(FEATURE_COMMAND_LINE_INTERFACE) && !defined(OPTION_EXCLUDE_EXTENDED_CLI_COMMANDS) +void cli_eeprom_dump(PRIMARY_SERIAL_CLS * port_to_use){ + + byte eeprom_byte_in; + byte y = 0; + int w = 0; + int x = 0; + + #define EEPROM_DUMP_COLUMNS 32 + #define EEPROM_DUMP_LINES 30 + + for (x = 0; x < memory_area_end; x++) { + if (y == 0){ + port_to_use->print("\r\n"); + if (x < 0x1000){port_to_use->print("0");} + if (x < 0x100){port_to_use->print("0");} + if (x < 0x10){port_to_use->print("0");} + port_to_use->print(x,HEX); + port_to_use->print("\t"); + } + eeprom_byte_in = EEPROM.read(x); + if (eeprom_byte_in < 0x10){port_to_use->print("0");} + port_to_use->print(eeprom_byte_in,HEX); + port_to_use->write(" "); + y++; + if (y > (EEPROM_DUMP_COLUMNS - 1)){ + port_to_use->print("\t"); + for (int z = x - y; z < x; z++) { + eeprom_byte_in = EEPROM.read(z); + if ((eeprom_byte_in > 31) && (eeprom_byte_in < 127)){ + port_to_use->write(eeprom_byte_in); + } else { + port_to_use->print("."); + } + } + y = 0; + w++; + if (w > EEPROM_DUMP_LINES){ + port_to_use->println(F("\r\nPress enter...")); + while(!port_to_use->available()){} + while(port_to_use->available()){port_to_use->read();} + w = 0; + } + } + } + if (y > 0){ + for (int z = (EEPROM_DUMP_COLUMNS - y); z > 0; z--){ + port_to_use->write(" "); + } + port_to_use->print("\t"); + for (int z = x - y; z < x; z++) { + eeprom_byte_in = EEPROM.read(z); + if ((eeprom_byte_in > 31) && (eeprom_byte_in < 127)){ + port_to_use->write(eeprom_byte_in); + } else { + port_to_use->print("."); + } + } + } + +} +#endif //defined(FEATURE_SERIAL) && defined(FEATURE_COMMAND_LINE_INTERFACE) && !defined(OPTION_EXCLUDE_EXTENDED_CLI_COMMANDS) +//--------------------------------------------------------------------- +#ifdef FEATURE_PADDLE_ECHO +void service_paddle_echo() +{ + + #ifdef DEBUG_LOOP + debug_serial_port->println(F("loop: entering service_paddle_echo")); + #endif + + static byte paddle_echo_space_sent = 1; + byte character_to_send = 0; + static byte no_space = 0; + + #if defined(OPTION_PROSIGN_SUPPORT) + byte byte_temp = 0; + static char * prosign_temp = (char*)""; + #endif + + #if defined(FEATURE_DISPLAY) && defined(OPTION_DISPLAY_NON_ENGLISH_EXTENSIONS) + byte ascii_temp = 0; + #endif //defined(FEATURE_DISPLAY) && defined(OPTION_DISPLAY_NON_ENGLISH_EXTENSIONS) + + + #if defined(FEATURE_CW_COMPUTER_KEYBOARD) + static byte backspace_flag = 0; + if (paddle_echo_buffer == 111111) {paddle_echo_buffer_decode_time = 0; backspace_flag = 1;} //this is a special hack to make repeating backspace work + #endif //defined(FEATURE_CW_COMPUTER_KEYBOARD) + + #ifdef FEATURE_SD_CARD_SUPPORT + char temp_string[2]; + #endif + + + if ((paddle_echo_buffer) && (millis() > paddle_echo_buffer_decode_time)) { + + + #if defined(FEATURE_CW_COMPUTER_KEYBOARD) + switch (paddle_echo_buffer){ + case 111111: + case 1111111: + case 11111111: + case 111111111: + Keyboard.write(KEY_BACKSPACE); // backspace + no_space = 1; + break; + #ifdef OPTION_CW_KEYBOARD_GERMAN // DL1HTB changed sign AA for return to BK + case 2111212: // return prosign BK + #else + case 1212: // prosign AA + #endif //OPTION_CW_KEYBOARD_GERMAN // #end DL1HTB changed sign AA for return to BK + Keyboard.write(KEY_RETURN); + no_space = 1; + break; + case 211222: // prosign DO + Keyboard.write(KEY_CAPS_LOCK); + #ifdef OPTION_CW_KEYBOARD_CAPSLOCK_BEEP + if (cw_keyboard_capslock_on){ + beep();delay(100); + boop(); + cw_keyboard_capslock_on = 0; + } else { + boop(); + beep(); + cw_keyboard_capslock_on = 1; + } + #endif //OPTION_CW_KEYBOARD_CAPSLOCK_BEEP + no_space = 1; + break; + + #ifdef OPTION_CW_KEYBOARD_ITALIAN // courtesy of Giorgio IZ2XBZ + case 122121: // "@" + Keyboard.press(KEY_LEFT_ALT); + Keyboard.write(59); + Keyboard.releaseAll(); + break; + case 112211:// "?" + Keyboard.write(95); + break; + case 11221: // "!" + Keyboard.write(33); + break; + case 21121: // "/" + Keyboard.write(38); + break; + case 21112: // "=" or "BT" + Keyboard.write(41); + break; + case 12212: //à + Keyboard.write(39); + break; + case 11211: //è + Keyboard.write(91); + break; + case 12221: //ì + Keyboard.write(61); + break; + case 2221: //ò + Keyboard.write(59); + break; + case 1122: //ù + Keyboard.write(92); + break; + case 21221: // ( + Keyboard.write(42); + break; + case 212212: // ) + Keyboard.write(40); + break; + case 12111: // & + Keyboard.write(94); + break; + case 222111: //: + Keyboard.write(62); + break; + case 212121: //; + Keyboard.write(60); + break; + case 12121: //+ + Keyboard.write(93); + break; + case 211112: // - + Keyboard.write(47); + break; + #endif //OPTION_CW_KEYBOARD_ITALIAN + + #ifdef OPTION_CW_KEYBOARD_GERMAN // DL1HTB added german keyboard mapping + case 122121: // "@" + Keyboard.press(KEY_RIGHT_ALT); + Keyboard.write('q'); + Keyboard.releaseAll(); + break; + case 112211: // "?" + Keyboard.write(95); + break; + case 11221: // "!" + Keyboard.write(33); + break; + case 21121: // "/" + Keyboard.write(38); + break; + case 222222: // "\" + Keyboard.press(KEY_RIGHT_ALT); + Keyboard.write('-'); + Keyboard.releaseAll(); + // Keyboard.write(92); + break; + case 21112: // "=" or "BT" + Keyboard.press(KEY_LEFT_SHIFT); + Keyboard.write('0'); + Keyboard.releaseAll(); + break; + case 1212: // "ä" + Keyboard.write(39); + break; + case 2221: // "ö" + Keyboard.write(59); + break; + case 1122: // "ü" + Keyboard.write(91); + break; + case 2222: // "ch" + Keyboard.write(99); + Keyboard.write(104); + break; + case 2122: // "y" + Keyboard.write(122); + break; + case 2211: // "z" + Keyboard.write(121); + break; + case 21221: // "(" + Keyboard.press(KEY_LEFT_SHIFT); + Keyboard.write('8'); + Keyboard.releaseAll(); + break; + case 212212: // ")" + Keyboard.write(40); + break; + case 12111: // "&" "AS" + Keyboard.press(KEY_LEFT_SHIFT); + Keyboard.write('6'); + Keyboard.releaseAll(); + break; + case 222111: // ":" + Keyboard.write(62); + break; + case 212121: // ";" + Keyboard.write(60); + break; + case 12121: // "+" + Keyboard.write(93); + break; + case 211112: // "-" + Keyboard.write(47); + break; + #endif //OPTION_CW_KEYBOARD_GERMAN // #end DL1HTB added german keyboard mapping + + default: + character_to_send = convert_cw_number_to_ascii(paddle_echo_buffer); + // if ((character_to_send > 64) && (character_to_send < 91)) {character_to_send = character_to_send + 32;} + if ((cw_keyboard_capslock_on == 0) && (character_to_send > 64) && (character_to_send < 91)) {character_to_send = character_to_send + 32;} + if (character_to_send=='*'){ + no_space = 1; + #ifdef OPTION_UNKNOWN_CHARACTER_ERROR_TONE + boop(); + #endif //OPTION_UNKNOWN_CHARACTER_ERROR_TONE + } else { + if (!((backspace_flag) && ((paddle_echo_buffer == 1) || (paddle_echo_buffer == 11) || (paddle_echo_buffer == 111) || (paddle_echo_buffer == 1111) || (paddle_echo_buffer == 11111)))){ + Keyboard.write(char(character_to_send)); + } + backspace_flag = 0; + } + break; + } + #ifdef DEBUG_CW_COMPUTER_KEYBOARD + debug_serial_port->print("service_paddle_echo: Keyboard.write: "); + debug_serial_port->write(character_to_send); + debug_serial_port->println(); + #endif //DEBUG_CW_COMPUTER_KEYBOARD + #endif //defined(FEATURE_CW_COMPUTER_KEYBOARD) + + #ifdef FEATURE_DISPLAY + if (lcd_paddle_echo){ + #if defined(OPTION_PROSIGN_SUPPORT) + #ifndef OPTION_DISPLAY_NON_ENGLISH_EXTENSIONS + byte_temp = convert_cw_number_to_ascii(paddle_echo_buffer); + if ((byte_temp > PROSIGN_START) && (byte_temp < PROSIGN_END)){ + prosign_temp = convert_prosign(byte_temp); + display_scroll_print_char(prosign_temp[0]); + display_scroll_print_char(prosign_temp[1]); + } else { + display_scroll_print_char(byte(convert_cw_number_to_ascii(paddle_echo_buffer))); + } + #else //OPTION_DISPLAY_NON_ENGLISH_EXTENSIONS + ascii_temp = byte(convert_cw_number_to_ascii(paddle_echo_buffer)); + if ((ascii_temp > PROSIGN_START) && (ascii_temp < PROSIGN_END)){ + prosign_temp = convert_prosign(ascii_temp); + display_scroll_print_char(prosign_temp[0]); + display_scroll_print_char(prosign_temp[1]); + } else { + switch (ascii_temp){ + case 220: ascii_temp = 0;break; // U_umlaut (D, ...) + case 214: ascii_temp = 1;break; // O_umlaut (D, SM, OH, ...) + case 196: ascii_temp = 2;break; // A_umlaut (D, SM, OH, ...) + case 198: ascii_temp = 3;break; // AE_capital (OZ, LA) + case 216: ascii_temp = 4;break; // OE_capital (OZ, LA) + case 197: ascii_temp = 6;break; // AA_capital (OZ, LA, SM) + case 209: ascii_temp = 7;break; // N-tilde (EA) + } + display_scroll_print_char(ascii_temp); + } + #endif //OPTION_DISPLAY_NON_ENGLISH_EXTENSIONS + + #else // ! OPTION_PROSIGN_SUPPORT + + #ifndef OPTION_DISPLAY_NON_ENGLISH_EXTENSIONS + display_scroll_print_char(byte(convert_cw_number_to_ascii(paddle_echo_buffer))); + #else //OPTION_DISPLAY_NON_ENGLISH_EXTENSIONS + ascii_temp = byte(convert_cw_number_to_ascii(paddle_echo_buffer)); + switch (ascii_temp){ + case 220: ascii_temp = 0;break; // U_umlaut (D, ...) + case 214: ascii_temp = 1;break; // O_umlaut (D, SM, OH, ...) + case 196: ascii_temp = 2;break; // A_umlaut (D, SM, OH, ...) + case 198: ascii_temp = 3;break; // AE_capital (OZ, LA) + case 216: ascii_temp = 4;break; // OE_capital (OZ, LA) + case 197: ascii_temp = 6;break; // AA_capital (OZ, LA, SM) + case 209: ascii_temp = 7;break; // N-tilde (EA) + } + display_scroll_print_char(ascii_temp); + #endif //OPTION_DISPLAY_NON_ENGLISH_EXTENSIONS + #endif //OPTION_PROSIGN_SUPPORT + } + #endif //FEATURE_DISPLAY + + #if defined(FEATURE_SERIAL) && defined(FEATURE_COMMAND_LINE_INTERFACE) + #if defined(OPTION_PROSIGN_SUPPORT) + byte_temp = convert_cw_number_to_ascii(paddle_echo_buffer); + if (cli_paddle_echo){ + if ((byte_temp > PROSIGN_START) && (byte_temp < PROSIGN_END)){ + primary_serial_port->print(prosign_temp[0]); + primary_serial_port->print(prosign_temp[1]); + #ifdef FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + secondary_serial_port->print(prosign_temp[0]); + secondary_serial_port->print(prosign_temp[1]); + #endif //FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + } else { + if (configuration.cli_mode == CLI_MILL_MODE_KEYBOARD_RECEIVE){ + primary_serial_port->println(); + primary_serial_port->println(); + #ifdef FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + secondary_serial_port->println(); + secondary_serial_port->println(); + #endif + #ifdef FEATURE_SD_CARD_SUPPORT + sd_card_log("\r\nTX:",0); + #endif + configuration.cli_mode = CLI_MILL_MODE_PADDLE_SEND; + } + primary_serial_port->write(byte_temp); + #ifdef FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + secondary_serial_port->write(byte_temp); + #endif //FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + #ifdef FEATURE_SD_CARD_SUPPORT + sd_card_log("",incoming_serial_byte); + #endif + } + + } + + #else // ! OPTION_PROSIGN_SUPPORT + if (cli_paddle_echo){ + if (configuration.cli_mode == CLI_MILL_MODE_KEYBOARD_RECEIVE){ + primary_serial_port->println(); + primary_serial_port->println(); + #ifdef FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + secondary_serial_port->println(); + secondary_serial_port->println(); + #endif + #ifdef FEATURE_SD_CARD_SUPPORT + sd_card_log("\r\nTX:",0); + #endif + configuration.cli_mode = CLI_MILL_MODE_PADDLE_SEND; + } + primary_serial_port->write(byte(convert_cw_number_to_ascii(paddle_echo_buffer))); + #ifdef FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + secondary_serial_port->write(byte(convert_cw_number_to_ascii(paddle_echo_buffer))); + #endif + #ifdef FEATURE_SD_CARD_SUPPORT + sd_card_log("",convert_cw_number_to_ascii(paddle_echo_buffer)); + #endif + } + #endif //OPTION_PROSIGN_SUPPORT + #endif //defined(FEATURE_SERIAL) && defined(FEATURE_COMMAND_LINE_INTERFACE) + + paddle_echo_buffer = 0; + paddle_echo_buffer_decode_time = millis() + (float(600/configuration.wpm)*length_letterspace); + paddle_echo_space_sent = 0; + } + + // is it time to echo a space? + if ((paddle_echo_buffer == 0) && (millis() > (paddle_echo_buffer_decode_time + (float(1200/configuration.wpm)*(configuration.length_wordspace-length_letterspace)))) && (!paddle_echo_space_sent)) { + + #if defined(FEATURE_CW_COMPUTER_KEYBOARD) + if (!no_space){ + Keyboard.write(' '); + #ifdef DEBUG_CW_COMPUTER_KEYBOARD + debug_serial_port->println("service_paddle_echo: Keyboard.write: "); + #endif //DEBUG_CW_COMPUTER_KEYBOARD + } + no_space = 0; + #endif //defined(FEATURE_CW_COMPUTER_KEYBOARD) + + #ifdef FEATURE_DISPLAY + if (lcd_paddle_echo){ + display_scroll_print_char(' '); + } + #endif //FEATURE_DISPLAY + + #if defined(FEATURE_SERIAL) && defined(FEATURE_COMMAND_LINE_INTERFACE) + if (cli_paddle_echo){ + primary_serial_port->write(" "); + + #ifdef FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + secondary_serial_port->write(" "); + #endif + + } + #endif //defined(FEATURE_SERIAL) && defined(FEATURE_COMMAND_LINE_INTERFACE) + + #ifdef FEATURE_SD_CARD_SUPPORT + sd_card_log(" ",0); + #endif + + paddle_echo_space_sent = 1; + } + +} +#endif //FEATURE_PADDLE_ECHO + +//--------------------------------------------------------------------- +#if defined(FEATURE_SERIAL) && defined(FEATURE_COMMAND_LINE_INTERFACE) && defined(FEATURE_MEMORIES) +void serial_set_memory_repeat(PRIMARY_SERIAL_CLS * port_to_use) { + + int temp_int = serial_get_number_input(5, -1, 32000, port_to_use, RAISE_ERROR_MSG); + if (temp_int > -1) { + configuration.memory_repeat_time = temp_int; + config_dirty = 1; + } + +} +#endif + +//--------------------------------------------------------------------- + +#if defined(FEATURE_SERIAL) && defined(FEATURE_COMMAND_LINE_INTERFACE) && defined(FEATURE_MEMORIES) +void repeat_play_memory(PRIMARY_SERIAL_CLS * port_to_use) { + + byte memory_number = serial_get_number_input(2,0, (number_of_memories+1), port_to_use, RAISE_ERROR_MSG); + #ifdef DEBUG_CHECK_SERIAL + debug_serial_port->print(F("repeat_play_memory: memory_number:")); + debug_serial_port->println(memory_number); + #endif //DEBUG_SERIAL + if (memory_number > -1) { + repeat_memory = memory_number - 1; + } +} +#endif + +//--------------------------------------------------------------------- + +#if defined(FEATURE_SERIAL) && defined(FEATURE_COMMAND_LINE_INTERFACE) && defined(FEATURE_MEMORIES) + +void serial_play_memory(byte memory_number) { + + if (memory_number < number_of_memories) { + add_to_send_buffer(SERIAL_SEND_BUFFER_MEMORY_NUMBER); + add_to_send_buffer(memory_number); + repeat_memory = 255; + } +} +#endif + +//--------------------------------------------------------------------- + +#if defined(FEATURE_SERIAL) && defined(FEATURE_COMMAND_LINE_INTERFACE) +int serial_get_number_input(byte places,int lower_limit, int upper_limit,PRIMARY_SERIAL_CLS * port_to_use,int raise_error_message) { + byte incoming_serial_byte = 0; + byte looping = 1; + byte error = 0; + String numberstring = ""; + byte numberindex = 0; + int numbers[6]; + + while (looping) { + if (port_to_use->available() == 0) { // wait for the next keystroke + if (keyer_machine_mode == KEYER_NORMAL) { // might as well do something while we're waiting + check_paddles(); + service_dit_dah_buffers(); + service_send_buffer(PRINTCHAR); + + check_ptt_tail(); + #ifdef FEATURE_POTENTIOMETER + if (configuration.pot_activated) { + check_potentiometer(); + } + #endif + + #ifdef FEATURE_ROTARY_ENCODER + check_rotary_encoder(); + #endif //FEATURE_ROTARY_ENCODER + } + } else { + incoming_serial_byte = port_to_use->read(); + port_to_use->write(incoming_serial_byte); + if ((incoming_serial_byte > 47) && (incoming_serial_byte < 58)) { // ascii 48-57 = "0" - "9") + numberstring = numberstring + incoming_serial_byte; + numbers[numberindex] = incoming_serial_byte; + numberindex++; + if (numberindex > places){ + looping = 0; + error = 1; + } + } else { + if (incoming_serial_byte == 13) { // carriage return - get out + looping = 0; + } else { // bogus input - error out + looping = 0; + error = 1; + } + } + } + } + if (error) { + if (raise_error_message == RAISE_ERROR_MSG){ + port_to_use->println(F("Error...")); + } + while (port_to_use->available() > 0) { incoming_serial_byte = port_to_use->read(); } // clear out buffer + return(-1); + } else { + int y = 1; + int return_number = 0; + for (int x = (numberindex - 1); x >= 0 ; x = x - 1) { + return_number = return_number + ((numbers[x]-48) * y); + y = y * 10; + } + if ((return_number > lower_limit) && (return_number < upper_limit)) { + return(return_number); + } else { + if (raise_error_message == RAISE_ERROR_MSG){ + port_to_use->println(F("Error...")); + } + return(-1); + } + } +} +#endif + +//--------------------------------------------------------------------- + +#if defined(FEATURE_SERIAL) && defined(FEATURE_COMMAND_LINE_INTERFACE) +void serial_change_wordspace(PRIMARY_SERIAL_CLS * port_to_use) +{ + int set_wordspace_to = serial_get_number_input(2,0,100,port_to_use, RAISE_ERROR_MSG); + if (set_wordspace_to > 0) { + config_dirty = 1; + configuration.length_wordspace = set_wordspace_to; + port_to_use->write("\r\nWordspace set to "); + port_to_use->println(set_wordspace_to,DEC); + } +} +#endif + +//--------------------------------------------------------------------- + +#if defined(FEATURE_SERIAL) && defined(FEATURE_COMMAND_LINE_INTERFACE) +void serial_switch_tx(PRIMARY_SERIAL_CLS * port_to_use) { + int set_tx_to = serial_get_number_input(1,0,7,port_to_use,RAISE_ERROR_MSG); + if (set_tx_to > 0) { + switch (set_tx_to){ + case 1: switch_to_tx_silent(1); port_to_use->print(F("\r\nSwitching to TX #")); port_to_use->println(F("1")); break; + case 2: if ((ptt_tx_2) || (tx_key_line_2)) {switch_to_tx_silent(2); port_to_use->print(F("\r\nSwitching to TX #"));} port_to_use->println(F("2")); break; + case 3: if ((ptt_tx_3) || (tx_key_line_3)) {switch_to_tx_silent(3); port_to_use->print(F("\r\nSwitching to TX #"));} port_to_use->println(F("3")); break; + case 4: if ((ptt_tx_4) || (tx_key_line_4)) {switch_to_tx_silent(4); port_to_use->print(F("\r\nSwitching to TX #"));} port_to_use->println(F("4")); break; + case 5: if ((ptt_tx_5) || (tx_key_line_5)) {switch_to_tx_silent(5); port_to_use->print(F("\r\nSwitching to TX #"));} port_to_use->println(F("5")); break; + case 6: if ((ptt_tx_6) || (tx_key_line_6)) {switch_to_tx_silent(6); port_to_use->print(F("\r\nSwitching to TX #"));} port_to_use->println(F("6")); break; + } + } +} +#endif + +//--------------------------------------------------------------------- +#if defined(FEATURE_SERIAL) && defined(FEATURE_COMMAND_LINE_INTERFACE) +void serial_set_dit_to_dah_ratio(PRIMARY_SERIAL_CLS * port_to_use) +{ + int set_ratio_to = serial_get_number_input(4, 99, 1000, port_to_use, DONT_RAISE_ERROR_MSG); + // if ((set_ratio_to > 99) && (set_ratio_to < 1000)) { + // configuration.dah_to_dit_ratio = set_ratio_to; + // port_to_use->print(F("Setting dah to dit ratio to ")); + // port_to_use->println((float(configuration.dah_to_dit_ratio)/100)); + // config_dirty = 1; + // } + + if ((set_ratio_to < 100) || (set_ratio_to > 999)) { + set_ratio_to = 300; + } + configuration.dah_to_dit_ratio = set_ratio_to; + port_to_use->write("\r\nDah to dit ratio set to "); + port_to_use->println((float(configuration.dah_to_dit_ratio)/100)); + config_dirty = 1; +} +#endif + +//--------------------------------------------------------------------- + +#if defined(FEATURE_SERIAL) && defined(FEATURE_COMMAND_LINE_INTERFACE) +void serial_set_serial_number(PRIMARY_SERIAL_CLS * port_to_use) +{ + int set_serial_number_to = serial_get_number_input(4,0,10000, port_to_use,RAISE_ERROR_MSG); + if (set_serial_number_to > 0) { + serial_number = set_serial_number_to; + port_to_use->write("\r\nSetting serial number to "); + port_to_use->println(serial_number); + } +} +#endif + +//--------------------------------------------------------------------- + +#if defined(FEATURE_SERIAL) && defined(FEATURE_COMMAND_LINE_INTERFACE) && defined(FEATURE_POTENTIOMETER) +void serial_set_pot_low_high(PRIMARY_SERIAL_CLS * port_to_use) +{ + int serial_get_number = serial_get_number_input(4,500,10000, port_to_use,RAISE_ERROR_MSG); + int low_number = (serial_get_number / 100); + int high_number = serial_get_number % (int(serial_get_number / 100)*100); + if ((low_number < high_number) && (low_number >= wpm_limit_low) && (high_number <= wpm_limit_high)){ + port_to_use->print(F("\r\nSetting potentiometer range to ")); + port_to_use->print(low_number); + port_to_use->print(F(" - ")); + port_to_use->print(high_number); + port_to_use->println(F(" WPM")); + pot_wpm_low_value = low_number; + pot_wpm_high_value = high_number; + } else { + port_to_use->println(F("\nError")); + } +} +#endif + +//--------------------------------------------------------------------- + +#if defined(FEATURE_SERIAL) && defined(FEATURE_COMMAND_LINE_INTERFACE) +void serial_set_sidetone_freq(PRIMARY_SERIAL_CLS * port_to_use) { + int set_sidetone_hz = serial_get_number_input(4,(sidetone_hz_limit_low-1),(sidetone_hz_limit_high+1), port_to_use, RAISE_ERROR_MSG); + if ((set_sidetone_hz > sidetone_hz_limit_low) && (set_sidetone_hz < sidetone_hz_limit_high)) { + port_to_use->write("\r\nSetting sidetone to "); + port_to_use->print(set_sidetone_hz,DEC); + port_to_use->println(F(" hz")); + configuration.hz_sidetone = set_sidetone_hz; + config_dirty = 1; + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9) lcd_center_print_timed(String(configuration.hz_sidetone) + " Hz", 0, default_display_msg_delay); + else lcd_center_print_timed("Sidetone " + String(configuration.hz_sidetone) + " Hz", 0, default_display_msg_delay); + #endif // FEATURE_DISPLAY + } +} +#endif + +//--------------------------------------------------------------------- + +#if defined(FEATURE_SERIAL) && defined(FEATURE_COMMAND_LINE_INTERFACE) +void serial_wpm_set(PRIMARY_SERIAL_CLS * port_to_use) { + int set_wpm = serial_get_number_input(3,0,1000, port_to_use, RAISE_ERROR_MSG); + if (set_wpm > 0) { + speed_set(set_wpm); + port_to_use->write("\r\nSetting WPM to "); + port_to_use->println(set_wpm,DEC); + config_dirty = 1; + } +} +#endif + +//--------------------------------------------------------------------- + +#if defined(FEATURE_SERIAL) && defined(FEATURE_COMMAND_LINE_INTERFACE) && defined(FEATURE_FARNSWORTH) +void serial_set_farnsworth(PRIMARY_SERIAL_CLS * port_to_use) { + int set_farnsworth_wpm = serial_get_number_input(3,-1,1000, port_to_use, RAISE_ERROR_MSG); + if ((set_farnsworth_wpm > 0) || (set_farnsworth_wpm == 0)) { + configuration.wpm_farnsworth = set_farnsworth_wpm; + port_to_use->write("\r\nSetting Farnsworth WPM to "); + port_to_use->println(set_farnsworth_wpm,DEC); + config_dirty = 1; + } +} +#endif + +//--------------------------------------------------------------------- + +#if defined(FEATURE_SERIAL) && defined(FEATURE_COMMAND_LINE_INTERFACE) +void serial_set_weighting(PRIMARY_SERIAL_CLS * port_to_use) { + int set_weighting = serial_get_number_input(2,9,91,port_to_use, DONT_RAISE_ERROR_MSG); + if (set_weighting < 1) { + set_weighting = 50; + } + configuration.weighting = set_weighting; + port_to_use->write("\r\nSetting weighting to "); + port_to_use->println(set_weighting,DEC); +} +#endif + +//--------------------------------------------------------------------- + +#if defined(FEATURE_SERIAL) && defined(FEATURE_COMMAND_LINE_INTERFACE) +void serial_tune_command (PRIMARY_SERIAL_CLS * port_to_use) { + byte incoming; + + delay(100); + while (port_to_use->available() > 0) { // clear out the buffer if anything is there + incoming = port_to_use->read(); + } + + sending_mode = MANUAL_SENDING; + tx_and_sidetone_key(1); + port_to_use->println(F("\r\nKeying tx - press a key to unkey")); + #ifdef FEATURE_BUTTONS + while ((port_to_use->available() == 0) && (!analogbuttonread(0))) {} // keystroke or button0 hit gets us out of here + #else + while (port_to_use->available() == 0) {} + #endif + while (port_to_use->available() > 0) { // clear out the buffer if anything is there + incoming = port_to_use->read(); + } + tx_and_sidetone_key(0); +} +#endif + +//--------------------------------------------------------------------- + +#ifdef FEATURE_TRAINING_COMMAND_LINE_INTERFACE + +String generate_callsign(byte callsign_mode) { + + static String callsign(10); + char nextchar; + char word_buffer[10]; + + callsign = ""; + + if (callsign_mode == CALLSIGN_INTERNATIONAL){ + if (random(1,101) < 96) { + // start with a letter 96% of the time + nextchar = random(65,91); + callsign = callsign + nextchar; + if (random(1,101) < 20) { // randomly add second prefix letter 20% of the time + nextchar = random(65,91); + callsign = callsign + nextchar; + } + } else { + // start with a number + nextchar = random(49,58); // generate the number + callsign = callsign + nextchar; + nextchar = random(65,91); // must add a letter next + callsign = callsign + nextchar; + } + } //CALLSIGN_INTERNATIONAL + + if (callsign_mode == CALLSIGN_US){ + switch (random(1,5)) { + case 1: callsign = "K"; break; + case 2: callsign = "W"; break; + case 3: callsign = "N"; break; + case 4: callsign = "A"; break; + } + if (callsign == "A") { // if the first letter is A, we definitely need a second letter before the number + nextchar = random(65,91); + callsign = callsign + nextchar; + } else { + // randomly add a second letter for K, W, N prefixes + if (random(1,101) < 51) { + nextchar = random(65,91); + callsign = callsign + nextchar; + } + } + } //CALLSIGN_US + + if (callsign_mode == CALLSIGN_CANADA){ + strcpy_P(word_buffer, (char*)pgm_read_word(&(canadian_prefix_table[random(0,canadian_prefix_size)]))); + callsign = word_buffer; + } + + if (callsign_mode == CALLSIGN_EUROPEAN){ + + strcpy_P(word_buffer, (char*)pgm_read_word(&(eu_prefix_table[random(0,eu_prefix_size)]))); + callsign = word_buffer; + } + + if (callsign_mode != CALLSIGN_CANADA){ + nextchar = random(48,58); // generate the number + callsign = callsign + nextchar; + } + + + nextchar = random(65,91); // generate first letter after number + callsign = callsign + nextchar; + if ((random(1,101) < 40) || (callsign_mode == CALLSIGN_CANADA)) { // randomly put a second character after the number + nextchar = random(65,91); + callsign = callsign + nextchar; + if ((random(1,101) < 96) || (callsign_mode == CALLSIGN_CANADA)) { // randomly put a third character after the number + nextchar = random(65,91); + callsign = callsign + nextchar; + } + } + + if (random(1,101) < 10) { // randomly put a slash something on the end like /QRP or /# + if (random(1,101) < 25) { + callsign = callsign + "/QRP"; + } else { + nextchar = random(48,58); + callsign = callsign + "/" + nextchar; + } + } + + return callsign; +} + +#endif //FEATURE_TRAINING_COMMAND_LINE_INTERFACE +//--------------------------------------------------------------------- +// #if defined(FEATURE_SERIAL) && defined(FEATURE_TRAINING_COMMAND_LINE_INTERFACE) && defined(FEATURE_COMMAND_LINE_INTERFACE) +// void paqso_practice(PRIMARY_SERIAL_CLS * port_to_use){ + +// // VT100 emulation in Linux: screen /dev/ttyACM1 115200 term vt100 + +// #define CONTEST_PRACTICE_IDLE 0 +// #define CONTEST_PRACTICE_CQ_SENT 1 +// #define CONTEST_PRACTICE_REPORT_SENT 2 + +// #define FIELD_CALLSIGN 0 +// #define FIELD_NR 1 +// #define FIELD_SECTION 2 + +// byte overall_state = CONTEST_PRACTICE_IDLE; +// byte loop1 = 1; +// byte user_input_buffer[10]; +// byte user_input_buffer_characters = 0; +// byte incoming_char = 0; +// byte process_user_input_buffer = 0; +// unsigned long escape_flag_time = 0; +// String callsign; +// String nr; +// String section; +// byte cq_answered = 0; +// unsigned long transition_time = 0; +// byte current_field = FIELD_CALLSIGN; + +// int previous_sidetone = configuration.hz_sidetone; +// int previous_wpm = configuration.wpm; +// int caller_sidetone = 0; +// int caller_wpm_delta = 0; + +// while (port_to_use->available() > 0) { // clear out the buffer if anything is there +// port_to_use->read(); +// } + +// term.init(); +// term.cls(); +// term.position(0,0); +// term.println(F("\nPA QSO Party Practice\n")); +// term.println(F("This requires VT100 emulation!\n")); +// term.println(F("F1 - Call CQ")); +// term.println(F("F2 - Exchange")); +// term.println(F("F3 - TU")); +// term.println(F("Insert - Callsign + Exchange")); +// term.println(F("\\ - Exit\n")); +// term.println(F("Callsign NR Section")); +// term.println(F("-------- ---- -------\n\n")); + +// while (loop1){ +// // get user keyboard input +// if (port_to_use->available()){ +// user_input_buffer[user_input_buffer_characters] = toupper(port_to_use->read()); +// switch(user_input_buffer[user_input_buffer_characters]){ +// case 27: //escape +// escape_flag_time = millis(); +// user_input_buffer_characters++; +// case 13: //return +// case 32: //space +// process_user_input_buffer = 1; +// break; +// case 127: +// case 8: //backspace +// if (user_input_buffer_characters > 0){user_input_buffer_characters--;} +// port_to_use->write(27); +// port_to_use->write(91); +// port_to_use->write(49); +// port_to_use->write(68); +// break; + +// default: +// if (!(((user_input_buffer[user_input_buffer_characters-1] == 27) && (user_input_buffer[user_input_buffer_characters] == 79) && (user_input_buffer_characters>0)) || +// ((user_input_buffer[user_input_buffer_characters-2] == 27) && (user_input_buffer[user_input_buffer_characters-1] == 79) && (user_input_buffer_characters>1)))){ +// port_to_use->write(user_input_buffer[user_input_buffer_characters]); +// } +// user_input_buffer_characters++; +// break; +// } //switch(user_input_buffer[user_input_buffer_characters]) +// if (user_input_buffer_characters == 10){process_user_input_buffer = 1;} + +// }//(port_to_use->available()) + +// // process user keyboard input +// if ((process_user_input_buffer) && ((escape_flag_time == 0) || ((millis()-escape_flag_time) > 100))){ + +// #ifdef DEBUG_CW_PRACTICE +// debug_serial_port->print(F("escape_flag_time: process_user_input_buffer user_input_buffer_characters:")); +// debug_serial_port->println(user_input_buffer_characters); +// #endif + +// if (user_input_buffer_characters > 0){ +// if (user_input_buffer[0] == '\\'){ // does user want to exit? +// loop1 = 0; +// } else { +// if (user_input_buffer[0] == 27){ +// if (user_input_buffer_characters == 3){ +// if ((user_input_buffer[1] == 79) && (user_input_buffer[2] == 80)) { //VT100 F1 key +// configuration.hz_sidetone = previous_sidetone; +// configuration.wpm = previous_wpm; +// add_to_send_buffer('C'); +// add_to_send_buffer('Q'); +// add_to_send_buffer(' '); +// add_to_send_buffer('T'); +// add_to_send_buffer('E'); +// add_to_send_buffer('S'); +// add_to_send_buffer('T'); +// add_to_send_buffer(' '); +// add_to_send_buffer('D'); +// add_to_send_buffer('E'); +// add_to_send_buffer(' '); +// add_to_send_buffer('K'); +// add_to_send_buffer('3'); +// add_to_send_buffer('N'); +// add_to_send_buffer('G'); +// overall_state = CONTEST_PRACTICE_CQ_SENT; +// transition_time = millis(); +// } //((user_input_buffer[1] == 79) && (user_input_buffer[2] == 80)) VT100 F1 key +// } //(user_input_buffer_characters == 3) +// if (user_input_buffer_characters == 4){ +// if ((user_input_buffer[1] == 91) && (user_input_buffer[2] == 50) && (user_input_buffer[3] == 126)) { //VT100 INS key +// for (byte x = 0; x < user_input_buffer_characters; x++) { +// add_to_send_buffer(user_input_buffer[x]); +// } +// add_to_send_buffer(' '); +// add_to_send_buffer('0'); +// add_to_send_buffer('0'); +// add_to_send_buffer('1'); +// add_to_send_buffer(' '); +// add_to_send_buffer('C'); +// add_to_send_buffer('A'); +// add_to_send_buffer('R'); +// configuration.hz_sidetone = previous_sidetone; +// configuration.wpm = previous_wpm; +// overall_state = CONTEST_PRACTICE_REPORT_SENT; +// } +// } //(user_input_buffer_characters == 4) + +// } else { //(user_input_buffer[0] == 27) + +// // we have a callsign, nr, or section + +// switch(current_field){ +// case FIELD_CALLSIGN: +// callsign = ""; +// for (byte x = 0; x < user_input_buffer_characters; x++) { +// callsign.concat(char(user_input_buffer[x])); +// } +// current_field = FIELD_NR; +// break; + +// case FIELD_NR: +// nr = ""; +// for (byte x = 0; x < user_input_buffer_characters; x++) { +// nr.concat(char(user_input_buffer[x])); +// } +// current_field = FIELD_SECTION; +// break; + +// case FIELD_SECTION: +// section = ""; +// for (byte x = 0; x < user_input_buffer_characters; x++) { +// section.concat(char(user_input_buffer[x])); +// } +// current_field = FIELD_CALLSIGN; +// break; +// } +// term.position(13,0); +// term.print(callsign); +// term.position(13,9); +// term.print(nr); +// term.position(13,14); +// term.println(section); +// term.position(15,0); +// term.print(F(" ")); +// term.position(15,0); +// } +// } //(user_input_buffer[0] == '\\') +// } //(user_input_buffer_characters > 0) +// process_user_input_buffer = 0; +// user_input_buffer_characters = 0; +// escape_flag_time = 0; +// } //((process_user_input_buffer) && ((escape_flag_time == 0) || ((millis() -escape_flag_time) > 100))) + +// //do autonomous events +// service_send_buffer(NOPRINT); + +// switch(overall_state){ +// case CONTEST_PRACTICE_CQ_SENT: +// if (send_buffer_bytes == 0){ +// if (!cq_answered){ +// if (((millis() - transition_time) > random(250,1500))){ // add some random delay +// callsign = generate_callsign(); +// caller_sidetone = random(500,1000); +// configuration.hz_sidetone = caller_sidetone; +// caller_wpm_delta = random(-5,5); +// configuration.wpm = configuration.wpm + caller_wpm_delta; +// for (byte x = 0; x < (callsign.length()); x++) { +// add_to_send_buffer(callsign[x]); +// } +// cq_answered = 1; +// transition_time = millis(); +// } +// } else { //send it again +// if ((cq_answered) && ((millis() - transition_time) > random(2000,4000))){ +// configuration.hz_sidetone = caller_sidetone; +// configuration.wpm = configuration.wpm + caller_wpm_delta; +// for (byte x = 0; x < (callsign.length()); x++) { +// add_to_send_buffer(callsign[x]); +// } +// cq_answered++; +// transition_time = millis(); +// } +// } +// } else { +// transition_time = millis(); +// } //send_buffer_bytes == 0 +// break; //CONTEST_PRACTICE_CQ_SENT +// } //switch(overall_state) +// } //while (loop1) + +// configuration.hz_sidetone = previous_sidetone; +// configuration.wpm = previous_wpm; +// send_buffer_bytes = 0; +// } +// #endif + +//--------------------------------------------------------------------- + +#if defined(FEATURE_SERIAL) && defined(FEATURE_TRAINING_COMMAND_LINE_INTERFACE) && defined(FEATURE_COMMAND_LINE_INTERFACE) +void serial_cw_practice(PRIMARY_SERIAL_CLS * port_to_use) { + + + byte menu_loop = 1; + byte menu_loop2 = 1; + char incoming_char = ' '; + + check_serial_override = 1; + + byte previous_key_tx_state = key_tx; + key_tx = 0; + + while(menu_loop){ + + while (port_to_use->available() > 0) { // clear out the buffer if anything is there + port_to_use->read(); + } + + port_to_use->println(F("\r\n\nCW Training Menu\n")); + port_to_use->println(F("C - Receive Practice")); + port_to_use->println(F("I - Keyboard Interactive Receive Practice")); + port_to_use->println(F("R - Random Groups Receive Practice")); + port_to_use->println(F("W - Wordsworth Receive Practice")); + port_to_use->println(F("E - Receive / Transmit Echo Practice")); + //port_to_use->println("2 - PA QSO Party"); // Don't think this is working right / wasn't finished - Goody 2017-05-01 + port_to_use->println(F("\nX - Exit\n")); + + menu_loop2 = 1; + + while (menu_loop2) { + + if (port_to_use->available()){ + incoming_char = port_to_use->read(); + if ((incoming_char != 10) && (incoming_char != 13)){ + menu_loop2 = 0; + } + } + } + + incoming_char = toUpperCase(incoming_char); + + switch(incoming_char){ + case 'X': menu_loop = 0; break; + case 'C': serial_receive_practice_menu(port_to_use,PRACTICE_NON_INTERACTIVE); break; + case 'I': serial_receive_practice_menu(port_to_use,PRACTICE_INTERACTIVE); break; + case 'R': serial_random_menu(port_to_use); break; + case 'W': serial_wordsworth_menu(port_to_use); break; + case 'E': serial_receive_transmit_echo_menu(port_to_use); break; + //case '2': paqso_practice(port_to_use); break; + } //switch(incoming_char) + + } //while(menu_loop) + + port_to_use->println(F("Exited Training module...")); + check_serial_override = 0; + key_tx = previous_key_tx_state; + paddle_echo_buffer = 0; +} +#endif + +//--------------------------------------------------------------------- + +#if defined(FEATURE_SERIAL) && defined(FEATURE_TRAINING_COMMAND_LINE_INTERFACE) && defined(FEATURE_COMMAND_LINE_INTERFACE) +void serial_receive_transmit_echo_menu(PRIMARY_SERIAL_CLS * port_to_use) { + + + byte menu_loop = 1; + byte menu_loop2 = 1; + char incoming_char = ' '; + + while(menu_loop) { + + while (port_to_use->available() > 0) { // clear out the buffer if anything is there + port_to_use->read(); + } + + port_to_use->println(F("\r\n\nReceive / Transmit Echo Practice Menu\n")); + port_to_use->println(F("I - International Callsigns")); + port_to_use->println(F("U - US Callsigns")); + port_to_use->println(F("E - European Callsigns")); + port_to_use->println(F("C - Canadian Callsigns")); + port_to_use->println(F("P - Progressive 5 Character Groups")); + port_to_use->println(F("2 - Two Letter Words")); + port_to_use->println(F("3 - Three Letter Words")); + port_to_use->println(F("4 - Four Letter Words")); + port_to_use->println(F("N - Names")); + port_to_use->println(F("Q - QSO Words")); + port_to_use->println(F("M - Mixed\n")); + port_to_use->println(F("\nX - Exit\n")); + + menu_loop2 = 1; + + while (menu_loop2) { + if (port_to_use->available()){ + incoming_char = port_to_use->read(); + if ((incoming_char != 10) && (incoming_char != 13)) { + menu_loop2 = 0; + } + } + } + + incoming_char = toUpperCase(incoming_char); + + switch(incoming_char) { + case 'X': menu_loop = 0; break; + case 'I': receive_transmit_echo_practice(port_to_use,CALLSIGN_INTERNATIONAL); break; + case 'U': receive_transmit_echo_practice(port_to_use,CALLSIGN_US); break; + case 'E': receive_transmit_echo_practice(port_to_use,CALLSIGN_EUROPEAN); break; + case 'C': receive_transmit_echo_practice(port_to_use,CALLSIGN_CANADA); break; + case 'P': receive_transmit_echo_practice(port_to_use,ECHO_PROGRESSIVE_5); break; + case '2': receive_transmit_echo_practice(port_to_use,ECHO_2_CHAR_WORDS); break; + case '3': receive_transmit_echo_practice(port_to_use,ECHO_3_CHAR_WORDS); break; + case '4': receive_transmit_echo_practice(port_to_use,ECHO_4_CHAR_WORDS); break; + case 'N': receive_transmit_echo_practice(port_to_use,ECHO_NAMES); break; + case 'M': receive_transmit_echo_practice(port_to_use,ECHO_MIXED); break; + case 'Q': receive_transmit_echo_practice(port_to_use,ECHO_QSO_WORDS); break; + } //switch(incoming_char) + + } // while(menu_loop) + + port_to_use->println(F("Exiting receive / transmit echo practice...")); +} +#endif + +//--------------------------------------------------------------------- + +#if defined(FEATURE_SERIAL) && defined(FEATURE_TRAINING_COMMAND_LINE_INTERFACE) && defined(FEATURE_COMMAND_LINE_INTERFACE) +void receive_transmit_echo_practice(PRIMARY_SERIAL_CLS * port_to_use, byte practice_mode_called) { + + byte loop1 = 1; + byte loop2 = 0; + byte x = 0; + byte user_send_loop = 0; + String cw_to_send_to_user(10); + char incoming_char = ' '; + String user_sent_cw = ""; + byte paddle_hit = 0; + unsigned long last_element_time = 0; + unsigned long cw_char; + byte speed_mode_before = speed_mode; + byte keyer_mode_before = configuration.keyer_mode; + byte progressive_step_counter; + byte practice_mode; + char word_buffer[10]; + + speed_mode = SPEED_NORMAL; // put us in normal speed mode + if ((configuration.keyer_mode != IAMBIC_A) && (configuration.keyer_mode != IAMBIC_B)) { + configuration.keyer_mode = IAMBIC_B; // we got to be in iambic mode (life is too short to make this work in bug mode) + } + + randomSeed(millis()); + + #ifdef FEATURE_DISPLAY + lcd_clear(); + if (LCD_COLUMNS > 17) { + lcd_center_print_timed("Receive / Transmit", 0, default_display_msg_delay); + lcd_center_print_timed("Echo Practice", 1, default_display_msg_delay); + } else { + lcd_center_print_timed("RX / TX", 0, default_display_msg_delay); + if (LCD_ROWS > 1) { + if (LCD_COLUMNS < 9) { + lcd_center_print_timed("EchoPrct", 1, default_display_msg_delay); + } else { + lcd_center_print_timed("Echo Practice", 1, default_display_msg_delay); + } + } + } + service_display(); + #endif + + port_to_use->println(F("Receive / Transmit Echo Practice\r\n\r\nCopy the code and send it back using the paddle.")); + port_to_use->println(F("Enter a blackslash \\ to exit.\r\n")); + while (port_to_use->available() > 0) { // clear out the buffer if anything is there + incoming_char = port_to_use->read(); + } + port_to_use->print(F("Press enter to start...\r\n")); + while (port_to_use->available() == 0) { + } + while (port_to_use->available() > 0) { // clear out the buffer if anything is there + incoming_char = port_to_use->read(); + } + + while (loop1) { + if (practice_mode_called == ECHO_MIXED){ + practice_mode = random(ECHO_2_CHAR_WORDS,ECHO_QSO_WORDS+1); + } else { + practice_mode = practice_mode_called; + } + + progressive_step_counter = 255; + + switch (practice_mode) { + case CALLSIGN_INTERNATIONAL: + case CALLSIGN_US: + case CALLSIGN_EUROPEAN: + case CALLSIGN_CANADA: + cw_to_send_to_user = generate_callsign(practice_mode); + break; + case ECHO_PROGRESSIVE_5: + cw_to_send_to_user = (char)random(65,91); + cw_to_send_to_user.concat((char)random(65,91)); + cw_to_send_to_user.concat((char)random(65,91)); + cw_to_send_to_user.concat((char)random(65,91)); + cw_to_send_to_user.concat((char)random(65,91)); + progressive_step_counter = 1; + break; + case ECHO_2_CHAR_WORDS: + strcpy_P(word_buffer, (char*)pgm_read_word(&(s2_table[random(0,s2_size)]))); + cw_to_send_to_user = word_buffer; + break; + case ECHO_3_CHAR_WORDS: + strcpy_P(word_buffer, (char*)pgm_read_word(&(s3_table[random(0,s3_size)]))); + cw_to_send_to_user = word_buffer; + break; + case ECHO_4_CHAR_WORDS: + strcpy_P(word_buffer, (char*)pgm_read_word(&(s4_table[random(0,s4_size)]))); + cw_to_send_to_user = word_buffer; + break; + case ECHO_NAMES: + strcpy_P(word_buffer, (char*)pgm_read_word(&(name_table[random(0,name_size)]))); + cw_to_send_to_user = word_buffer; + break; + case ECHO_QSO_WORDS: + strcpy_P(word_buffer, (char*)pgm_read_word(&(qso_table[random(0,qso_size)]))); + cw_to_send_to_user = word_buffer; + break; + + } // switch (practice_mode) + + loop2 = 1; + + while (loop2){ + user_send_loop = 1; + user_sent_cw = ""; + cw_char = 0; + x = 0; + + // send the CW to the user + while ((x < (cw_to_send_to_user.length())) && (x < progressive_step_counter)) { + send_char(cw_to_send_to_user[x],KEYER_NORMAL); + // test + port_to_use->print(cw_to_send_to_user[x]); + // + x++; + } + port_to_use->println(); + + while (user_send_loop) { + // get their paddle input + + #ifdef FEATURE_DISPLAY + service_display(); + #endif + + #ifdef FEATURE_POTENTIOMETER + if (configuration.pot_activated) { + check_potentiometer(); + } + #endif + + #ifdef FEATURE_ROTARY_ENCODER + check_rotary_encoder(); + #endif //FEATURE_ROTARY_ENCODER + + check_paddles(); + + if (dit_buffer) { + sending_mode = MANUAL_SENDING; + send_dit(); + dit_buffer = 0; + paddle_hit = 1; + cw_char = (cw_char * 10) + 1; + last_element_time = millis(); + } + if (dah_buffer) { + sending_mode = MANUAL_SENDING; + send_dah(); + dah_buffer = 0; + paddle_hit = 1; + cw_char = (cw_char * 10) + 2; + last_element_time = millis(); + } + + // have we hit letterspace time (end of a letter?) + if ((paddle_hit) && (millis() > (last_element_time + (float(600/configuration.wpm) * length_letterspace)))) { + #ifdef DEBUG_PRACTICE_SERIAL + debug_serial_port->println(F("receive_transmit_echo_practice: user_send_loop: hit length_letterspace")); + #endif // DEBUG_PRACTICE_SERIAL + incoming_char = convert_cw_number_to_ascii(cw_char); + port_to_use->print(incoming_char); + #ifdef FEATURE_DISPLAY + display_scroll_print_char(incoming_char); + service_display(); + #endif // FEATURE_DISPLAY + user_sent_cw.concat(incoming_char); + cw_char = 0; + paddle_hit = 0; + // TODO - print it to serial and lcd + } + + // do we have all the characters from the user? - if so, get out of user_send_loop + if ((user_sent_cw.length() >= cw_to_send_to_user.length()) || ((progressive_step_counter < 255) && (user_sent_cw.length() == progressive_step_counter))) { + user_send_loop = 0; + port_to_use->println(); + #ifdef FEATURE_DISPLAY + display_scroll_print_char(' '); + service_display(); + #endif + + } + + // does the user want to exit? + while(port_to_use->available() > 0) { + incoming_char = port_to_use->read(); + user_send_loop = 0; + loop1 = 0; + loop2 = 0; + if (correct_answer_led) digitalWrite(correct_answer_led, LOW); // clear the LEDs as we exit + if (wrong_answer_led) digitalWrite(wrong_answer_led, LOW); + } + #ifdef FEATURE_BUTTONS + while (analogbuttonread(0)) { // can exit by pressing the Command Mode button + user_send_loop = 0; + loop1 = 0; + loop2 = 0; + if (correct_answer_led) digitalWrite(correct_answer_led, LOW); // clear the LEDs as we exit + if (wrong_answer_led) digitalWrite(wrong_answer_led, LOW); + } + #endif // FEATURE_BUTTONS + + } //while (user_send_loop) + + if (loop1 && loop2) { + + if (progressive_step_counter < 255) { // we're in progressive mode + if (user_sent_cw.substring(0,progressive_step_counter) == cw_to_send_to_user.substring(0,progressive_step_counter)) { // characters are correct + if (correct_answer_led) digitalWrite(correct_answer_led, HIGH); // set the correct answer LED high + if (wrong_answer_led) digitalWrite(wrong_answer_led, LOW); // clear the wrong answer LED + beep(); + send_char(' ',0); + send_char(' ',0); + progressive_step_counter++; + if (progressive_step_counter == 6) { // all five characters are correct + loop2 = 0; + unsigned int NEWtone = 400; // the initial tone freuency for the tone sequence + unsigned int TONEduration = 50; // define the duration of each tone element in the tone sequence to drive a speaker + for (int k=0; k<6; k++) { // a loop to generate some increasing tones + tone(sidetone_line,NEWtone); // generate a tone on the speaker pin + delay(TONEduration); // hold the tone for the specified delay period + noTone(sidetone_line); // turn off the tone + NEWtone = NEWtone*1.25; // calculate a new value for the tone frequency + } // end for + send_char(' ', 0); + send_char(' ', 0); + } + } else { // characters are wrong + if (wrong_answer_led) digitalWrite(wrong_answer_led, HIGH); // set the wrong answer LED high + if (correct_answer_led) digitalWrite(correct_answer_led, LOW); // clear the correct answer LED + boop(); + send_char(' ', 0); + send_char(' ', 0); + } + } else { + if (user_sent_cw == cw_to_send_to_user) { // we only get here if progressive_step_counter is equal to 255 + beep(); + send_char(' ', 0); + send_char(' ',0); + loop2 = 0; + } else { + boop(); + send_char(' ',0); + send_char(' ',0); + } + } + } + + } // loop2 + } // loop1 + + + speed_mode = speed_mode_before; + configuration.keyer_mode = keyer_mode_before; + paddle_echo_buffer = 0; +} +#endif //defined(FEATURE_SERIAL) && defined(FEATURE_TRAINING_COMMAND_LINE_INTERFACE) && defined(FEATURE_COMMAND_LINE_INTERFACE) + +//--------------------------------------------------------------------- + +#if defined(FEATURE_SERIAL) && defined(FEATURE_TRAINING_COMMAND_LINE_INTERFACE) && defined(FEATURE_COMMAND_LINE_INTERFACE) +void serial_receive_practice_menu(PRIMARY_SERIAL_CLS * port_to_use,byte practice_mode){ + + byte menu_loop = 1; + byte menu_loop2 = 1; + char incoming_char = ' '; + + while(menu_loop) { + + while (port_to_use->available() > 0) { // clear out the buffer if anything is there + port_to_use->read(); + } + + if (practice_mode == PRACTICE_INTERACTIVE){ + port_to_use->println(F("\r\n\nInteractive Receive Practice Menu\n")); + } else { + port_to_use->println(F("\r\n\nReceive Practice Menu\n")); + } + port_to_use->println(F("I - International Callsigns")); + port_to_use->println(F("U - US Callsigns")); + port_to_use->println(F("E - European Callsigns")); + port_to_use->println(F("C - Canadian Callsigns")); + port_to_use->println(F("2 - Two Letter Words")); + port_to_use->println(F("3 - Three Letter Words")); + port_to_use->println(F("4 - Four Letter Words")); + port_to_use->println(F("N - Names")); + port_to_use->println(F("Q - QSO Words")); + port_to_use->println(F("M - Mixed Words\n")); + port_to_use->println(F("\nX - Exit\n")); + menu_loop2 = 1; + + while (menu_loop2){ + + if (port_to_use->available()){ + incoming_char = port_to_use->read(); + if ((incoming_char != 10) && (incoming_char != 13)){ + menu_loop2 = 0; + } + } + } + + incoming_char = toUpperCase(incoming_char); + + if (practice_mode == PRACTICE_INTERACTIVE){ + switch(incoming_char){ + case 'X': menu_loop = 0; break; + case 'I': serial_practice_interactive(port_to_use,CALLSIGN_INTERNATIONAL); break; + case 'U': serial_practice_interactive(port_to_use,CALLSIGN_US); break; + case 'E': serial_practice_interactive(port_to_use,CALLSIGN_EUROPEAN); break; + case 'C': serial_practice_interactive(port_to_use,CALLSIGN_CANADA); break; + case '2': serial_practice_interactive(port_to_use,PRACTICE_2_CHAR_WORDS); break; + case '3': serial_practice_interactive(port_to_use,PRACTICE_3_CHAR_WORDS); break; + case '4': serial_practice_interactive(port_to_use,PRACTICE_4_CHAR_WORDS); break; + case 'N': serial_practice_interactive(port_to_use,PRACTICE_NAMES); break; + case 'M': serial_practice_interactive(port_to_use,PRACTICE_MIXED); break; + case 'Q': serial_practice_interactive(port_to_use,PRACTICE_QSO_WORDS); break; + } //switch(incoming_char) + } else { + switch(incoming_char){ + case 'X': menu_loop = 0; break; + case 'I': serial_practice_non_interactive(port_to_use,CALLSIGN_INTERNATIONAL); break; + case 'U': serial_practice_non_interactive(port_to_use,CALLSIGN_US); break; + case 'E': serial_practice_non_interactive(port_to_use,CALLSIGN_EUROPEAN); break; + case 'C': serial_practice_non_interactive(port_to_use,CALLSIGN_CANADA); break; + case '2': serial_practice_non_interactive(port_to_use,PRACTICE_2_CHAR_WORDS); break; + case '3': serial_practice_non_interactive(port_to_use,PRACTICE_3_CHAR_WORDS); break; + case '4': serial_practice_non_interactive(port_to_use,PRACTICE_4_CHAR_WORDS); break; + case 'N': serial_practice_non_interactive(port_to_use,PRACTICE_NAMES); break; + case 'M': serial_practice_non_interactive(port_to_use,PRACTICE_MIXED); break; + case 'Q': serial_practice_non_interactive(port_to_use,PRACTICE_QSO_WORDS); break; + } // switch(incoming_char) + + } + } // while(menu_loop) + + port_to_use->println(F("Exiting callsign training...")); + +} +#endif + +//--------------------------------------------------------------------- + +#if defined(FEATURE_SERIAL) && defined(FEATURE_TRAINING_COMMAND_LINE_INTERFACE) && defined(FEATURE_COMMAND_LINE_INTERFACE) +void serial_set_wordspace_parameters(PRIMARY_SERIAL_CLS * port_to_use,byte mode_select) { + + byte menu_loop = 1; + byte menu_loop2 = 1; + char incoming_char = ' '; + unsigned int temp_value; + + + while(menu_loop){ + + while (port_to_use->available() > 0) { // clear out the buffer if anything is there + port_to_use->read(); + } + + switch(mode_select){ + case WORDSWORTH_WORDSPACE: port_to_use->print(F("\r\nEnter Wordspace >")); break; + case WORDSWORTH_WPM: port_to_use->print(F("\r\nEnter WPM >")); break; + case WORDSWORTH_REPETITION: port_to_use->print(F("\r\nEnter Repetition >")); break; + } + + + menu_loop2 = 1; + temp_value = 0; + + while (menu_loop2){ + + if (port_to_use->available()){ + incoming_char = port_to_use->read(); + if ((incoming_char > 47) && (incoming_char < 58)){ + port_to_use->print(incoming_char); + temp_value = (temp_value * 10) + (incoming_char - 48); + } + if (incoming_char == 13){ // Enter Key + menu_loop2 = 0; + } + } + } + + // validate value + if (temp_value == 0){ + menu_loop = 0; // just blow out if nothing was entered + } else { + if ((temp_value > 0) && (temp_value < 101) && (mode_select == WORDSWORTH_WPM)){ + configuration.wpm = temp_value; + config_dirty = 1; + menu_loop = 0; + } else { + if ((temp_value > 1) && (temp_value < 13) && (mode_select == WORDSWORTH_WORDSPACE)){ + configuration.wordsworth_wordspace = temp_value; + config_dirty = 1; + menu_loop = 0; + } else { + if ((temp_value > 0) && (temp_value < 11) && (mode_select == WORDSWORTH_REPETITION)){ + configuration.wordsworth_repetition = temp_value; + config_dirty = 1; + menu_loop = 0; + } else { + port_to_use->println(F("\r\nOMG that's an invalid value. Try again, OM...")); + } + } + } + } + + } //while(menu_loop) + +} +#endif //defined(FEATURE_SERIAL) && defined(FEATURE_TRAINING_COMMAND_LINE_INTERFACE) && defined(FEATURE_COMMAND_LINE_INTERFACE) + + +//--------------------------------------------------------------------- + +#if defined(FEATURE_SERIAL) && defined(FEATURE_TRAINING_COMMAND_LINE_INTERFACE) && defined(FEATURE_COMMAND_LINE_INTERFACE) +void serial_random_menu(PRIMARY_SERIAL_CLS * port_to_use){ + + byte menu_loop = 1; + byte menu_loop2 = 1; + char incoming_char = ' '; + + while(menu_loop){ + + while (port_to_use->available() > 0) { // clear out the buffer if anything is there + port_to_use->read(); + } + + port_to_use->println(F("\r\n\nRandom Code Receive Practice Menu\n")); + port_to_use->println(F("A - Letter Groups")); + port_to_use->println(F("1 - Number Groups")); + port_to_use->println(F("M - Mixed Groups")); + port_to_use->println(F("\nX - Exit\n")); + + menu_loop2 = 1; + + while (menu_loop2){ + + if (port_to_use->available()){ + incoming_char = port_to_use->read(); + if ((incoming_char != 10) && (incoming_char != 13)){ + menu_loop2 = 0; + } + } + } + + incoming_char = toUpperCase(incoming_char); + + switch(incoming_char){ + case 'A': random_practice(port_to_use,RANDOM_LETTER_GROUPS,5); break; + case '1': random_practice(port_to_use,RANDOM_NUMBER_GROUPS,5); break; + case 'M': random_practice(port_to_use,RANDOM_MIXED_GROUPS,5); break; + case 'X': menu_loop = 0; break; + } //switch(incoming_char) + + } //while(menu_loop) + + port_to_use->println(F("Exiting Random code module...")); +} +#endif //defined(FEATURE_SERIAL) && defined(FEATURE_TRAINING_COMMAND_LINE_INTERFACE) && defined(FEATURE_COMMAND_LINE_INTERFACE) + +//--------------------------------------------------------------------- + +#if defined(FEATURE_SERIAL) && defined(FEATURE_TRAINING_COMMAND_LINE_INTERFACE) && defined(FEATURE_COMMAND_LINE_INTERFACE) +void random_practice(PRIMARY_SERIAL_CLS * port_to_use,byte random_mode,byte group_size) { + + byte loop1 = 1; + byte x = 0; + byte y = 0; + char incoming_char = ' '; + char random_character = 0; + + randomSeed(millis()); + port_to_use->println(F("Random group practice\r\n")); + + #ifdef FEATURE_DISPLAY + lcd_clear(); + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("RndGroup", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Random Group", 0, default_display_msg_delay); + } + if (LCD_ROWS > 1){ + lcd_center_print_timed("Practice", 1, default_display_msg_delay); + } + service_display(); + #endif + + while (port_to_use->available() > 0) { // clear out the buffer if anything is there + incoming_char = port_to_use->read(); + } + + while (loop1){ + + switch(random_mode){ + case RANDOM_LETTER_GROUPS: random_character = random(65,91); break; + case RANDOM_NUMBER_GROUPS: random_character = random(48,58); break; + case RANDOM_MIXED_GROUPS: + random_character = random(65,101); + if (random_character > 90) {random_character = random_character - 43;}; + break; + } + + send_char(random_character,KEYER_NORMAL); + port_to_use->print(random_character); + #ifdef FEATURE_DISPLAY + display_scroll_print_char(random_character); + service_display(); + #endif + + + x++; + + if (x == group_size){ + send_char(' ',KEYER_NORMAL); + port_to_use->print(" "); + #ifdef FEATURE_DISPLAY + display_scroll_print_char(' '); + service_display(); + #endif + x = 0; + y++; + } + + if (y > 4){ + port_to_use->println(""); + y = 0; + } + + if (port_to_use->available()){ + port_to_use->read(); + loop1 = 0; + } + + #ifdef FEATURE_BUTTONS + while ((paddle_pin_read(paddle_left) == LOW) || (paddle_pin_read(paddle_right) == LOW) || (analogbuttonread(0))) { + loop1 = 0; + } + #else + while ((paddle_pin_read(paddle_left) == LOW) || (paddle_pin_read(paddle_right) == LOW)) { + loop1 = 0; + } + #endif //FEATURE_BUTTONS + + } //loop1 + + +} +#endif //defined(FEATURE_SERIAL) && defined(FEATURE_TRAINING_COMMAND_LINE_INTERFACE) && defined(FEATURE_COMMAND_LINE_INTERFACE) + +//--------------------------------------------------------------------- + +#if defined(FEATURE_SERIAL) && defined(FEATURE_TRAINING_COMMAND_LINE_INTERFACE) && defined(FEATURE_COMMAND_LINE_INTERFACE) +void serial_wordsworth_menu(PRIMARY_SERIAL_CLS * port_to_use){ + + byte menu_loop = 1; + byte menu_loop2 = 1; + char incoming_char = ' '; + byte effective_wpm_factor[] = {100,93,86,81,76,71,68,64,61,58,56,53}; + + while(menu_loop){ + + while (port_to_use->available() > 0) { // clear out the buffer if anything is there + port_to_use->read(); + } + + port_to_use->println(F("\r\n\nWordsworth Menu\n")); + port_to_use->println(F("2 - Two Letter Words")); + port_to_use->println(F("3 - Three Letter Words")); + port_to_use->println(F("4 - Four Letter Words")); + port_to_use->println(F("N - Names")); + port_to_use->println(F("Q - QSO Words")); + port_to_use->println(F("M - Mixed\n")); + port_to_use->println(F("O - Set Wordspace")); + port_to_use->println(F("W - Set WPM")); + port_to_use->println(F("R - Set Repetition")); + port_to_use->println(F("\nX - Exit\n")); + port_to_use->print(F("WPM:")); + port_to_use->print(configuration.wpm); + port_to_use->print(F(" Wordspace:")); + port_to_use->print(configuration.wordsworth_wordspace); + port_to_use->print(F(" Effective WPM:")); + port_to_use->print(configuration.wpm * (effective_wpm_factor[configuration.wordsworth_wordspace-1]/100.0),0); + port_to_use->print(F(" Repetition:")); + port_to_use->println(configuration.wordsworth_repetition); + port_to_use->println("\r\n\nEnter choice >"); + + menu_loop2 = 1; + + while (menu_loop2){ + + if (port_to_use->available()){ + incoming_char = port_to_use->read(); + if ((incoming_char != 10) && (incoming_char != 13)){ + menu_loop2 = 0; + } + } + } + + incoming_char = toUpperCase(incoming_char); + + switch(incoming_char){ + case '2': wordsworth_practice(port_to_use,WORDSWORTH_2_CHAR_WORDS); break; + case '3': wordsworth_practice(port_to_use,WORDSWORTH_3_CHAR_WORDS); break; + case '4': wordsworth_practice(port_to_use,WORDSWORTH_4_CHAR_WORDS); break; + case 'N': wordsworth_practice(port_to_use,WORDSWORTH_NAMES); break; + case 'M': wordsworth_practice(port_to_use,WORDSWORTH_MIXED); break; + case 'Q': wordsworth_practice(port_to_use,WORDSWORTH_QSO_WORDS); break; + case 'W': serial_set_wordspace_parameters(port_to_use,WORDSWORTH_WPM); break; + case 'O': serial_set_wordspace_parameters(port_to_use,WORDSWORTH_WORDSPACE); break; + case 'R': serial_set_wordspace_parameters(port_to_use,WORDSWORTH_REPETITION); break; + case 'X': menu_loop = 0; break; + } //switch(incoming_char) + + + + } //while(menu_loop) + + port_to_use->println(F("Exiting Wordsworth module...")); + + +} +#endif //defined(FEATURE_SERIAL) && defined(FEATURE_TRAINING_COMMAND_LINE_INTERFACE) && defined(FEATURE_COMMAND_LINE_INTERFACE) + + +//--------------------------------------------------------------------- + +#if defined(FEATURE_SERIAL) && defined(FEATURE_TRAINING_COMMAND_LINE_INTERFACE) && defined(FEATURE_COMMAND_LINE_INTERFACE) +void wordsworth_practice(PRIMARY_SERIAL_CLS * port_to_use,byte practice_type) +{ + + byte loop1 = 1; + byte loop2; + byte loop3; + unsigned int word_index; + char word_buffer[10]; + byte x; + byte not_printed; + byte practice_type_called = practice_type; + byte repetitions; + + + randomSeed(millis()); + port_to_use->println(F("Wordsworth practice...\n")); + + #ifdef FEATURE_DISPLAY + lcd_clear(); + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("Wrdswrth", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Wordsworth", 0, default_display_msg_delay); + } + if (LCD_ROWS > 1){ + lcd_center_print_timed("Practice", 1, default_display_msg_delay); + } + service_display(); + #endif + + while (port_to_use->available() > 0) { // clear out the buffer if anything is there + port_to_use->read(); + } + + + while (loop1){ + + if (practice_type_called == WORDSWORTH_MIXED){ + practice_type = random(WORDSWORTH_2_CHAR_WORDS,WORDSWORTH_QSO_WORDS+1); + } else { + practice_type = practice_type_called; + } + + switch(practice_type){ + case WORDSWORTH_2_CHAR_WORDS: + word_index = random(0,s2_size); // min parm is inclusive, max parm is exclusive + strcpy_P(word_buffer, (char*)pgm_read_word(&(s2_table[word_index]))); + break; + case WORDSWORTH_3_CHAR_WORDS: + word_index = random(0,s3_size); // min parm is inclusive, max parm is exclusive + strcpy_P(word_buffer, (char*)pgm_read_word(&(s3_table[word_index]))); + break; + case WORDSWORTH_4_CHAR_WORDS: + word_index = random(0,s4_size); // min parm is inclusive, max parm is exclusive + strcpy_P(word_buffer, (char*)pgm_read_word(&(s4_table[word_index]))); + break; + case WORDSWORTH_NAMES: + word_index = random(0,name_size); // min parm is inclusive, max parm is exclusive + strcpy_P(word_buffer, (char*)pgm_read_word(&(name_table[word_index]))); + break; + case WORDSWORTH_QSO_WORDS: + word_index = random(0,qso_size); // min parm is inclusive, max parm is exclusive + strcpy_P(word_buffer, (char*)pgm_read_word(&(qso_table[word_index]))); + break; + } + + #if defined(DEBUG_WORDSWORTH) + debug_serial_port->print("wordsworth_practice: word_index:"); + debug_serial_port->println(word_index); + debug_serial_port->print("wordsworth_practice: word_buffer:"); + debug_serial_port->println(word_buffer); + #endif + + loop3 = 1; + repetitions = 0; + + while ((loop3) && (repetitions < configuration.wordsworth_repetition)){ // word sending loop + + loop2 = 1; + x = 0; + + while (loop2){ //character sending loop + + #if defined(DEBUG_WORDSWORTH) + debug_serial_port->print("wordsworth_practice: send_char:"); + debug_serial_port->print(word_buffer[x]); + debug_serial_port->print(" "); + debug_serial_port->println((byte)word_buffer[x]); + #endif + + //word_buffer[x] = toUpperCase(word_buffer[x]); // word files should be in CAPS + + #if defined(OPTION_NON_ENGLISH_EXTENSIONS) + if (((byte)word_buffer[x] == 195) || ((byte)word_buffer[x] == 197)){ // do we have a unicode character? + x++; + if ((word_buffer[x] != 0) && (x < 10)){ + send_char(convert_unicode_to_send_char_code((byte)word_buffer[x-1],(byte)word_buffer[x]),KEYER_NORMAL); + #ifdef FEATURE_DISPLAY + display_scroll_print_char(convert_unicode_to_send_char_code((byte)word_buffer[x-1],(byte)word_buffer[x])); + service_display(); + #endif + x++; + } + } else { + send_char(word_buffer[x],KEYER_NORMAL); + #ifdef FEATURE_DISPLAY + display_scroll_print_char(word_buffer[x]); + service_display(); + #endif + x++; + } + #else //OPTION_NON_ENGLISH_EXTENSIONS + send_char(word_buffer[x],KEYER_NORMAL); + #ifdef FEATURE_DISPLAY + display_scroll_print_char(word_buffer[x]); + service_display(); + #endif + x++; + #endif //OPTION_NON_ENGLISH_EXTENSIONS + + not_printed = 1; + + if ((word_buffer[x] == 0) || (x > 9)){ // are we at the end of the word? + loop2 = 0; + for (int y = 0;y < (configuration.wordsworth_wordspace-1);y++){ // send extra word spaces + send_char(' ',KEYER_NORMAL); + #ifdef FEATURE_DISPLAY + display_scroll_print_char(' '); + service_display(); + #endif + if (((y > ((configuration.wordsworth_wordspace-1)/2)) || (configuration.wordsworth_wordspace < 4)) && (not_printed)){ + port_to_use->println(word_buffer); + not_printed = 0; + } + if (port_to_use->available()){ + port_to_use->read(); + y = 99; + loop1 = 0; + loop2 = 0; + loop3 = 0; + } + } + } + + if (port_to_use->available()){ + port_to_use->read(); + loop1 = 0; + loop2 = 0; + loop3 = 0; + } + } //loop2 + + repetitions++; + + } //loop3 + + } //loop1 + + + +} +#endif //defined(FEATURE_SERIAL) && defined(FEATURE_TRAINING_COMMAND_LINE_INTERFACE) && defined(FEATURE_COMMAND_LINE_INTERFACE) + +//--------------------------------------------------------------------- + +#if defined(FEATURE_SERIAL) && defined(FEATURE_TRAINING_COMMAND_LINE_INTERFACE) && defined(FEATURE_COMMAND_LINE_INTERFACE) +void serial_practice_interactive(PRIMARY_SERIAL_CLS * port_to_use,byte practice_type_called) +{ + + byte loop1 = 1; + byte loop2 = 0; + byte x = 0; + byte serialwaitloop = 0; + String cw_to_send_to_user(10); + char incoming_char = ' '; + String user_entered_cw = ""; + byte practice_type = 0; + char word_buffer[10]; + + randomSeed(millis()); + + #ifdef FEATURE_DISPLAY + lcd_clear(); + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("IntrctRX", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Interactive RX", 0, default_display_msg_delay); + } + if (LCD_ROWS > 1){ + lcd_center_print_timed("Practice", 1, default_display_msg_delay); + } + service_display(); + #endif + + port_to_use->println(F("Interactive receive practice\r\n\r\nCopy the code, type it in, and hit ENTER.")); + port_to_use->println(F("If you are using the Arduino serial monitor, select \"Carriage Return\" line ending.")); + port_to_use->println(F("Enter a blackslash \\ to exit.\r\n")); + while (port_to_use->available() > 0) { // clear out the buffer if anything is there + incoming_char = port_to_use->read(); + } + port_to_use->print(F("Press enter to start...\r\n")); + while (port_to_use->available() == 0) { + } + while (port_to_use->available() > 0) { // clear out the buffer if anything is there + incoming_char = port_to_use->read(); + } + + while (loop1){ + + if (practice_type_called == PRACTICE_MIXED){ + practice_type = random(PRACTICE_2_CHAR_WORDS,PRACTICE_QSO_WORDS+1); + } else { + practice_type = practice_type_called; + } + + switch(practice_type){ + case CALLSIGN_INTERNATIONAL: + case CALLSIGN_US: + case CALLSIGN_EUROPEAN: + case CALLSIGN_CANADA: + cw_to_send_to_user = generate_callsign(practice_type); + break; + case PRACTICE_2_CHAR_WORDS: + strcpy_P(word_buffer, (char*)pgm_read_word(&(s2_table[random(0,s2_size)]))); + cw_to_send_to_user = word_buffer; + #ifdef DEBUG_PRACTICE_SERIAL + debug_serial_port->print("serial_practice_interactive: PRACTICE_2_CHAR_WORDS:"); + #endif + break; + case PRACTICE_3_CHAR_WORDS: + strcpy_P(word_buffer, (char*)pgm_read_word(&(s3_table[random(0,s3_size)]))); + cw_to_send_to_user = word_buffer; + #ifdef DEBUG_PRACTICE_SERIAL + debug_serial_port->print("serial_practice_interactive: PRACTICE_3_CHAR_WORDS:"); + #endif + break; + case PRACTICE_4_CHAR_WORDS: + strcpy_P(word_buffer, (char*)pgm_read_word(&(s4_table[random(0,s4_size)]))); + cw_to_send_to_user = word_buffer; + #ifdef DEBUG_PRACTICE_SERIAL + debug_serial_port->print("serial_practice_interactive: PRACTICE_4_CHAR_WORDS:"); + #endif + break; + case PRACTICE_NAMES: + strcpy_P(word_buffer, (char*)pgm_read_word(&(name_table[random(0,name_size)]))); + cw_to_send_to_user = word_buffer; + #ifdef DEBUG_PRACTICE_SERIAL + debug_serial_port->print("serial_practice_interactive: PRACTICE_NAMES:"); + #endif + break; + case PRACTICE_QSO_WORDS: + strcpy_P(word_buffer, (char*)pgm_read_word(&(qso_table[random(0,qso_size)]))); + cw_to_send_to_user = word_buffer; + #ifdef DEBUG_PRACTICE_SERIAL + debug_serial_port->print("serial_practice_interactive: PRACTICE_QSO_WORDS:"); + #endif + break; + } //switch(practice_type) + + loop2 = 1; + + while (loop2){ + + #if defined(DEBUG_CALLSIGN_PRACTICE_SHOW_CALLSIGN) + port_to_use->println(callsign); + #endif + + serialwaitloop = 1; + user_entered_cw = ""; + x = 0; + while (serialwaitloop) { + + if(x < (cw_to_send_to_user.length())){ + send_char(cw_to_send_to_user[x],KEYER_NORMAL); + #ifdef FEATURE_DISPLAY + display_scroll_print_char(cw_to_send_to_user[x]); + service_display(); + #endif + x++; + } + + while(port_to_use->available() > 0) { + incoming_char = port_to_use->read(); + incoming_char = toUpperCase(incoming_char); + port_to_use->print(incoming_char); + if (incoming_char == 13) { + serialwaitloop = 0; + } else { + if (incoming_char != 10) { + user_entered_cw = user_entered_cw + incoming_char; + } + } + } + } + + if (user_entered_cw[0] != '?') { + if ((user_entered_cw[0] == '\\')){ + port_to_use->println(F("Exiting...\n")); + loop1 = 0; + loop2 = 0; + } else { + user_entered_cw.toUpperCase(); // the toUpperCase function was modified in 1.0; now it changes string in place + if (cw_to_send_to_user.compareTo(user_entered_cw) == 0) { + port_to_use->println(F("\nCorrect!")); + #ifdef FEATURE_DISPLAY + lcd_clear(); + lcd_center_print_timed("Correct!", 0, default_display_msg_delay); + service_display(); + #endif + loop2 = 0; + } else { + port_to_use->println(F("\nWrong!")); + #ifdef FEATURE_DISPLAY + lcd_clear(); + lcd_center_print_timed("Wrong!", 0, default_display_msg_delay); + service_display(); + #endif + } + } + } + + delay(100); + #ifdef FEATURE_BUTTONS + while ((paddle_pin_read(paddle_left) == LOW) || (paddle_pin_read(paddle_right) == LOW) || (analogbuttonread(0))) { + loop1 = 0; + loop2 = 0; + } + #else + while ((paddle_pin_read(paddle_left) == LOW) || (paddle_pin_read(paddle_right) == LOW)) { + loop1 = 0; + loop2 = 0; + } + #endif //FEATURE_BUTTONS + delay(10); + } //loop2 + } //loop1 + + +} +#endif //defined(FEATURE_SERIAL) && defined(FEATURE_TRAINING_COMMAND_LINE_INTERFACE) && defined(FEATURE_COMMAND_LINE_INTERFACE) + +//--------------------------------------------------------------------- + +#if defined(FEATURE_SERIAL) && defined(FEATURE_TRAINING_COMMAND_LINE_INTERFACE) && defined(FEATURE_COMMAND_LINE_INTERFACE) +void serial_practice_non_interactive(PRIMARY_SERIAL_CLS * port_to_use,byte practice_type_called) +{ + + byte loop1 = 1; + byte loop2; + byte x; + String cw_to_send_to_user(10); + char incoming_char = ' '; + byte practice_type; + char word_buffer[10]; + + key_tx = 0; + randomSeed(millis()); + + #ifdef FEATURE_DISPLAY + lcd_clear(); + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("Call RX", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Callsign RX", 0, default_display_msg_delay); + } + if (LCD_ROWS > 1){ + lcd_center_print_timed("Practice", 1, default_display_msg_delay); + } + service_display(); + #endif + + port_to_use->println(F("Callsign receive practice\r\n")); + + while (port_to_use->available() > 0) { // clear out the buffer if anything is there + incoming_char = port_to_use->read(); + } + + + + while (loop1){ + + + + if (practice_type_called == PRACTICE_MIXED){ + practice_type = random(PRACTICE_2_CHAR_WORDS,PRACTICE_QSO_WORDS+1); + } else { + practice_type = practice_type_called; + } + + switch(practice_type){ + case CALLSIGN_INTERNATIONAL: + case CALLSIGN_US: + case CALLSIGN_EUROPEAN: + case CALLSIGN_CANADA: + cw_to_send_to_user = generate_callsign(practice_type); + break; + case PRACTICE_2_CHAR_WORDS: + strcpy_P(word_buffer, (char*)pgm_read_word(&(s2_table[random(0,s2_size)]))); + cw_to_send_to_user = word_buffer; + #ifdef DEBUG_PRACTICE_SERIAL + debug_serial_port->print("serial_practice_non_interactive: PRACTICE_2_CHAR_WORDS:"); + #endif + break; + case PRACTICE_3_CHAR_WORDS: + strcpy_P(word_buffer, (char*)pgm_read_word(&(s3_table[random(0,s3_size)]))); + cw_to_send_to_user = word_buffer; + #ifdef DEBUG_PRACTICE_SERIAL + debug_serial_port->print("serial_practice_non_interactive: PRACTICE_3_CHAR_WORDS:"); + #endif + break; + case PRACTICE_4_CHAR_WORDS: + strcpy_P(word_buffer, (char*)pgm_read_word(&(s4_table[random(0,s4_size)]))); + cw_to_send_to_user = word_buffer; + #ifdef DEBUG_PRACTICE_SERIAL + debug_serial_port->print("serial_practice_non_interactive: PRACTICE_4_CHAR_WORDS:"); + #endif + break; + case PRACTICE_NAMES: + strcpy_P(word_buffer, (char*)pgm_read_word(&(name_table[random(0,name_size)]))); + cw_to_send_to_user = word_buffer; + #ifdef DEBUG_PRACTICE_SERIAL + debug_serial_port->print("serial_practice_non_interactive: PRACTICE_NAMES:"); + #endif + break; + case PRACTICE_QSO_WORDS: + strcpy_P(word_buffer, (char*)pgm_read_word(&(qso_table[random(0,qso_size)]))); + cw_to_send_to_user = word_buffer; + #ifdef DEBUG_PRACTICE_SERIAL + debug_serial_port->print("serial_practice_non_interactive: PRACTICE_QSO_WORDS:"); + #endif + break; + } //switch(practice_type) + + + cw_to_send_to_user = cw_to_send_to_user + " "; + + + loop2 = 1; + x = 0; + + while ((loop2) && (x < (cw_to_send_to_user.length()))) { + + send_char(cw_to_send_to_user[x],KEYER_NORMAL); + #ifdef FEATURE_DISPLAY + display_scroll_print_char(cw_to_send_to_user[x]); + service_display(); + #endif + x++; + + if (port_to_use->available()){ + port_to_use->read(); + loop1 = 0; + loop2 = 0; + x = 99; + } + + #ifdef FEATURE_BUTTONS + while ((paddle_pin_read(paddle_left) == LOW) || (paddle_pin_read(paddle_right) == LOW) || (analogbuttonread(0))) { + loop1 = 0; + loop2 = 0; + x = 99; + } + #else + while ((paddle_pin_read(paddle_left) == LOW) || (paddle_pin_read(paddle_right) == LOW)) { + loop1 = 0; + loop2 = 0; + x = 99; + } + #endif //FEATURE_BUTTONS + + } //loop2 + + port_to_use->println(cw_to_send_to_user); + + } //loop1 + + +} +#endif //defined(FEATURE_SERIAL) && defined(FEATURE_TRAINING_COMMAND_LINE_INTERFACE) && defined(FEATURE_COMMAND_LINE_INTERFACE) + +//--------------------------------------------------------------------- + +#if defined(FEATURE_SERIAL) && defined(FEATURE_COMMAND_LINE_INTERFACE) +void serial_status(PRIMARY_SERIAL_CLS * port_to_use) { + + port_to_use->println(); + #if defined(FEATURE_AMERICAN_MORSE) + if (char_send_mode == AMERICAN_MORSE){port_to_use->println(F("American Morse"));} + #endif + #if defined(FEATURE_HELL) + if (char_send_mode == HELL){port_to_use->println(F("Hellschreiber"));} + #endif + switch (configuration.keyer_mode) { + case IAMBIC_A: port_to_use->print(F("Iambic A")); break; + case IAMBIC_B: port_to_use->print(F("Iambic B")); + #ifdef FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING + port_to_use->print(F(" / CMOS Super Keyer Timing: O")); + if (configuration.cmos_super_keyer_iambic_b_timing_on) { + port_to_use->print("n "); + port_to_use->print(configuration.cmos_super_keyer_iambic_b_timing_percent); + port_to_use->print("%"); + } else { + port_to_use->print(F("ff")); + } + #endif //FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING + break; + case BUG: port_to_use->print(F("Bug")); break; + case STRAIGHT: port_to_use->print(F("Straight Key")); break; + + #ifndef OPTION_NO_ULTIMATIC + case ULTIMATIC: + port_to_use->print(F("Ultimatic ")); + switch(ultimatic_mode){ + // case ULTIMATIC_NORMAL: + // port_to_use->print(F("Normal")); + // break; + case ULTIMATIC_DIT_PRIORITY: + port_to_use->print(F("Dit Priority")); + break; + case ULTIMATIC_DAH_PRIORITY: + port_to_use->print(F("Dah Priority")); + break; + } + break; + #endif // OPTION_NO_ULTIMATIC + case SINGLE_PADDLE: port_to_use->print(F("Single Paddle")); break; + + break; + } + port_to_use->println(); + port_to_use->print(F("Buffers: Dit O")); + if (configuration.dit_buffer_off){ + port_to_use->print(F("ff")); + } else { + port_to_use->print(F("n")); + } + port_to_use->print(F(" Dah O")); + if (configuration.dah_buffer_off){ + port_to_use->println(F("ff")); + } else { + port_to_use->println(F("n")); + } + if (speed_mode == SPEED_NORMAL) { + port_to_use->print(F("WPM: ")); + port_to_use->println(configuration.wpm,DEC); + port_to_use->print(F("Command Mode WPM: ")); + port_to_use->println(configuration.wpm_command_mode,DEC); + #ifdef FEATURE_FARNSWORTH + port_to_use->print(F("Farnsworth WPM: ")); + if (configuration.wpm_farnsworth < configuration.wpm) { + port_to_use->println(F("Disabled")); //(WD9DMP) + } else { + port_to_use->println(configuration.wpm_farnsworth,DEC); + } + #endif //FEATURE_FARNSWORTH + } else { + port_to_use->print(F("QRSS Mode Activated - Dit Length: ")); + port_to_use->print(qrss_dit_length,DEC); + port_to_use->println(F(" seconds")); + } + port_to_use->print(F("Sidetone: ")); + switch (configuration.sidetone_mode) { + case SIDETONE_ON: port_to_use->print(F("On")); break; //(WD9DMP) + case SIDETONE_OFF: port_to_use->print(F("Off")); break; //(WD9DMP) + case SIDETONE_PADDLE_ONLY: port_to_use->print(F("Paddle Only")); break; + } + port_to_use->print(" "); + port_to_use->print(configuration.hz_sidetone,DEC); + port_to_use->println(" hz"); + + // #if defined(FEATURE_SINEWAVE_SIDETONE) + // port_to_use->print(F("Sidetone Volume: ")); + // port_to_use->print(map(configuration.sidetone_volume,sidetone_volume_low_limit,sidetone_volume_high_limit,0,100)); + // port_to_use->println(F("%")); + // port_to_use->println(configuration.sidetone_volume); + // #endif //FEATURE_SINEWAVE_SIDETONE + + #ifdef FEATURE_SIDETONE_SWITCH + port_to_use->print(F("Sidetone Switch: ")); + port_to_use->println(sidetone_switch_value() ? F("On") : F("Off")); //(WD9DMP) + #endif // FEATURE_SIDETONE_SWITCH + port_to_use->print(F("Dah to dit: ")); + port_to_use->println((float(configuration.dah_to_dit_ratio)/100)); + port_to_use->print(F("Weighting: ")); + port_to_use->println(configuration.weighting,DEC); + port_to_use->print(F("Keying Compensation: ")); + port_to_use->print(configuration.keying_compensation,DEC); + port_to_use->println(F(" mS")); + port_to_use->print(F("Serial Number: ")); + port_to_use->println(serial_number,DEC); + #ifdef FEATURE_POTENTIOMETER + port_to_use->print(F("Potentiometer WPM: ")); + port_to_use->print(pot_value_wpm(),DEC); + port_to_use->print(F(" (")); + if (configuration.pot_activated != 1) { + port_to_use->print(F("Not ")); + } + port_to_use->print(F("Activated) Range: ")); + port_to_use->print(pot_wpm_low_value); + port_to_use->print(" - "); + port_to_use->print(pot_wpm_high_value); + port_to_use->println(F(" WPM")); + #endif + #ifdef FEATURE_AUTOSPACE + port_to_use->print(F("Autospace O")); + if (configuration.autospace_active) { + port_to_use->println(F("n")); + } else { + port_to_use->println(F("ff")); + } + port_to_use->print(F("Autospace Timing Factor: ")); + port_to_use->println((float)configuration.autospace_timing_factor/(float)100); + #endif + port_to_use->print(F("Wordspace: ")); + port_to_use->println(configuration.length_wordspace,DEC); + port_to_use->print(F("TX: ")); + port_to_use->println(configuration.current_tx); + + + #ifdef FEATURE_QLF + port_to_use->print(F("QLF: O")); + if (qlf_active){ + port_to_use->println(F("n")); + } else { + port_to_use->println(F("ff")); + } + #endif //FEATURE_QLF + + port_to_use->print(F("Quiet Paddle Interrupt: ")); + if (configuration.paddle_interruption_quiet_time_element_lengths > 0){ + port_to_use->println(configuration.paddle_interruption_quiet_time_element_lengths); + } else { + port_to_use->println(F("Off")); + } + + #if !defined(OPTION_EXCLUDE_MILL_MODE) + port_to_use->print(F("Mill Mode: O")); + if (configuration.cli_mode == CLI_NORMAL_MODE){ + port_to_use->println(F("ff")); + } else { + port_to_use->println(F("n")); + } + #endif // !defined(OPTION_EXCLUDE_MILL_MODE) + + port_to_use->print(F("PTT Buffered Character Hold: O")); + if (configuration.ptt_buffer_hold_active){ + port_to_use->println(F("n")); + } else { + port_to_use->println(F("ff")); + } + + if (configuration.ptt_disabled){ + port_to_use->println(F("PTT: Disabled")); + } + + port_to_use->print(F("TX Inhibit: O")); + if ((digitalRead(tx_inhibit_pin) == tx_inhibit_pin_active_state)){ + port_to_use->println(F("n")); + } else { + port_to_use->println(F("ff")); + } + port_to_use->print(F("TX Pause: O")); + if ((digitalRead(tx_pause_pin) == tx_pause_pin_active_state)){ + port_to_use->println(F("n")); + } else { + port_to_use->println(F("ff")); + } + + #if defined(FEATURE_BEACON_SETTING) + port_to_use->print(F("Beacon Mode At Boot Up: O")); + if (configuration.beacon_mode_on_boot_up){ + port_to_use->println(F("n")); + } else { + port_to_use->println(F("ff")); + } + #endif + + #if defined(FEATURE_PADDLE_ECHO) + port_to_use->print(F("Paddle Echo: O")); + if (cli_paddle_echo){ + port_to_use->println(F("n")); + } else { + port_to_use->println(F("ff")); + } + port_to_use->print(F("Paddle Echo Timing Factor: ")); + port_to_use->println((float)configuration.cw_echo_timing_factor/(float)100); + #endif + + + #if defined(FEATURE_STRAIGHT_KEY_ECHO) + port_to_use->print(F("Straight Key Echo: O")); + if (cli_straight_key_echo){ + port_to_use->println(F("n")); + } else { + port_to_use->println(F("ff")); + } + #endif + + port_to_use->print(F("Tx")); // show the ptt lead time for the current tx + port_to_use->print(configuration.current_tx); + port_to_use->print(F(" lead time: ")); + port_to_use->println(configuration.ptt_lead_time[configuration.current_tx - 1]); + port_to_use->print(F("Tx")); // show the ptt tail time for the current tx + port_to_use->print(configuration.current_tx); + port_to_use->print(F(" tail time: ")); + port_to_use->println(configuration.ptt_tail_time[configuration.current_tx - 1]); + + port_to_use->print(F("PTT hang time: ")); // show the hang time + port_to_use->print(ptt_hang_time_wordspace_units); + port_to_use->println(F(" wordspace units")); + port_to_use->print(F("Memory repeat time: ")); // show the memory repeat time + port_to_use->println(configuration.memory_repeat_time); + + #ifdef FEATURE_MEMORIES + serial_status_memories(port_to_use); + #endif + + #ifdef DEBUG_MEMORYCHECK + memorycheck(); + #endif + + #ifdef DEBUG_VARIABLE_DUMP + port_to_use->println(configuration.wpm); + #ifdef FEATURE_FARNSWORTH + port_to_use->println(configuration.wpm_farnsworth); + #endif //FEATURE_FARNSWORTH + port_to_use->println(1.0*(float(configuration.weighting)/50)); + port_to_use->println(configuration.keying_compensation,DEC); + port_to_use->println(2.0-(float(configuration.weighting)/50)); + port_to_use->println(-1.0*configuration.keying_compensation); + port_to_use->println((dit_end_time-dit_start_time),DEC); + port_to_use->println((dah_end_time-dah_start_time),DEC); + port_to_use->println(millis(),DEC); + #endif //DEBUG_VARIABLE_DUMP + + #ifdef DEBUG_BUTTONS + for (int x = 0;x < analog_buttons_number_of_buttons;x++) { + port_to_use->print(F("analog_button_array: ")); + port_to_use->print(x); + port_to_use->print(F(" button_array_low_limit: ")); + port_to_use->print(button_array_low_limit[x]); + port_to_use->print(F(" button_array_high_limit: ")); + port_to_use->println(button_array_high_limit[x]); + } + #endif +//aaaaaaa + #if defined(FEATURE_ETHERNET) + port_to_use->print(F("Ethernet: ")); + port_to_use->print(configuration.ip[0]); + port_to_use->print(F(".")); + port_to_use->print(configuration.ip[1]); + port_to_use->print(F(".")); + port_to_use->print(configuration.ip[2]); + port_to_use->print(F(".")); + port_to_use->println(configuration.ip[3]); + #endif + + port_to_use->println(F(">")); + +} +#endif + +//--------------------------------------------------------------------- + + + +#if defined(OPTION_PROSIGN_SUPPORT) +char * convert_prosign(byte prosign_code) +{ + + switch(prosign_code){ + case PROSIGN_AA: return((char*)"AA"); break; + case PROSIGN_AS: return((char*)"AS"); break; + case PROSIGN_BK: return((char*)"BK"); break; + case PROSIGN_CL: return((char*)"CL"); break; + case PROSIGN_CT: return((char*)"CT"); break; + case PROSIGN_KN: return((char*)"KN"); break; + case PROSIGN_NJ: return((char*)"NJ"); break; + case PROSIGN_SK: return((char*)"SK"); break; + case PROSIGN_SN: return((char*)"SN"); break; + case PROSIGN_HH: return((char*)"HH"); break; // iz0rus + default: return((char*)""); break; + + } + +} +#endif //OPTION_PROSIGN_SUPPORT + +//--------------------------------------------------------------------- + +int convert_cw_number_to_ascii (long number_in) +{ + + // number_in: 1 = dit, 2 = dah, 9 = a space + + switch (number_in) { + case 12: return 65; break; // A + case 2111: return 66; break; + case 2121: return 67; break; + case 211: return 68; break; + case 1: return 69; break; + case 1121: return 70; break; + case 221: return 71; break; + case 1111: return 72; break; + case 11: return 73; break; + case 1222: return 74; break; + case 212: return 75; break; + case 1211: return 76; break; + case 22: return 77; break; + case 21: return 78; break; + case 222: return 79; break; + case 1221: return 80; break; + case 2212: return 81; break; + case 121: return 82; break; + case 111: return 83; break; + case 2: return 84; break; + case 112: return 85; break; + case 1112: return 86; break; + case 122: return 87; break; + case 2112: return 88; break; + case 2122: return 89; break; + case 2211: return 90; break; // Z + + case 22222: return 48; break; // 0 + case 12222: return 49; break; + case 11222: return 50; break; + case 11122: return 51; break; + case 11112: return 52; break; + case 11111: return 53; break; + case 21111: return 54; break; + case 22111: return 55; break; + case 22211: return 56; break; + case 22221: return 57; break; + case 112211: return '?'; break; // ? + case 21121: return 47; break; // / + #if !defined(OPTION_PROSIGN_SUPPORT) + case 2111212: return '*'; break; // BK + #endif +// case 221122: return '!'; break; // ! sp5iou 20180328 + case 221122: return ','; break; + case 121212: return '.'; break; + case 122121: return '@'; break; + case 222222: return 92; break; // special hack; six dahs = \ (backslash) + case 21112: return '='; break; // BT + case 211112: return '-'; break; + //case 2222222: return '+'; break; + case 9: return 32; break; // special 9 = space + + #ifndef OPTION_PS2_NON_ENGLISH_CHAR_LCD_DISPLAY_SUPPORT + case 12121: return '+'; break; + #else + + case 212122: return 33; break; // ! //sp5iou + case 1112112: return 36; break; // $ //sp5iou + #if !defined(OPTION_PROSIGN_SUPPORT) + case 12111: return 38; break; // & // sp5iou + #endif + case 122221: return 39; break; // ' // sp5iou + case 121121: return 34; break; // " // sp5iou + case 112212: return 95; break; // _ // sp5iou + case 212121: return 59; break; // ; // sp5iou + case 222111: return 58; break; // : // sp5iou + case 212212: return 41; break; // KK (stored as ascii ) ) // sp5iou + #if !defined(OPTION_PROSIGN_SUPPORT) + case 111212: return 62; break; // SK (stored as ascii > ) // sp5iou + #endif + case 12121: return 60; break; // AR (store as ascii < ) // sp5iou + #endif //OPTION_PS2_NON_ENGLISH_CHAR_LCD_DISPLAY_SUPPORT + + + #if defined(OPTION_PROSIGN_SUPPORT) + #if !defined(OPTION_NON_ENGLISH_EXTENSIONS) + case 1212: return PROSIGN_AA; break; + #endif + case 12111: return PROSIGN_AS; break; + case 2111212: return PROSIGN_BK; break; + case 21211211: return PROSIGN_CL; break; + case 21212: return PROSIGN_CT; break; + case 21221: return PROSIGN_KN; break; + case 211222: return PROSIGN_NJ; break; + case 111212: return PROSIGN_SK; break; + case 11121: return PROSIGN_SN; break; + case 11111111: return PROSIGN_HH; break; // iz0rus + #else //OPTION_PROSIGN_SUPPORT + case 21221: return 40; break; // (KN store as ascii ( ) //sp5iou //aaaaaaa + #endif //OPTION_PROSIGN_SUPPORT + + #ifdef OPTION_NON_ENGLISH_EXTENSIONS + // for English/Cyrillic/Western European font LCD controller (HD44780UA02): + case 12212: return 197; break; // 'Ã…' - AA_capital (OZ, LA, SM) + //case 12212: return 192; break; // 'À' - A accent + case 1212: return 198; break; // 'Æ' - AE_capital (OZ, LA) + //case 1212: return 196; break; // 'Ä' - A_umlaut (D, SM, OH, ...) + case 2222: return 138; break; // CH - (Russian letter symbol) + case 22122: return 209; break; // 'Ñ' - (EA) + //case 2221: return 214; break; // 'Ö' – O_umlaut (D, SM, OH, ...) + //case 2221: return 211; break; // 'Ã’' - O accent + case 2221: return 216; break; // 'Ø' - OE_capital (OZ, LA) + case 1122: return 220; break; // 'Ãœ' - U_umlaut (D, ...) + case 111111: return 223; break; // beta - double S (D?, ...) + case 21211: return 199; break; // Ç + case 11221: return 208; break; // à + case 12112: return 200; break; // È + case 11211: return 201; break; // É + case 221121: return 142; break; // Ž + #endif //OPTION_NON_ENGLISH_EXTENSIONS + + + default: + #ifdef OPTION_UNKNOWN_CHARACTER_ERROR_TONE + boop(); + #endif //OPTION_UNKNOWN_CHARACTER_ERROR_TONE + return unknown_cw_character; + break; + + } + +} + +//--------------------------------------------------------------------- +#ifdef DEBUG_MEMORYCHECK +void memorycheck() +{ + void* HP = malloc(4); + if (HP) + free (HP); + + unsigned long free = (unsigned long)SP - (unsigned long)HP; + +// port_to_use->print("Heap="); +// port_to_use->println((unsigned long)HP,HEX); +// port_to_use->print("Stack="); +// port_to_use->println((unsigned long)SP,HEX); +// port_to_use->print("Free Memory = "); +// port_to_use->print((unsigned long)free,HEX); +// port_to_use->print(" "); + + // if (free > 2048) { + // free = 0; + // } + + if (primary_serial_port_mode == SERIAL_CLI) { + port_to_use->print((unsigned long)free,DEC); + port_to_use->println(F(" bytes free")); + } +} +#endif + +//--------------------------------------------------------------------- + +#ifdef FEATURE_MEMORIES +void initialize_eeprom_memories() +{ + for (int x = 0; x < number_of_memories; x++) { + EEPROM.write(memory_start(x),255); + } +} +#endif + +//--------------------------------------------------------------------- + +#if defined(FEATURE_MEMORIES) && defined(FEATURE_COMMAND_LINE_INTERFACE) +void serial_status_memories(PRIMARY_SERIAL_CLS * port_to_use) +{ + int last_memory_location; + + #if defined(OPTION_PROSIGN_SUPPORT) + byte eeprom_temp = 0; + static char * prosign_temp = 0; + #endif + + for (int x = 0; x < number_of_memories; x++) { + last_memory_location = memory_end(x) + 1 ; + port_to_use->write("Memory "); + port_to_use->print(x+1); + port_to_use->write(":"); + if ( EEPROM.read(memory_start(x)) == 255) { + port_to_use->write("{empty}"); + } else { + for (int y = (memory_start(x)); (y < last_memory_location); y++) { + if (EEPROM.read(y) < 255) { + #if defined(OPTION_PROSIGN_SUPPORT) + eeprom_temp = EEPROM.read(y); + if ((eeprom_temp > PROSIGN_START) && (eeprom_temp < PROSIGN_END)){ + prosign_temp = convert_prosign(eeprom_temp); + port_to_use->print(prosign_temp[0]); + port_to_use->print(prosign_temp[1]); + } else { + port_to_use->write(eeprom_temp); + } + #else + if ((EEPROM.read(y) == 32) && ((EEPROM.read(y+1) == 255) || ((y+1) >= last_memory_location))){ + port_to_use->write("_"); + } else { + port_to_use->write(EEPROM.read(y)); + } + #endif //OPTION_PROSIGN_SUPPORT + } else { + //port_to_use->write("$"); + y = last_memory_location; + } + } + } + + #if defined(DEBUG_MEMORY_LOCATIONS) + port_to_use->print(F(" start: ")); + port_to_use->print(memory_start(x)); + port_to_use->print(F(" end: ")); + port_to_use->print(memory_end(x)); + port_to_use->print(F(" size: ")); + port_to_use->print(memory_end(x)-memory_start(x)); + #endif + + port_to_use->println(); + } + + #if defined(DEBUG_MEMORY_LOCATIONS) + port_to_use->print(F("Config Area end: ")); + port_to_use->println(sizeof(configuration)); + #endif +} +#endif + +//--------------------------------------------------------------------- + +#if defined(FEATURE_SERIAL) && defined(FEATURE_MEMORIES) && defined(FEATURE_COMMAND_LINE_INTERFACE) +void serial_program_memory(PRIMARY_SERIAL_CLS * port_to_use) +{ + + + /* + + CLI memory programming test string + + WE LOVE RADIO AND SMALL COMPUTING DEVICES AND WE COMBINE THE TWO TO AUTOMATE EXPERIMENT OR JUST SEE IF SOMETHING CAN BE DONE WE BELIEVE ITS BETTER TO BUILD SOMETHING WITH OUR OWN HANDS HOWEVER SMALL OR IMPERFECT AND IMPROVE AND EXPAND IT OVER TIME WE SUPPORT EXPERIMENTERS OF ALL LEVELS AND EXCHANGE IDEAS ABOUT AMATEUR RADIO HARDWARE HOMEBREWING AND SOFTWARE DEVELOPMENT + + */ + + uint8_t incoming_serial_byte = 0; + uint8_t memory_number = 0; + uint8_t looping = 1; + int memory_index = 0; + uint8_t memory_number_entered = 0; + uint8_t memory_data_entered = 0; + uint8_t error_flag = 0; + uint8_t memory_1_or_1x_flag = 0; + + + uint8_t incoming_serial_byte_buffer[serial_program_memory_buffer_size]; + unsigned int incoming_serial_byte_buffer_size = 0; + + + while (looping){ + if (keyer_machine_mode == KEYER_NORMAL) { // might as well do something while we're waiting + check_paddles(); + service_dit_dah_buffers(); + } + + while ((port_to_use->available()) && (incoming_serial_byte_buffer_size < serial_program_memory_buffer_size)){ // get serial data if available + incoming_serial_byte_buffer[incoming_serial_byte_buffer_size] = uppercase(port_to_use->read()); + incoming_serial_byte_buffer_size++; + } + + if (incoming_serial_byte_buffer_size){ + incoming_serial_byte = incoming_serial_byte_buffer[0]; + port_to_use->write(incoming_serial_byte); + for (unsigned int x = 0;x < (incoming_serial_byte_buffer_size - 1);x++){ + incoming_serial_byte_buffer[x] = incoming_serial_byte_buffer[x+1]; + } + incoming_serial_byte_buffer_size--; + + if ((memory_1_or_1x_flag) && ((incoming_serial_byte < 48) || (incoming_serial_byte > 57))){ // do we have something other than a number? + memory_1_or_1x_flag = 0; + memory_number_entered = 1; + } + + if (!memory_number_entered) { + if ((incoming_serial_byte > 47) && (incoming_serial_byte < 58)) { // do we have a number? + if (memory_1_or_1x_flag){ + memory_number = incoming_serial_byte - 48 + 10; + memory_1_or_1x_flag = 0; + memory_number_entered = 1; + } else { + memory_number = incoming_serial_byte - 48; + if ((memory_number == 1) && (number_of_memories > 9)) { + memory_1_or_1x_flag = 1; + } else { + memory_number_entered = 1; + } + } + // memory number out of range check + if (memory_number > number_of_memories){ + looping = 0; + error_flag = 1; + } + } else { + looping = 0; + error_flag = 1; + } + + } else { + + if (incoming_serial_byte == 13){ // we got a carriage return + looping = 0; + EEPROM.write((memory_start(memory_number-1)+memory_index),255); + } else { // looking for memory data + memory_data_entered = 1; + #if !defined(OPTION_SAVE_MEMORY_NANOKEYER) + while ((port_to_use->available()) && (incoming_serial_byte_buffer_size < serial_program_memory_buffer_size)){ // get serial data if available + incoming_serial_byte_buffer[incoming_serial_byte_buffer_size] = uppercase(port_to_use->read()); + incoming_serial_byte_buffer_size++; + } + #endif + EEPROM.write((memory_start(memory_number-1)+memory_index),incoming_serial_byte); + #if !defined(OPTION_SAVE_MEMORY_NANOKEYER) + while ((port_to_use->available()) && (incoming_serial_byte_buffer_size < serial_program_memory_buffer_size)){ // get serial data if available + incoming_serial_byte_buffer[incoming_serial_byte_buffer_size] = uppercase(port_to_use->read()); + incoming_serial_byte_buffer_size++; + } + #endif + #ifdef DEBUG_EEPROM + debug_serial_port->print(F("serial_program_memory: wrote ")); + debug_serial_port->print(incoming_serial_byte); + debug_serial_port->print(F(" to location ")); + debug_serial_port->println((memory_start(memory_number-1)+memory_index)); + #endif + memory_index++; + if ((memory_start(memory_number-1) + memory_index) > (memory_end(memory_number-1)-2)) { // are we at last memory location? + looping = 0; + EEPROM.write((memory_start(memory_number-1)+memory_index),255); + port_to_use->println(F("Memory full, truncating.")); + } + + } + + } // + } + + } + + if ((memory_number_entered) && (memory_data_entered) && (!error_flag)){ + port_to_use->print(F("\n\rWrote memory ")); + port_to_use->println(memory_number); + } else { + port_to_use->println(F("\n\rError")); + } + + #if defined(ARDUINO_SAMD_VARIANT_COMPLIANCE) + EEPROM.commit(); + #endif + +} + +#endif + + +//--------------------------------------------------------------------- + +#if defined(FEATURE_MEMORIES) && defined(FEATURE_COMMAND_MODE) +void command_program_memory() +{ + int cw_char; + cw_char = get_cw_input_from_user(0); // get another cw character from the user to find out which memory number + #ifdef DEBUG_COMMAND_MODE + debug_serial_port->print(F("command_program_memory: cw_char: ")); + debug_serial_port->println(cw_char); + #endif + if (cw_char > 0) { + if ((cw_char == 12222) && (number_of_memories > 9)) { // we have a 1, this could be 1 or 1x + cw_char = get_cw_input_from_user((1200/configuration.wpm)*14); // give the user some time to enter a second digit + switch (cw_char) { + case 0: program_memory(0); break; // we didn't get anything, it's a 1 + case 22222: program_memory(9); break; + case 12222: program_memory(10); break; + case 11222: program_memory(11); break; + case 11122: program_memory(12); break; + case 11112: program_memory(13); break; + case 11111: program_memory(14); break; + case 21111: program_memory(15); break; + default: send_char('?',KEYER_NORMAL); break; + } + } else { + switch (cw_char) { + case 12222: program_memory(0); break; // 1 = memory 0 + case 11222: program_memory(1); break; + case 11122: program_memory(2); break; + case 11112: program_memory(3); break; + case 11111: program_memory(4); break; + case 21111: program_memory(5); break; + case 22111: program_memory(6); break; + case 22211: program_memory(7); break; + case 22221: program_memory(8); break; + //case 22222: program_memory(9); break; + default: send_char('?',KEYER_NORMAL); break; + } + } + } +} +#endif //FEATURE_COMMAND_MODE + +//--------------------------------------------------------------------- + +#ifdef FEATURE_MEMORIES +byte memory_nonblocking_delay(unsigned long delaytime) +{ + // 2012-04-20 was long starttime = millis(); + unsigned long starttime = millis(); + + while ((millis() - starttime) < delaytime) { + check_paddles(); + #ifdef FEATURE_BUTTONS + if (((dit_buffer) || (dah_buffer) || (analogbuttonread(0))) && (keyer_machine_mode != BEACON)) { // exit if the paddle or button0 was hit + #else + if (((dit_buffer) || (dah_buffer)) && (keyer_machine_mode != BEACON)) { // exit if the paddle or button0 was hit + #endif + dit_buffer = 0; + dah_buffer = 0; + #ifdef FEATURE_BUTTONS + while (analogbuttonread(0)) {} + #endif + return 1; + } + } + return 0; +} + +#endif + +//--------------------------------------------------------------------- +void check_button0() +{ + #ifdef FEATURE_BUTTONS + if (analogbuttonread(0)) {button0_buffer = 1;} + #endif +} + +//--------------------------------------------------------------------- +/* + +program HelloWorld; + +{ HelloWorld version 2019.12.27.02 by K3NG } + +VAR + Callsign:string; + +BEGIN + Write('What is your callsign: '); + Readln(Callsign); + WriteLn('Hello ', Callsign); +END. + +*/ +//--------------------------------------------------------------------- +#if defined(FEATURE_MEMORIES) || defined(FEATURE_COMMAND_LINE_INTERFACE) +void send_serial_number(byte cut_numbers,int increment_serial_number,byte buffered_sending){ + + String serial_number_string; + + serial_number_string = String(serial_number, DEC); + if (serial_number_string.length() < 3 ) { + if (cut_numbers){ + if (buffered_sending){ + add_to_send_buffer('T'); + } else { + if (keyer_machine_mode != KEYER_COMMAND_MODE){display_serial_number_character('T');} //Display the SN as well as play it unless playing back after programming for verification(WD9DMP) + send_char('T',KEYER_NORMAL); + } + } else { + if (buffered_sending){ + add_to_send_buffer('0'); + } else { + if (keyer_machine_mode != KEYER_COMMAND_MODE){display_serial_number_character('0');} //Display the SN as well as play it unless playing back after programming for verification(WD9DMP) + send_char('0',KEYER_NORMAL); + } + } + } + if (serial_number_string.length() == 1) { + if (cut_numbers){ + if (buffered_sending){ + add_to_send_buffer('T'); + } else { + if (keyer_machine_mode != KEYER_COMMAND_MODE){display_serial_number_character('T');} //Display the SN as well as play it unless playing back after programming for verification(WD9DMP) + send_char('T',KEYER_NORMAL); + } + } else { + if (buffered_sending){ + add_to_send_buffer('0'); + } else { + if (keyer_machine_mode != KEYER_COMMAND_MODE){display_serial_number_character('0');} //Display the SN as well as play it unless playing back after programming for verification(WD9DMP) + send_char('0',KEYER_NORMAL); + } + } + } + for (unsigned int a = 0; a < serial_number_string.length(); a++) { + if ((serial_number_string[a] == '0') && (cut_numbers)){ + if (buffered_sending){ + add_to_send_buffer('T'); + } else { + if (keyer_machine_mode != KEYER_COMMAND_MODE){display_serial_number_character('T');} //Display the SN as well as play it unless playing back after programming for verification(WD9DMP) + send_char('T',KEYER_NORMAL); + } + } else { + if ((serial_number_string[a] == '9') && (cut_numbers)) { + if (buffered_sending){ + add_to_send_buffer('N'); + } else { + if (keyer_machine_mode != KEYER_COMMAND_MODE){display_serial_number_character('N');} //Display the SN as well as play it unless playing back after programming for verification(WD9DMP) + send_char('N',KEYER_NORMAL); + } + } else { + if (buffered_sending){ + add_to_send_buffer(serial_number_string[a]); + } else { + if (keyer_machine_mode != KEYER_COMMAND_MODE){display_serial_number_character(serial_number_string[a]);} //Display the SN as well as play it unless playing back after programming for verification(WD9DMP) + send_char(serial_number_string[a],KEYER_NORMAL); + } + + } + } + } + + serial_number = serial_number + increment_serial_number; + +} + +#endif +//New function below to send serial number character to CLI as well as LCD (WD9DMP) +//--------------------------------------------------------------------- +#if defined(FEATURE_MEMORIES) || defined(FEATURE_COMMAND_LINE_INTERFACE) +void display_serial_number_character(char snumchar){ + #if defined(FEATURE_SERIAL) + #ifdef FEATURE_COMMAND_LINE_INTERFACE + primary_serial_port->write(snumchar); + #ifdef FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + secondary_serial_port->write(snumchar); + #endif //FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + #endif //FEATURE_COMMAND_LINE_INTERFACE +#endif // (FEATURE_SERIAL) +#ifdef FEATURE_DISPLAY + if (lcd_send_echo) { + display_scroll_print_char(snumchar); + service_display(); + } +#endif // FEATURE_DISPLAY +} + +#endif +//--------------------------------------------------------------------- + +#ifdef FEATURE_MEMORIES +byte play_memory(byte memory_number) { + + unsigned int jump_back_to_y = 9999; + byte jump_back_to_memory_number = 255; + static byte prosign_flag = 0; + static byte prosign_before_flag = 0; + byte eeprom_byte_read = 0; + byte pause_sending_buffer_backspace = 0; + + play_memory_prempt = 0; + + #if defined(OPTION_PROSIGN_SUPPORT) + byte eeprom_temp = 0; + static char * prosign_temp = 0; + #endif + + if (memory_number > (number_of_memories - 1)) { + boop(); + return 0; + } + + #ifdef DEBUG_PLAY_MEMORY + debug_serial_port->print(F("play_memory: called with memory_number:")); + debug_serial_port->println(memory_number); + #endif + + #ifdef FEATURE_MEMORY_MACROS + byte eeprom_byte_read2; + int z; + byte input_error; + byte delay_result = 0; + int int_from_macro; + #endif //FEATURE_MEMORY_MACROS + + button0_buffer = 0; + + if (keyer_machine_mode == KEYER_NORMAL) { + #if defined(FEATURE_SERIAL) + #ifdef FEATURE_WINKEY_EMULATION + if (primary_serial_port_mode != SERIAL_WINKEY_EMULATION) { + primary_serial_port->println(); + } + #else + primary_serial_port->println(); + #endif + + #ifdef FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + secondary_serial_port->println(); + #endif //FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + #endif + } + + for (int y = (memory_start(memory_number)); (y < (memory_end(memory_number)+1)); y++) { + + service_tx_inhibit_and_pause(); + + if (keyer_machine_mode == KEYER_NORMAL) { + #ifdef FEATURE_POTENTIOMETER + check_potentiometer(); + #endif + + #ifdef FEATURE_ROTARY_ENCODER + check_rotary_encoder(); + #endif //FEATURE_ROTARY_ENCODER + + #ifdef FEATURE_PS2_KEYBOARD + check_ps2_keyboard(); + #endif + + check_button0(); + + #ifdef FEATURE_DISPLAY + service_display(); + #endif + } + + #if defined(FEATURE_SERIAL) + check_serial(); + #endif + + if ((play_memory_prempt == 0) && (pause_sending_buffer == 0)) { + + pause_sending_buffer_backspace = 0; + + eeprom_byte_read = EEPROM.read(y); + if (eeprom_byte_read < 255) { + + #ifdef DEBUG_PLAY_MEMORY + debug_serial_port->println(F("\n\nplay_memory:\r")); + debug_serial_port->print(F(" Memory number:")); + debug_serial_port->println(memory_number); + debug_serial_port->print(F(" EEPROM location:")); + debug_serial_port->println(y); + debug_serial_port->print(F(" eeprom_byte_read:")); + debug_serial_port->println(eeprom_byte_read); + #endif + + if (eeprom_byte_read != 92) { // do we have a backslash? + if (keyer_machine_mode == KEYER_NORMAL) { + + #if defined(OPTION_PROSIGN_SUPPORT) + eeprom_temp = eeprom_byte_read; + if ((eeprom_temp > PROSIGN_START) && (eeprom_temp < PROSIGN_END)){ + prosign_temp = convert_prosign(eeprom_temp); + } + #endif //OPTION_PROSIGN_SUPPORT + + #if defined(FEATURE_SERIAL) + #ifndef FEATURE_WINKEY_EMULATION + if (!cw_send_echo_inhibit) { + #if defined(OPTION_PROSIGN_SUPPORT) + if ((eeprom_temp > PROSIGN_START) && (eeprom_temp < PROSIGN_END)){ + primary_serial_port->print(prosign_temp[0]); + primary_serial_port->print(prosign_temp[1]); + #ifdef FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + secondary_serial_port->print(prosign_temp[0]); + secondary_serial_port->print(prosign_temp[1]); + #endif //FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + } else { + primary_serial_port->write(eeprom_byte_read); + #ifdef FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + secondary_serial_port->write(eeprom_byte_read); + #endif //FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + } + #else + primary_serial_port->write(eeprom_byte_read); + #ifdef FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + secondary_serial_port->write(eeprom_byte_read); + #endif //FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + #endif // OPTION_PROSIGN_SUPPORT + } + #else //FEATURE_WINKEY_EMULATION + if (((primary_serial_port_mode == SERIAL_WINKEY_EMULATION) && (winkey_paddle_echo_activated) && (winkey_host_open)) || (primary_serial_port_mode != SERIAL_WINKEY_EMULATION)) { + + #if defined(OPTION_PROSIGN_SUPPORT) + if ((eeprom_temp > PROSIGN_START) && (eeprom_temp < PROSIGN_END)){ + winkey_port_write(prosign_temp[0],0); + winkey_port_write(prosign_temp[1],0); + } else { + winkey_port_write(eeprom_byte_read,0); + } + #else + winkey_port_write(eeprom_byte_read,0); + #endif // OPTION_PROSIGN_SUPPORT + + } + #ifdef FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + #if defined(OPTION_PROSIGN_SUPPORT) + if ((eeprom_temp > PROSIGN_START) && (eeprom_temp < PROSIGN_END)){ + secondary_serial_port->print(prosign_temp[0]); + secondary_serial_port->print(prosign_temp[1]); + } else { + secondary_serial_port->write(eeprom_byte_read); + } + #else + secondary_serial_port->write(eeprom_byte_read); + #endif // OPTION_PROSIGN_SUPPORT + #endif //FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + + #endif //FEATURE_WINKEY_EMULATION + #endif //FEATURE_SERIAL + + #ifdef FEATURE_DISPLAY + if (lcd_send_echo) { + #if defined(OPTION_PROSIGN_SUPPORT) + if ((eeprom_temp > PROSIGN_START) && (eeprom_temp < PROSIGN_END)){ + display_scroll_print_char(prosign_temp[0]); + display_scroll_print_char(prosign_temp[1]); + } else { + if (prosign_flag){ + display_scroll_print_char(eeprom_byte_read); + display_scroll_print_char(EEPROM.read(y+1)); + prosign_before_flag = 1; + } else { + if (prosign_before_flag){ + prosign_before_flag = 0; + } else { + display_scroll_print_char(eeprom_byte_read); + } + } + } + #else + if (prosign_flag){ + display_scroll_print_char(eeprom_byte_read); + display_scroll_print_char(EEPROM.read(y+1)); + prosign_before_flag = 1; + } else { + if (prosign_before_flag){ + prosign_before_flag = 0; + } else { + display_scroll_print_char(eeprom_byte_read); + } + } + #endif + service_display(); + } + #endif // FEATURE_DISPLAY + + } + if (prosign_flag) { + send_char(eeprom_byte_read,OMIT_LETTERSPACE); + prosign_flag = 0; + } else { + send_char(eeprom_byte_read,KEYER_NORMAL); // no - play the character + } + } else { // yes - we have a backslash command ("macro") + y++; // get the next memory byte + #ifdef FEATURE_MEMORY_MACROS + if (y < (memory_end(memory_number)+1)) { + eeprom_byte_read = EEPROM.read(y); // memory macros (backslash commands) + switch (eeprom_byte_read) { + case 48: // 0 - jump to memory 10 + eeprom_byte_read = 58; + case 49: // 1 - jump to memory 1 + case 50: // 2 - jump to memory 2 + case 51: // 3 - jump to memory 3 + case 52: // 4 - jump to memory 4 + case 53: // 5 - jump to memory 5 + case 54: // 6 - jump to memory 6 + case 55: // 7 - jump to memory 7 + case 56: // 8 - jump to memory 8 + case 57: // 9 - jump to memory 9 + if (number_of_memories > (eeprom_byte_read-49)) { + memory_number = (eeprom_byte_read-49); + y = ((memory_start(memory_number)) - 1); + if (keyer_machine_mode == KEYER_NORMAL) { + primary_serial_port->println(); + #ifdef FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + secondary_serial_port->println(); + #endif //FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + } + } + break; + case 'I': // insert memory # + y++; + if (y < (memory_end(memory_number)+1)) { // get the next byte + eeprom_byte_read = EEPROM.read(y); + if (number_of_memories > (eeprom_byte_read-49)) { + jump_back_to_y = y; + jump_back_to_memory_number = memory_number; + memory_number = (eeprom_byte_read-49); + y = ((memory_start(memory_number)) - 1); + if (keyer_machine_mode == KEYER_NORMAL) { + primary_serial_port->println(); + #ifdef FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + secondary_serial_port->println(); + #endif //FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + } + } + } + break; + + case 'S': // insert space + send_char(' ',KEYER_NORMAL); + primary_serial_port->print(' '); + #ifdef FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + secondary_serial_port->print(' '); + #endif //FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + #ifdef FEATURE_DISPLAY + if (lcd_send_echo) { + display_scroll_print_char(' '); + } + #endif //FEATURE_DISPLAY + break; + + case 'X': // X - switch transmitter + y++; + if (y < (memory_end(memory_number)+1)) { + eeprom_byte_read2 = EEPROM.read(y); + if ((eeprom_byte_read2 > 48) && (eeprom_byte_read2 < 55)) { + switch (eeprom_byte_read2) { + case 49: switch_to_tx_silent(1); break; + case 50: if ((ptt_tx_2) || (tx_key_line_2)) {switch_to_tx_silent(2); } break; + case 51: if ((ptt_tx_3) || (tx_key_line_3)) {switch_to_tx_silent(3); } break; + case 52: if ((ptt_tx_4) || (tx_key_line_4)) {switch_to_tx_silent(4); } break; + case 53: if ((ptt_tx_5) || (tx_key_line_5)) {switch_to_tx_silent(5); } break; + case 54: if ((ptt_tx_6) || (tx_key_line_6)) {switch_to_tx_silent(6); } break; + } + } + + } + break; // case X + + case 'C': // C - play serial number with cut numbers T and N, then increment + send_serial_number(1,1,0); + break; + + case 'D': // D - delay for ### seconds + int_from_macro = 0; + z = 100; + input_error = 0; + for (int x = 1; x < 4; x ++) { + y++; + if (y < (memory_end(memory_number)+1)) { + eeprom_byte_read2 = EEPROM.read(y); + if ((eeprom_byte_read2 > 47) && (eeprom_byte_read2 < 58)) { // ascii 48-57 = "0" - "9") + int_from_macro = int_from_macro + ((eeprom_byte_read2 - 48) * z); + z = z / 10; + } else { + x = 4; // error - exit + input_error = 1; + y--; // go back one so we can at least play the errant character + } + } else { + x = 4; + input_error = 1; + } + } + if (input_error != 1) { // do the delay + delay_result = memory_nonblocking_delay(int_from_macro*1000); + } + if (delay_result) { // if a paddle or button0 was hit during the delay, exit + return 0; + } + break; // case D + + case 'E': // E - play serial number, then increment + send_serial_number(0,1,0); + break; + + case 'F': // F - change sidetone frequency + int_from_macro = 0; + z = 1000; + input_error = 0; + for (int x = 1; x < 5; x ++) { + y++; + if (y < (memory_end(memory_number)+1)) { + eeprom_byte_read2 = EEPROM.read(y); + if ((eeprom_byte_read2 > 47) && (eeprom_byte_read2 < 58)) { // ascii 48-57 = "0" - "9") + int_from_macro = int_from_macro + ((eeprom_byte_read2 - 48) * z); + z = z / 10; + } else { + x = 5; // error - exit + input_error = 1; + y--; // go back one so we can at least play the errant character + } + } else { + x = 4; + input_error = 1; + } + } + if ((input_error != 1) && (int_from_macro > sidetone_hz_limit_low) && (int_from_macro < sidetone_hz_limit_high)) { + configuration.hz_sidetone = int_from_macro; + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9) lcd_center_print_timed(String(configuration.hz_sidetone) + " Hz", 0, default_display_msg_delay); + else lcd_center_print_timed("Sidetone " + String(configuration.hz_sidetone) + " Hz", 0, default_display_msg_delay); + #endif // FEATURE_DISPLAY + } + break; + if ((input_error != 1) && (int_from_macro > sidetone_hz_limit_low) && (int_from_macro < sidetone_hz_limit_high)) { + configuration.hz_sidetone = int_from_macro; + } + break; + + case 'H': // H - Switch to Hell + char_send_mode = HELL; + break; + + case 'L': // L - Switch to CW + char_send_mode = CW; + break; + + case 'N': // N - decrement serial number (do not play) + serial_number--; + break; + + case '+': // + - Prosign + prosign_flag = 1; + break; + + case 'Q': // Q - QRSS mode and set dit length to ## + int_from_macro = 0; + z = 10; + input_error = 0; + for (int x = 1; x < 3; x ++) { + y++; + if (y < (memory_end(memory_number)+1)) { + eeprom_byte_read2 = EEPROM.read(y); + if ((eeprom_byte_read2 > 47) && (eeprom_byte_read2 < 58)) { // ascii 48-57 = "0" - "9") + int_from_macro = int_from_macro + ((eeprom_byte_read2 - 48) * z); + z = z / 10; + } else { + x = 4; // error - exit + input_error = 1; + y--; // go back one so we can at least play the errant character + } + } else { + x = 4; + input_error = 1; + } + } + if (input_error == 0) { + speed_mode = SPEED_QRSS; + qrss_dit_length = int_from_macro; + //calculate_element_length(); + } + break; //case Q + + case 'R': // R - regular speed mode + speed_mode = SPEED_NORMAL; + //calculate_element_length(); + break; + + case 'T': // T - transmit for ### seconds + int_from_macro = 0; + z = 100; + input_error = 0; + for (int x = 1; x < 4; x ++) { + y++; + if (y < (memory_end(memory_number)+1)) { + eeprom_byte_read2 = EEPROM.read(y); + if ((eeprom_byte_read2 > 47) && (eeprom_byte_read2 < 58)) { // ascii 48-57 = "0" - "9") + int_from_macro = int_from_macro + ((eeprom_byte_read2 - 48) * z); + z = z / 10; + } else { + x = 4; // error - exit + input_error = 1; + y--; // go back one so we can at least play the errant character + } + } else { + x = 4; + input_error = 1; + } + } + sending_mode = AUTOMATIC_SENDING; + if (input_error != 1) { // go ahead and transmit + tx_and_sidetone_key(1); + delay_result = memory_nonblocking_delay(int_from_macro*1000); + tx_and_sidetone_key(0); + } + if (delay_result) { // if a paddle or button0 was hit during the delay, exit + return 0; + } + break; // case T + + case 'U': // U - turn on PTT + manual_ptt_invoke = 1; + ptt_key(); + break; + + case 'V': // V - turn off PTT + manual_ptt_invoke = 0; + ptt_unkey(); + break; + + case 'W': // W - change speed to ### WPM + int_from_macro = 0; + z = 100; + input_error = 0; + for (int x = 1; x < 4; x ++) { + y++; + if (y < (memory_end(memory_number)+1)) { + eeprom_byte_read2 = EEPROM.read(y); + if ((eeprom_byte_read2 > 47) && (eeprom_byte_read2 < 58)) { // ascii 48-57 = "0" - "9") + int_from_macro = int_from_macro + ((eeprom_byte_read2 - 48) * z); + z = z / 10; + } else { + x = 4; // error - exit + input_error = 1; + y--; // go back one so we can at least play the errant character + } + } else { + x = 4; + input_error = 1; + } + } + if (input_error != 1) { + speed_mode = SPEED_NORMAL; + speed_set(int_from_macro); + } + break; // case W + + case 'Y': // Y - Relative WPM change (positive) + y++; + if ((y < (memory_end(memory_number)+1)) && (speed_mode == SPEED_NORMAL)) { + eeprom_byte_read2 = EEPROM.read(y); + if ((eeprom_byte_read2 > 47) && (eeprom_byte_read2 < 58)) { // ascii 48-57 = "0" - "9") + speed_set(configuration.wpm + eeprom_byte_read2 - 48); + } else { + y--; // go back one so we can at least play the errant character + } + } else { + } + break; // case Y + + case 'Z': // Z - Relative WPM change (positive) + y++; + if ((y < (memory_end(memory_number)+1)) && (speed_mode == SPEED_NORMAL)) { + eeprom_byte_read2 = EEPROM.read(y); + if ((eeprom_byte_read2 > 47) && (eeprom_byte_read2 < 58)) { // ascii 48-57 = "0" - "9") + speed_set(configuration.wpm - (eeprom_byte_read2 - 48)); + } else { + y--; // go back one so we can at least play the errant character + } + } else { + } + break; // case Z + + } + + } + #endif //FEATURE_MEMORY_MACROS + } + if (keyer_machine_mode != BEACON) { + #ifdef FEATURE_STRAIGHT_KEY + if ((dit_buffer) || (dah_buffer) || (button0_buffer) || (digitalRead(pin_straight_key) == STRAIGHT_KEY_ACTIVE_STATE)) { // exit if the paddle or button0 was hit + dit_buffer = 0; + dah_buffer = 0; + button0_buffer = 0; + repeat_memory = 255; + #ifdef FEATURE_BUTTONS + while (analogbuttonread(0)) {} + #endif + return 0; + } + #else //FEATURE_STRAIGHT_KEY + if ((dit_buffer) || (dah_buffer) || (button0_buffer)) { // exit if the paddle or button0 was hit + dit_buffer = 0; + dah_buffer = 0; + button0_buffer = 0; + repeat_memory = 255; + #ifdef FEATURE_BUTTONS + while (analogbuttonread(0)) {} + #endif + return 0; + } + #endif //FEATURE_STRAIGHT_KEY + } + + } else { + if (y == (memory_start(memory_number))) { // memory is totally empty - do a boop + repeat_memory = 255; + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("MemEmpty", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Memory empty", 0, default_display_msg_delay); + } + #else + boop(); + #endif + } + + // if we had an inserted memory, jump back to the original one + if (/*(y== (memory_end(memory_number)+1)) &&*/ (jump_back_to_y < 9999) && (jump_back_to_memory_number < 255)) { + #ifdef DEBUG_PLAY_MEMORY + debug_serial_port->print(F("\nplay_memory: jump back to original memory:")); + debug_serial_port->println(jump_back_to_memory_number); + #endif + y = jump_back_to_y; + memory_number = jump_back_to_memory_number; + jump_back_to_y = 9999; + jump_back_to_memory_number = 255; + } else { + + + + return 0; + } + } + } else { + if (pause_sending_buffer == 0) { + y = (memory_end(memory_number)+1); // we got a play_memory_prempt flag, exit out + } else { + y--; // we're in a pause mode, so sit and spin awhile + if ((y > (memory_start(memory_number)) && (!pause_sending_buffer_backspace))){ + y--; + pause_sending_buffer_backspace = 1; + } + check_ptt_tail(); + #if defined(FEATURE_SEQUENCER) + check_sequencer_tail_time(); + #endif + } + } + + last_memory_repeat_time = millis(); + #ifdef DEBUG_PLAY_MEMORY + debug_serial_port->println(F("\nplay_memory: reset last_memory_repeat_time")); + debug_serial_port->print("y: "); + debug_serial_port->print(y); + debug_serial_port->print("\tmemory_number: "); + debug_serial_port->print(memory_number); + debug_serial_port->print("\tmemory_end: "); + debug_serial_port->print(memory_end(memory_number)); + debug_serial_port->print("\tjump_back_to_y: "); + debug_serial_port->print(jump_back_to_y); + debug_serial_port->print("\tjump_back_to_memory_number: "); + debug_serial_port->println(jump_back_to_memory_number); + #endif + + + // if we had an inserted memory, jump back to the original one + /* + if ((y== (memory_end(memory_number)+1)) && (jump_back_to_y < 99999) && (jump_back_to_memory_number < 255)) { + primary_serial_port->print(F("\nplay_memory: jump back to original memory:")); + primary_serial_port->println(jump_back_to_memory_number); + y = jump_back_to_y; + memory_number = jump_back_to_memory_number; + jump_back_to_y = 99999; + jump_back_to_memory_number = 255; + } + */ + + + } //for (int y = (memory_start(memory_number)); (y < (memory_end(memory_number)+1)); y++) + +} +#endif + +//--------------------------------------------------------------------- + +#ifdef FEATURE_MEMORIES +void program_memory(int memory_number) +{ + + if (memory_number > (number_of_memories-1)) { + boop(); + return; + } + + #ifdef FEATURE_DISPLAY + String lcd_print_string; + if (LCD_COLUMNS < 9){ + lcd_print_string.concat("PgmMem"); + } else { + lcd_print_string.concat("Pgm Memory "); + } + lcd_print_string.concat(memory_number+1); + lcd_center_print_timed(lcd_print_string, 0, default_display_msg_delay); + #endif + + //send_dit(); + beep(); + + byte paddle_hit = 0; + byte loop1 = 1; + byte loop2 = 1; + unsigned long last_element_time = 0; + int memory_location_index = 0; + long cwchar = 0; + byte space_count = 0; + + #ifdef FEATURE_MEMORY_MACROS + byte macro_flag = 0; + #endif //FEATURE_MEMORY_MACROS + + #if defined(FEATURE_STRAIGHT_KEY) + long straight_key_decoded_character = 0; + #endif + + dit_buffer = 0; + dah_buffer = 0; + + #if defined(FEATURE_BUTTONS) && !defined(FEATURE_STRAIGHT_KEY) + while ((paddle_pin_read(paddle_left) == HIGH) && (paddle_pin_read(paddle_right) == HIGH) && (!analogbuttonread(0))) { } // loop until user starts sending or hits the button + #endif + + #if defined(FEATURE_BUTTONS) && defined(FEATURE_STRAIGHT_KEY) + while ((paddle_pin_read(paddle_left) == HIGH) && (paddle_pin_read(paddle_right) == HIGH) && (!analogbuttonread(0)) && (digitalRead(pin_straight_key) == HIGH)) { } // loop until user starts sending or hits the button + #endif + + while (loop2) { + + #ifdef DEBUG_MEMORY_WRITE + debug_serial_port->println(F("program_memory: entering loop2\r")); + #endif + + cwchar = 0; + paddle_hit = 0; + loop1 = 1; + + + + while (loop1) { + check_paddles(); + if (dit_buffer) { + sending_mode = MANUAL_SENDING; + send_dit(); + dit_buffer = 0; + paddle_hit = 1; + cwchar = (cwchar * 10) + 1; + last_element_time = millis(); + #ifdef DEBUG_MEMORY_WRITE + debug_serial_port->write("."); + #endif + } + if (dah_buffer) { + sending_mode = MANUAL_SENDING; + send_dah(); + dah_buffer = 0; + paddle_hit = 1; + cwchar = (cwchar * 10) + 2; + last_element_time = millis(); + #ifdef DEBUG_MEMORY_WRITE + debug_serial_port->write("_"); + #endif + } + + #if defined(FEATURE_STRAIGHT_KEY) + straight_key_decoded_character = service_straight_key(); + if (straight_key_decoded_character != 0){ + cwchar = straight_key_decoded_character; + paddle_hit = 1; + } + #endif + + #if !defined(FEATURE_STRAIGHT_KEY) + if ((paddle_hit) && (millis() > (last_element_time + (float(600/configuration.wpm) * length_letterspace)))) { // this character is over + loop1 = 0; + } + #else + if (((paddle_hit) && (millis() > (last_element_time + (float(600/configuration.wpm) * length_letterspace)))) || (straight_key_decoded_character != 0)) { // this character is over + loop1 = 0; + } + #endif + + +// TODO - need to add something here to handle straight key leading space + #ifdef FEATURE_MEMORY_MACROS + if ((!macro_flag) && (paddle_hit == 0) && (millis() > (last_element_time + ((float(1200/configuration.wpm) * configuration.length_wordspace)))) && (space_count < program_memory_limit_consec_spaces)) { // we have a space + loop1 = 0; + cwchar = 9; + space_count++; + } + #else + if ((paddle_hit == 0) && (millis() > (last_element_time + ((float(1200/configuration.wpm) * configuration.length_wordspace)))) && (space_count < program_memory_limit_consec_spaces)) { // we have a space + loop1 = 0; + cwchar = 9; + space_count++; + } + #endif //FEATURE_MEMORY_MACROS + + #ifdef FEATURE_BUTTONS + while (analogbuttonread(0)) { // hit the button to get out of command mode if no paddle was hit + loop1 = 0; + loop2 = 0; + } + #endif + } //loop1 + + if (cwchar != 9) { + space_count = 0; + } + + // write the character to memory + if (cwchar > 0) { + + #ifdef DEBUG_MEMORY_WRITE + debug_serial_port->print(F("program_memory: write_character_to_memory")); + debug_serial_port->print(F(" mem number:")); + debug_serial_port->print(memory_number); + debug_serial_port->print(F(" memory_location_index:")); + debug_serial_port->print(memory_location_index); + debug_serial_port->print(F(" EEPROM location:")); + debug_serial_port->print(memory_start(memory_number)+memory_location_index); + debug_serial_port->print(F(" cwchar:")); + debug_serial_port->print(cwchar); + debug_serial_port->print(F(" ascii to write:")); + debug_serial_port->println(convert_cw_number_to_ascii(cwchar)); + #endif + + EEPROM.write((memory_start(memory_number)+memory_location_index),convert_cw_number_to_ascii(cwchar)); + memory_location_index++; + + #ifdef FEATURE_MEMORY_MACROS + if (!macro_flag) { + if (convert_cw_number_to_ascii(cwchar) == '\\') {macro_flag = 1;} // if we got the \ macro character, supress spaces + } else { + if (convert_cw_number_to_ascii(cwchar) == '+') { // if we're building a prosign, supress the next two spaces + macro_flag = 2; + } else { + macro_flag--; + } + } + #endif //FEATURE_MEMORY_MACROS + } + + // are we out of memory locations? + if ((memory_start(memory_number) + memory_location_index) == memory_end(memory_number)) { + loop1 = 0; + loop2 = 0; + #ifdef DEBUG_MEMORY_WRITE + debug_serial_port->println(F("program_memory: out of memory location")); + #endif + } + } + + //write terminating 255 at end + #ifdef DEBUG_MEMORY_WRITE + debug_serial_port->println(F("program_memory: writing memory termination")); + #endif + + EEPROM.write((memory_start(memory_number) + memory_location_index),255); + + #ifdef OPTION_PROG_MEM_TRIM_TRAILING_SPACES + for (int x = (memory_location_index-1); x > 0; x--) { + if (EEPROM.read((memory_start(memory_number) + x)) == 32) { + EEPROM.write((memory_start(memory_number) + x),255); + } else { + x = 0; + } + } + #endif + + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("Done", 0, default_display_msg_delay); + #endif + + play_memory(memory_number); + + +} +#endif + +//--------------------------------------------------------------------- + +#ifdef FEATURE_MEMORIES +int memory_start(byte memory_number) { + return (memory_area_start + (memory_number * ((memory_area_end - memory_area_start) / number_of_memories))); +} +#endif + +//--------------------------------------------------------------------- + +#ifdef FEATURE_MEMORIES +int memory_end(byte memory_number) { + return (memory_start(memory_number) - 1 + ((memory_area_end - memory_area_start)/number_of_memories)); +} +#endif + +//--------------------------------------------------------------------- + +void initialize_pins() { + +#if defined (ARDUINO_MAPLE_MINI)||defined(ARDUINO_GENERIC_STM32F103C) //sp5iou 20180329 + pinMode (paddle_left, INPUT_PULLUP); + pinMode (paddle_right, INPUT_PULLUP); +#else + pinMode (paddle_left, INPUT); + digitalWrite (paddle_left, HIGH); + pinMode (paddle_right, INPUT); + digitalWrite (paddle_right, HIGH); +#endif //defined (ARDUINO_MAPLE_MINI)||defined(ARDUINO_GENERIC_STM32F103C) sp5iou 20180329 + + #if defined(FEATURE_CAPACITIVE_PADDLE_PINS) + if (capactive_paddle_pin_inhibit_pin){ + pinMode (capactive_paddle_pin_inhibit_pin, INPUT); + digitalWrite (capactive_paddle_pin_inhibit_pin, LOW); + } + #endif //FEATURE_CAPACITIVE_PADDLE_PINS + + if (tx_key_line_1) { + pinMode (tx_key_line_1, OUTPUT); + digitalWrite (tx_key_line_1, tx_key_line_inactive_state); + } + if (tx_key_line_2) { + pinMode (tx_key_line_2, OUTPUT); + digitalWrite (tx_key_line_2, tx_key_line_inactive_state); + } + if (tx_key_line_3) { + pinMode (tx_key_line_3, OUTPUT); + digitalWrite (tx_key_line_3, tx_key_line_inactive_state); + } + if (tx_key_line_4) { + pinMode (tx_key_line_4, OUTPUT); + digitalWrite (tx_key_line_4, tx_key_line_inactive_state); + } + if (tx_key_line_5) { + pinMode (tx_key_line_5, OUTPUT); + digitalWrite (tx_key_line_5, tx_key_line_inactive_state); + } + if (tx_key_line_6) { + pinMode (tx_key_line_6, OUTPUT); + digitalWrite (tx_key_line_6, tx_key_line_inactive_state); + } + + + if (ptt_tx_1) { + pinMode (ptt_tx_1, OUTPUT); + digitalWrite (ptt_tx_1, ptt_line_inactive_state); + } + if (ptt_tx_2) { + pinMode (ptt_tx_2, OUTPUT); + digitalWrite (ptt_tx_2, ptt_line_inactive_state); + } + if (ptt_tx_3) { + pinMode (ptt_tx_3, OUTPUT); + digitalWrite (ptt_tx_3, ptt_line_inactive_state); + } + if (ptt_tx_4) { + pinMode (ptt_tx_4, OUTPUT); + digitalWrite (ptt_tx_4, ptt_line_inactive_state); + } + if (ptt_tx_5) { + pinMode (ptt_tx_5, OUTPUT); + digitalWrite (ptt_tx_5, ptt_line_inactive_state); + } + if (ptt_tx_6) { + pinMode (ptt_tx_6, OUTPUT); + digitalWrite (ptt_tx_6, ptt_line_inactive_state); + } + if (sidetone_line) { + pinMode (sidetone_line, OUTPUT); + digitalWrite (sidetone_line, sidetone_line_inactive_state); + } + if (tx_key_dit) { + pinMode (tx_key_dit, OUTPUT); + digitalWrite (tx_key_dit, tx_key_dit_and_dah_pins_inactive_state); + } + if (tx_key_dah) { + pinMode (tx_key_dah, OUTPUT); + digitalWrite (tx_key_dah, tx_key_dit_and_dah_pins_inactive_state); + } + + #ifdef FEATURE_CW_DECODER + pinMode (cw_decoder_pin, INPUT_PULLUP); + // pinMode (cw_decoder_pin, INPUT); + // digitalWrite (cw_decoder_pin, HIGH); + + #if defined(OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR) + digitalWrite (cw_decoder_audio_input_pin, LOW); + cwtonedetector.init(cw_decoder_audio_input_pin); + #endif //OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR + + if (cw_decoder_indicator){ + pinMode(cw_decoder_indicator,OUTPUT); + digitalWrite(cw_decoder_indicator, LOW); + } + #endif //FEATURE_CW_DECODER + + #if defined(FEATURE_COMMAND_MODE) && defined(command_mode_active_led) + if(command_mode_active_led) { + pinMode (command_mode_active_led, OUTPUT); + digitalWrite (command_mode_active_led,LOW); + } + #endif //FEATURE_COMMAND_MODE && command_mode_active_led + + + #ifdef FEATURE_LED_RING + pinMode(led_ring_sdi,OUTPUT); + pinMode(led_ring_clk,OUTPUT); + pinMode(led_ring_le,OUTPUT); + #endif //FEATURE_LED_RING + + #ifdef FEATURE_ALPHABET_SEND_PRACTICE + if (correct_answer_led) { + pinMode(correct_answer_led, OUTPUT); + digitalWrite(correct_answer_led, LOW); + } + if (wrong_answer_led) { + pinMode(wrong_answer_led, OUTPUT); + digitalWrite(wrong_answer_led, LOW); + } + #endif //FEATURE_ALPHABET_SEND_PRACTICE + + #ifdef FEATURE_PTT_INTERLOCK + pinMode(ptt_interlock,INPUT_PULLUP); + // pinMode(ptt_interlock,INPUT); + // if (ptt_interlock_active_state == HIGH){ + // digitalWrite(ptt_interlock,LOW); + // } else { + // digitalWrite(ptt_interlock,HIGH); + // } + #endif //FEATURE_PTT_INTERLOCK + + #ifdef FEATURE_STRAIGHT_KEY + pinMode(pin_straight_key,INPUT); + if (STRAIGHT_KEY_ACTIVE_STATE == HIGH){ + digitalWrite (pin_straight_key, LOW); + } else { + digitalWrite (pin_straight_key, HIGH); + } + #endif //FEATURE_STRAIGHT_KEY + + #if defined(FEATURE_COMPETITION_COMPRESSION_DETECTION) + pinMode(compression_detection_pin,OUTPUT); + digitalWrite(compression_detection_pin,LOW); + #endif //FEATURE_COMPETITION_COMPRESSION_DETECTION + + #if defined(FEATURE_SLEEP) + if (keyer_awake){ + pinMode(keyer_awake,OUTPUT); + digitalWrite(keyer_awake,KEYER_AWAKE_PIN_AWAKE_STATE); + } + #endif //FEATURE_SLEEP + + #if defined(FEATURE_LCD_BACKLIGHT_AUTO_DIM) + if (keyer_power_led){ + pinMode(keyer_power_led,OUTPUT); + analogWrite(keyer_power_led,keyer_power_led_awake_duty); + } + #endif //FEATURE_LCD_BACKLIGHT_AUTO_DIM + + #ifdef FEATURE_SIDETONE_SWITCH + pinMode(SIDETONE_SWITCH,INPUT_PULLUP); + #endif //FEATURE_SIDETONE_SWITCH + + #if defined (FEATURE_4x4_KEYPAD)||defined(FEATURE_3x4_KEYPAD) + pinMode(Row3,INPUT_PULLUP); + pinMode(Row2,INPUT_PULLUP); + pinMode(Row1,INPUT_PULLUP); + pinMode(Row0,INPUT_PULLUP); + pinMode(Col2,INPUT_PULLUP); + pinMode(Col1,INPUT_PULLUP); + pinMode(Col0,INPUT_PULLUP); + #endif + + #if defined (FEATURE_4x4_KEYPAD) + pinMode(Col3,INPUT_PULLUP); //Col3 not used if 3x4 keypad is defined. + #endif + + #ifdef FEATURE_SEQUENCER + if (sequencer_1_pin){ + pinMode(sequencer_1_pin,OUTPUT); + digitalWrite(sequencer_1_pin,sequencer_pins_inactive_state); + } + if (sequencer_2_pin){ + pinMode(sequencer_2_pin,OUTPUT); + digitalWrite(sequencer_2_pin,sequencer_pins_inactive_state); + } + if (sequencer_3_pin){ + pinMode(sequencer_3_pin,OUTPUT); + digitalWrite(sequencer_3_pin,sequencer_pins_inactive_state); + } + if (sequencer_4_pin){ + pinMode(sequencer_4_pin,OUTPUT); + digitalWrite(sequencer_4_pin,sequencer_pins_inactive_state); + } + if (sequencer_5_pin){ + pinMode(sequencer_5_pin,OUTPUT); + digitalWrite(sequencer_5_pin,sequencer_pins_inactive_state); + } + #endif //FEATURE_SEQUENCER + + #ifdef FEATURE_SO2R_BASE + if (so2r_tx_1) { + pinMode(so2r_tx_1, OUTPUT); + } + + if (so2r_tx_2) { + pinMode(so2r_tx_2, OUTPUT); + } + + if (so2r_rx_1) { + pinMode(so2r_rx_1, OUTPUT); + } + + if (so2r_rx_1s) { + pinMode(so2r_rx_1s, OUTPUT); + } + + if (so2r_rx_2) { + pinMode(so2r_rx_2, OUTPUT); + } + + if (so2r_rx_2s) { + pinMode(so2r_rx_2s, OUTPUT); + } + + if (so2r_rx_s) { + pinMode(so2r_rx_s, OUTPUT); + } + + so2r_set_tx(); + so2r_set_rx(); + + #ifdef FEATURE_SO2R_SWITCHES + if (so2r_tx_switch) { + pinMode(so2r_tx_switch, INPUT_PULLUP); + } + + if (so2r_rx1_switch) { + pinMode(so2r_rx1_switch, INPUT_PULLUP); + } + + if (so2r_rx2_switch) { + pinMode(so2r_rx2_switch, INPUT_PULLUP); + } + #endif // FEATURE_SO2R_SWITCHES + #endif // FEATURE_SO2R_BASE + + if (ptt_input_pin){ + pinMode(ptt_input_pin,INPUT_PULLUP); + } + + if (tx_inhibit_pin){ + pinMode(tx_inhibit_pin,INPUT_PULLUP); + } + + if (tx_pause_pin){ + pinMode(tx_pause_pin,INPUT_PULLUP); + } + + if (potentiometer_enable_pin){ + pinMode(potentiometer_enable_pin,INPUT_PULLUP); + } + +} //initialize_pins() + +//--------------------------------------------------------------------- + +void initialize_debug_startup(){ +#ifdef DEBUG_STARTUP + + serial_status(debug_serial_port); + #if defined(FEATURE_SERIAL) + debug_serial_port->println(F("FEATURE_SERIAL")); + #endif + #ifdef FEATURE_COMMAND_LINE_INTERFACE + debug_serial_port->println(F("FEATURE_COMMAND_LINE_INTERFACE")); + #endif + #ifndef OPTION_DO_NOT_SAY_HI + debug_serial_port->println(F("OPTION_DO_NOT_SAY_HI")); + #endif + #ifdef FEATURE_MEMORIES + debug_serial_port->println(F("FEATURE_MEMORIES")); + #endif + #ifdef FEATURE_MEMORY_MACROS + debug_serial_port->println(F("FEATURE_MEMORY_MACROS")); + #endif + #ifdef FEATURE_WINKEY_EMULATION + debug_serial_port->println(F("FEATURE_WINKEY_EMULATION")); + #endif + #ifdef OPTION_WINKEY_2_SUPPORT + debug_serial_port->println(F("OPTION_WINKEY_2_SUPPORT")); + #endif + #ifdef FEATURE_BEACON + debug_serial_port->println(F("FEATURE_BEACON")); + #endif + #ifdef FEATURE_TRAINING_COMMAND_LINE_INTERFACE + debug_serial_port->println(F("FEATURE_TRAINING_COMMAND_LINE_INTERFACE")); + #endif + #ifdef FEATURE_POTENTIOMETER + debug_serial_port->println(F("FEATURE_POTENTIOMETER")); + #endif + #if defined(FEATURE_SERIAL_HELP) + debug_serial_port->println(F("FEATURE_SERIAL_HELP")); + #endif + #ifdef FEATURE_HELL + debug_serial_port->println(F("FEATURE_HELL")); + #endif + #ifdef FEATURE_AMERICAN_MORSE + debug_serial_port->println(F("FEATURE_AMERICAN_MORSE")); + #endif + #ifdef FEATURE_PS2_KEYBOARD + debug_serial_port->println(F("FEATURE_PS2_KEYBOARD")); + #endif + #ifdef FEATURE_DEAD_OP_WATCHDOG + debug_serial_port->println(F("FEATURE_DEAD_OP_WATCHDOG")); + #endif + #ifdef FEATURE_AUTOSPACE + debug_serial_port->println(F("FEATURE_AUTOSPACE")); + #endif + #ifdef FEATURE_FARNSWORTH + debug_serial_port->println(F("FEATURE_FARNSWORTH")); + #endif + #ifdef FEATURE_DL2SBA_BANKSWITCH + debug_serial_port->println(F("FEATURE_DL2SBA_BANKSWITCH")); + #endif + #ifdef FEATURE_BUTTONS + debug_serial_port->println(F("FEATURE_BUTTONS")); + #endif + #ifdef FEATURE_COMMAND_MODE + debug_serial_port->println(F("FEATURE_COMMAND_MODE")); + #endif + #ifdef FEATURE_LCD_4BIT + debug_serial_port->println(F("FEATURE_LCD_4BIT")); + #endif + #ifdef FEATURE_LCD_8BIT + debug_serial_port->println(F("FEATURE_LCD_8BIT")); + #endif + debug_serial_port->println(F("setup: exiting, going into loop")); +#endif //DEBUG_STARTUP +} + + +//--------------------------------------------------------------------- + + + +#ifdef FEATURE_CW_DECODER +void service_cw_decoder() { + + static unsigned long last_transition_time = 0; + static unsigned long last_decode_time = 0; + static byte last_state = HIGH; + static int decode_elements[16]; // this stores received element lengths in mS (positive = tone, minus = no tone) + static byte decode_element_pointer = 0; + static float decode_element_tone_average = 0; + static float decode_element_no_tone_average = 0; + static int no_tone_count = 0; + static int tone_count = 0; + byte decode_it_flag = 0; + byte cd_decoder_pin_state = HIGH; + + int element_duration = 0; + static float decoder_wpm = configuration.wpm; + long decode_character = 0; + static byte space_sent = 0; + #ifdef FEATURE_COMMAND_LINE_INTERFACE + static byte screen_column = 0; + static int last_printed_decoder_wpm = 0; + #endif + + cd_decoder_pin_state = digitalRead(cw_decoder_pin); + + #if defined(OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR) + if (cwtonedetector.detecttone() == HIGH){ // invert states + cd_decoder_pin_state = LOW; + } else { + cd_decoder_pin_state = HIGH; + } + #endif + + #if defined(DEBUG_CW_DECODER_WITH_TONE) + if (cd_decoder_pin_state == LOW){ + #if defined(GOERTZ_TARGET_FREQ) + tone(sidetone_line, GOERTZ_TARGET_FREQ); + #else + tone(sidetone_line, hz_sidetone); + #endif //defined(GOERTZ_TARGET_FREQ) + } else { + noTone(sidetone_line); + } + #endif //DEBUG_CW_DECODER + + if ((cw_decoder_indicator) && (cd_decoder_pin_state == LOW)){ + digitalWrite(cw_decoder_indicator,HIGH); + } else { + digitalWrite(cw_decoder_indicator,LOW); + } + + #ifdef DEBUG_OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR + static unsigned long last_magnitude_debug_print = 0; + if ((millis() - last_magnitude_debug_print) > 250){ + //debug_serial_port->print("service_cw_decoder: cwtonedetector magnitude: "); + //debug_serial_port->print(cwtonedetector.magnitudelimit_low); + //debug_serial_port->print("\t"); + debug_serial_port->print(cwtonedetector.magnitudelimit); + debug_serial_port->print("\t"); + debug_serial_port->println(cwtonedetector.magnitude); + last_magnitude_debug_print = millis(); + } + #endif + + if (last_transition_time == 0) { + if (cd_decoder_pin_state == LOW) { // is this our first tone? + last_transition_time = millis(); + last_state = LOW; + + #ifdef FEATURE_SLEEP + last_activity_time = millis(); + #endif //FEATURE_SLEEP + #ifdef FEATURE_LCD_BACKLIGHT_AUTO_DIM + last_active_time = millis(); + #endif //FEATURE_LCD_BACKLIGHT_AUTO_DIM + + } else { + if ((last_decode_time > 0) && (!space_sent) && ((millis() - last_decode_time) > ((1200/decoder_wpm)*CW_DECODER_SPACE_PRINT_THRESH))) { // should we send a space? + #if defined(FEATURE_SERIAL) + #ifdef FEATURE_COMMAND_LINE_INTERFACE + primary_serial_port->write(32); + screen_column++; + #endif //FEATURE_COMMAND_LINE_INTERFACE + #endif //FEATURE_SERIAL + #ifdef FEATURE_DISPLAY + display_scroll_print_char(' '); + #endif //FEATURE_DISPLAY + space_sent = 1; + + } + } + } else { + if (cd_decoder_pin_state != last_state) { + // we have a transition + element_duration = millis() - last_transition_time; + if (element_duration > CW_DECODER_NOISE_FILTER) { // filter out noise + if (cd_decoder_pin_state == LOW) { // we have a tone + decode_elements[decode_element_pointer] = (-1 * element_duration); // the last element was a space, so make it negative + no_tone_count++; + if (decode_element_no_tone_average == 0) { + decode_element_no_tone_average = element_duration; + } else { + decode_element_no_tone_average = (element_duration + decode_element_no_tone_average) / 2; + } + decode_element_pointer++; + last_state = LOW; + } else { // we have no tone + decode_elements[decode_element_pointer] = element_duration; // the last element was a tone, so make it positive + tone_count++; + if (decode_element_tone_average == 0) { + decode_element_tone_average = element_duration; + } else { + decode_element_tone_average = (element_duration + decode_element_tone_average) / 2; + } + last_state = HIGH; + decode_element_pointer++; + } + last_transition_time = millis(); + if (decode_element_pointer == 16) { decode_it_flag = 1; } // if we've filled up the array, go ahead and decode it + } + + + } else { + // no transition + element_duration = millis() - last_transition_time; + if (last_state == HIGH) { + // we're still high (no tone) - have we reached character space yet? + //if ((element_duration > (decode_element_no_tone_average * 2.5)) || (element_duration > (decode_element_tone_average * 2.5))) { + if (element_duration > (float(1200/decoder_wpm)*CW_DECODER_SPACE_DECODE_THRESH)) { + decode_it_flag = 1; + } + } else { + // have we had tone for an outrageous amount of time? + } + } + } + + + + + if (decode_it_flag) { // are we ready to decode the element array? + + // adjust the decoder wpm based on what we got + + if ((no_tone_count > 0) && (tone_count > 1)){ // NEW + + if (decode_element_no_tone_average > 0) { + if (abs((1200/decode_element_no_tone_average) - decoder_wpm) < 5) { + decoder_wpm = (decoder_wpm + (1200/decode_element_no_tone_average))/2; + } else { + if (abs((1200/decode_element_no_tone_average) - decoder_wpm) < 10) { + decoder_wpm = (decoder_wpm + decoder_wpm + (1200/decode_element_no_tone_average))/3; + } else { + if (abs((1200/decode_element_no_tone_average) - decoder_wpm) < 20) { + decoder_wpm = (decoder_wpm + decoder_wpm + decoder_wpm + (1200/decode_element_no_tone_average))/4; + } + } + } + } + + + } // NEW + + #ifdef DEBUG_CW_DECODER_WPM + if (abs(decoder_wpm - last_printed_decoder_wpm) > 0.9) { + debug_serial_port->print("<"); + debug_serial_port->print(int(decoder_wpm)); + debug_serial_port->print(">"); + last_printed_decoder_wpm = decoder_wpm; + } + #endif //DEBUG_CW_DECODER_WPM + + for (byte x = 0;x < decode_element_pointer; x++) { + if (decode_elements[x] > 0) { // is this a tone element? + // we have no spaces to time from, use the current wpm + if ((decode_elements[x]/(1200/decoder_wpm)) < 2.1 /*1.3*/) { // changed from 1.3 to 2.1 2015-05-12 + decode_character = (decode_character * 10) + 1; // we have a dit + } else { + decode_character = (decode_character * 10) + 2; // we have a dah + } + } + #ifdef DEBUG_CW_DECODER + debug_serial_port->print(F("service_cw_decoder: decode_elements[")); + debug_serial_port->print(x); + debug_serial_port->print(F("]: ")); + debug_serial_port->println(decode_elements[x]); + #endif //DEBUG_CW_DECODER + } + + #ifdef DEBUG_CW_DECODER + debug_serial_port->print(F("service_cw_decoder: decode_element_tone_average: ")); + debug_serial_port->println(decode_element_tone_average); + debug_serial_port->print(F("service_cw_decoder: decode_element_no_tone_average: ")); + debug_serial_port->println(decode_element_no_tone_average); + debug_serial_port->print(F("service_cw_decoder: decode_element_no_tone_average wpm: ")); + debug_serial_port->println(1200/decode_element_no_tone_average); + debug_serial_port->print(F("service_cw_decoder: decoder_wpm: ")); + debug_serial_port->println(decoder_wpm); + debug_serial_port->print(F("service_cw_decoder: decode_character: ")); + debug_serial_port->println(decode_character); + #endif //DEBUG_CW_DECODER + + #if defined(FEATURE_SERIAL) && defined(FEATURE_COMMAND_LINE_INTERFACE) + primary_serial_port->write(convert_cw_number_to_ascii(decode_character)); + #ifdef FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + secondary_serial_port->write(convert_cw_number_to_ascii(decode_character)); + #endif + screen_column++; + #endif //defined(FEATURE_SERIAL) && defined(FEATURE_COMMAND_LINE_INTERFACE) + + #ifdef FEATURE_DISPLAY + display_scroll_print_char(convert_cw_number_to_ascii(decode_character)); + #endif //FEATURE_DISPLAY + + // reinitialize everything + last_transition_time = 0; + last_decode_time = millis(); + decode_element_pointer = 0; + decode_element_tone_average = 0; + decode_element_no_tone_average = 0; + space_sent = 0; + no_tone_count = 0; + tone_count = 0; + } //if (decode_it_flag) + + #if defined(FEATURE_SERIAL) + #ifdef FEATURE_COMMAND_LINE_INTERFACE + if (screen_column > CW_DECODER_SCREEN_COLUMNS) { + primary_serial_port->println(); + #ifdef FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + secondary_serial_port->println(); + #endif + screen_column = 0; + } + #endif //FEATURE_COMMAND_LINE_INTERFACE + #endif //FEATURE_SERIAL + +} + +#endif //FEATURE_CW_DECODER + +//--------------------------------------------------------------------- + +void initialize_keyer_state(){ + + key_state = 0; + key_tx = 1; + + configuration.wpm = initial_speed_wpm; + pot_wpm_low_value = initial_pot_wpm_low_value; + configuration.paddle_interruption_quiet_time_element_lengths = default_paddle_interruption_quiet_time_element_lengths; + configuration.hz_sidetone = initial_sidetone_freq; + configuration.memory_repeat_time = default_memory_repeat_time; + configuration.cmos_super_keyer_iambic_b_timing_percent = default_cmos_super_keyer_iambic_b_timing_percent; + configuration.dah_to_dit_ratio = initial_dah_to_dit_ratio; + configuration.length_wordspace = default_length_wordspace; + configuration.weighting = default_weighting; + configuration.wordsworth_wordspace = default_wordsworth_wordspace; + configuration.wordsworth_repetition = default_wordsworth_repetition; + configuration.wpm_farnsworth = initial_speed_wpm; + configuration.cli_mode = CLI_NORMAL_MODE; + configuration.wpm_command_mode = initial_command_mode_speed_wpm; + configuration.ptt_buffer_hold_active = 0; + configuration.sidetone_volume = sidetone_volume_low_limit + ((sidetone_volume_high_limit - sidetone_volume_low_limit) / 2); + configuration.ptt_disabled = 0; + configuration.beacon_mode_on_boot_up = 0; + configuration.cw_echo_timing_factor = 100 * default_cw_echo_timing_factor; + configuration.autospace_timing_factor = 100 * default_autospace_timing_factor; + configuration.keying_compensation = default_keying_compensation; + + configuration.ptt_lead_time[0] = initial_ptt_lead_time_tx1; + configuration.ptt_tail_time[0] = initial_ptt_tail_time_tx1; + configuration.ptt_lead_time[1] = initial_ptt_lead_time_tx2; + configuration.ptt_tail_time[1] = initial_ptt_tail_time_tx2; + #if !defined(OPTION_SAVE_MEMORY_NANOKEYER) + configuration.ptt_lead_time[2] = initial_ptt_lead_time_tx3; + configuration.ptt_tail_time[2] = initial_ptt_tail_time_tx3; + configuration.ptt_lead_time[3] = initial_ptt_lead_time_tx4; + configuration.ptt_tail_time[3] = initial_ptt_tail_time_tx4; + configuration.ptt_lead_time[4] = initial_ptt_lead_time_tx5; + configuration.ptt_tail_time[4] = initial_ptt_tail_time_tx5; + configuration.ptt_lead_time[5] = initial_ptt_lead_time_tx6; + configuration.ptt_tail_time[5] = initial_ptt_tail_time_tx6; + + for (int x = 0; x < 5; x++){ + configuration.ptt_active_to_sequencer_active_time[x] = 0; + configuration.ptt_inactive_to_sequencer_inactive_time[x] = 0; + } + #endif //OPTION_SAVE_MEMORY_NANOKEYER + + #ifndef FEATURE_SO2R_BASE + switch_to_tx_silent(1); + #endif + + #if (!defined(ARDUINO_SAM_DUE) || (defined(ARDUINO_SAM_DUE) && defined(FEATURE_EEPROM_E24C1024))) && !defined(HARDWARE_GENERIC_STM32F103C) + memory_area_end = EEPROM.length() - 1; + #else + #if defined(HARDWARE_GENERIC_STM32F103C) + memory_area_end = 254; + #else + memory_area_end = 1024; // not sure if this is a valid assumption + #endif + #endif + +} + +//--------------------------------------------------------------------- +void initialize_potentiometer(){ + + #ifdef FEATURE_POTENTIOMETER + pinMode(potentiometer,INPUT); + pot_wpm_high_value = initial_pot_wpm_high_value; + last_pot_wpm_read = pot_value_wpm(); + configuration.pot_activated = 1; + #endif + +} + +//--------------------------------------------------------------------- +void initialize_rotary_encoder(){ + + #ifdef FEATURE_ROTARY_ENCODER + #ifdef OPTION_ENCODER_ENABLE_PULLUPS + #if defined (ARDUINO_MAPLE_MINI)||defined(ARDUINO_GENERIC_STM32F103C) //sp5iou 20180329 + pinMode(rotary_pin1, INPUT_PULLUP);//sp5iou 20180329 + pinMode(rotary_pin2, INPUT_PULLUP);//sp5iou 20180329 + #else // (ARDUINO_MAPLE_MINI)||defined(ARDUINO_GENERIC_STM32F103C) //sp5iou 20180329 + pinMode(rotary_pin1, INPUT); + pinMode(rotary_pin2, INPUT); + digitalWrite(rotary_pin1, HIGH); + digitalWrite(rotary_pin2, HIGH); + #endif + #endif //OPTION_ENCODER_ENABLE_PULLUPS + #endif //FEATURE_ROTARY_ENCODER + +} + +//--------------------------------------------------------------------- + +void initialize_default_modes(){ + + + // setup default modes + keyer_machine_mode = KEYER_NORMAL; + configuration.paddle_mode = PADDLE_NORMAL; + configuration.keyer_mode = IAMBIC_B; + configuration.sidetone_mode = SIDETONE_ON; + + #ifdef initial_sidetone_mode + configuration.sidetone_mode = initial_sidetone_mode; + #endif + + char_send_mode = CW; + + #if defined(FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING) && defined(OPTION_CMOS_SUPER_KEYER_IAMBIC_B_TIMING_ON_BY_DEFAULT) // DL1HTB initialize CMOS Super Keyer if feature is enabled + configuration.cmos_super_keyer_iambic_b_timing_on = 1; + #endif //FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING // #end DL1HTB initialize CMOS Super Keyer if feature is enabled + + delay(250); // wait a little bit for the caps to charge up on the paddle lines + +} + +//--------------------------------------------------------------------- + +void initialize_watchdog(){ + + #ifdef OPTION_WATCHDOG_TIMER + wdt_enable(WDTO_4S); + #endif //OPTION_WATCHDOG_TIMER + +} + +//--------------------------------------------------------------------- + +void check_eeprom_for_initialization(){ + + // do an eeprom reset to defaults if paddles are squeezed + if (paddle_pin_read(paddle_left) == LOW && paddle_pin_read(paddle_right) == LOW) { + while (paddle_pin_read(paddle_left) == LOW && paddle_pin_read(paddle_right) == LOW) {} + initialize_eeprom(); + } + + // read settings from eeprom and initialize eeprom if it has never been written to + if (read_settings_from_eeprom()) { + #if defined(HARDWARE_GENERIC_STM32F103C) + EEPROM.init(); //sp5iou 20180328 to reinitialize / initialize EEPROM + EEPROM.format();//sp5iou 20180328 to reinitialize / format EEPROM + #endif + initialize_eeprom(); + } +} + +//--------------------------------------------------------------------- + +void initialize_eeprom(){ + + write_settings_to_eeprom(1); + // #if defined(FEATURE_SINEWAVE_SIDETONE) + // initialize_tonsin(); + // #endif + beep_boop(); + beep_boop(); + beep_boop(); + +} + +//--------------------------------------------------------------------- + +void check_for_beacon_mode(){ + + #ifndef OPTION_SAVE_MEMORY_NANOKEYER + // check for beacon mode (paddle_left == low) or straight key mode (paddle_right == low) + if (paddle_pin_read(paddle_left) == LOW) { + #ifdef FEATURE_BEACON + keyer_machine_mode = BEACON; + #endif + } else { + if (paddle_pin_read(paddle_right) == LOW) { + configuration.keyer_mode = STRAIGHT; + } + } + #endif //OPTION_SAVE_MEMORY_NANOKEYER + +} + +//--------------------------------------------------------------------- + +void check_for_debug_modes(){ + + #ifdef DEBUG_CAPTURE_COM_PORT + primary_serial_port->begin(primary_serial_port_baud_rate); + debug_capture(); + #endif + + #ifdef DEBUG_HELL_TEST + hell_test(); + #endif +} + +//--------------------------------------------------------------------- + +void initialize_serial_ports(){ + + // initialize serial port + #if defined(FEATURE_SERIAL) + + #if defined(FEATURE_WINKEY_EMULATION) && defined(FEATURE_COMMAND_LINE_INTERFACE) //-------------------------------------------- + + #ifdef FEATURE_BUTTONS + if (analogbuttonread(0)) { + #ifdef OPTION_PRIMARY_SERIAL_PORT_DEFAULT_WINKEY_EMULATION + primary_serial_port_mode = SERIAL_CLI; + primary_serial_port_baud_rate = PRIMARY_SERIAL_PORT_BAUD; + #else + primary_serial_port_mode = SERIAL_WINKEY_EMULATION; + primary_serial_port_baud_rate = WINKEY_DEFAULT_BAUD; + #endif //ifndef OPTION_PRIMARY_SERIAL_PORT_DEFAULT_WINKEY_EMULATION + } else { + #ifdef OPTION_PRIMARY_SERIAL_PORT_DEFAULT_WINKEY_EMULATION + primary_serial_port_mode = SERIAL_WINKEY_EMULATION; + primary_serial_port_baud_rate = WINKEY_DEFAULT_BAUD; + #else + primary_serial_port_mode = SERIAL_CLI; + primary_serial_port_baud_rate = PRIMARY_SERIAL_PORT_BAUD; + #endif //ifndef OPTION_PRIMARY_SERIAL_PORT_DEFAULT_WINKEY_EMULATION + } + while (analogbuttonread(0)) {} + #else //FEATURE_BUTTONS + #ifdef OPTION_PRIMARY_SERIAL_PORT_DEFAULT_WINKEY_EMULATION + primary_serial_port_mode = SERIAL_WINKEY_EMULATION; + primary_serial_port_baud_rate = WINKEY_DEFAULT_BAUD; + #else + primary_serial_port_mode = SERIAL_CLI; + primary_serial_port_baud_rate = PRIMARY_SERIAL_PORT_BAUD; + #endif //ifndef OPTION_PRIMARY_SERIAL_PORT_DEFAULT_WINKEY_EMULATION + #endif //FEATURE_BUTTONS + #endif //defined(FEATURE_WINKEY_EMULATION) && defined(FEATURE_COMMAND_LINE_INTERFACE)--------------------------------- + + #if !defined(FEATURE_WINKEY_EMULATION) && defined(FEATURE_COMMAND_LINE_INTERFACE) + primary_serial_port_mode = SERIAL_CLI; + primary_serial_port_baud_rate = PRIMARY_SERIAL_PORT_BAUD; + #endif //!defined(FEATURE_WINKEY_EMULATION) && defined(FEATURE_COMMAND_LINE_INTERFACE) + + #if defined(FEATURE_WINKEY_EMULATION) && !defined(FEATURE_COMMAND_LINE_INTERFACE) + primary_serial_port_mode = SERIAL_WINKEY_EMULATION; + primary_serial_port_baud_rate = WINKEY_DEFAULT_BAUD; + #endif //defined(FEATURE_WINKEY_EMULATION) && !defined(FEATURE_COMMAND_LINE_INTERFACE) + + primary_serial_port = PRIMARY_SERIAL_PORT; + + primary_serial_port->begin(primary_serial_port_baud_rate); + + #ifdef DEBUG_STARTUP + debug_serial_port->println(F("setup: serial port opened")); + #endif //DEBUG_STARTUP + + #if !defined(OPTION_SUPPRESS_SERIAL_BOOT_MSG) && defined(FEATURE_COMMAND_LINE_INTERFACE) + if (primary_serial_port_mode == SERIAL_CLI) { + primary_serial_port->print(F("\n\rK3NG Keyer Version ")); + primary_serial_port->write(CODE_VERSION); + primary_serial_port->println(); + #if defined(FEATURE_SERIAL_HELP) + primary_serial_port->println(F("\n\rEnter \\? for help\n")); + #endif + while(primary_serial_port->available()){primary_serial_port->read();} //clear out buffer at boot + } + #ifdef DEBUG_MEMORYCHECK + memorycheck(); + #endif //DEBUG_MEMORYCHECK + #endif //!defined(OPTION_SUPPRESS_SERIAL_BOOT_MSG) && defined(FEATURE_COMMAND_LINE_INTERFACE) + + #ifdef DEBUG_AUX_SERIAL_PORT + debug_port = DEBUG_AUX_SERIAL_PORT; + debug_serial_port->begin(DEBUG_AUX_SERIAL_PORT_BAUD); + debug_serial_port->print("debug port open "); + debug_serial_port->println(CODE_VERSION); + #endif //DEBUG_AUX_SERIAL_PORT + + #ifdef FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + secondary_serial_port = SECONDARY_SERIAL_PORT; + secondary_serial_port->begin(SECONDARY_SERIAL_PORT_BAUD); + #if !defined(OPTION_SUPPRESS_SERIAL_BOOT_MSG) + secondary_serial_port->print(F("\n\rK3NG Keyer Version ")); + secondary_serial_port->write(CODE_VERSION); + secondary_serial_port->println(); + #if defined(FEATURE_SERIAL_HELP) + secondary_serial_port->println(F("\n\rEnter \\? for help\n")); + #endif + while(secondary_serial_port->available()){secondary_serial_port->read();} //clear out buffer at boot + #endif + #endif //FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + + #ifdef FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + debug_serial_port = secondary_serial_port; + #else + debug_serial_port = primary_serial_port; + #endif + + #endif //FEATURE_SERIAL + + +} + +//--------------------------------------------------------------------- +void initialize_ps2_keyboard(){ + + #ifdef FEATURE_PS2_KEYBOARD + + #ifdef OPTION_PS2_KEYBOARD_RESET // code contributed by Bill, W9BEL + attachInterrupt(1, ps2int_write, FALLING); + digitalWrite(ps2_keyboard_data, LOW); // pullup off + pinMode(ps2_keyboard_data, OUTPUT); // pull clock low + delay(200); + #endif //OPTION_PS2_KEYBOARD_RESET + + + keyboard.begin(ps2_keyboard_data, ps2_keyboard_clock); + #endif //FEATURE_PS2_KEYBOARD + +} +//--------------------------------------------------------------------- + +#if defined(FEATURE_PS2_KEYBOARD) && defined(OPTION_PS2_KEYBOARD_RESET) +void ps2int_write() { + + // code contributed by Bill, W9BEL + //----- Called from initialize_ps2_keyboard to reset Mini KBD --------- + // The ISR for the external interrupt in read mode + + + uint8_t buffer[45]; + uint8_t head, tail, writeByte = 255; + uint8_t curbit = 0, parity = 0, ack =0; + + if(curbit < 8) { + if(writeByte & 1) { + parity ^= 1; + digitalWrite(ps2_keyboard_data, HIGH); + } else + digitalWrite(ps2_keyboard_data, LOW); + writeByte >>= 1; + } else if(curbit == 8) { // parity + if(parity) + digitalWrite(ps2_keyboard_data, LOW); + else + digitalWrite(ps2_keyboard_data, HIGH); + } else if(curbit == 9) { // time to let go + pinMode(ps2_keyboard_data, INPUT); // release line + digitalWrite(ps2_keyboard_data, HIGH); // pullup on + } else { // time to check device ACK and hold clock again + //holdClock(); + digitalWrite(ps2_keyboard_clock, LOW); // pullup off + pinMode(ps2_keyboard_clock, OUTPUT); // pull clock low + ack = !digitalRead(ps2_keyboard_data); + } + curbit++; +} +#endif + +//--------------------------------------------------------------------- + +void initialize_display(){ + + #ifdef FEATURE_DISPLAY + #if defined(FEATURE_LCD_SAINSMART_I2C) || defined(FEATURE_LCD_I2C_FDEBRABANDER) + lcd.begin(); + lcd.home(); + #else + lcd.begin(LCD_COLUMNS, LCD_ROWS); + #endif + #ifdef FEATURE_LCD_ADAFRUIT_I2C + lcd.setBacklight(lcdcolor); + #endif //FEATURE_LCD_ADAFRUIT_I2C + + #ifdef FEATURE_LCD_ADAFRUIT_BACKPACK + lcd.setBacklight(HIGH); + #endif + #ifdef FEATURE_LCD_MATHERTEL_PCF8574 + lcd.setBacklight(HIGH); + #endif + + #ifdef OPTION_DISPLAY_NON_ENGLISH_EXTENSIONS // OZ1JHM provided code, cleaned up by LA3ZA + // Store bit maps, designed using editor at http://omerk.github.io/lcdchargen/ + + + byte U_umlaut[8] = {B01010,B00000,B10001,B10001,B10001,B10001,B01110,B00000}; // 'Ãœ' + byte O_umlaut[8] = {B01010,B00000,B01110,B10001,B10001,B10001,B01110,B00000}; // 'Ö' + byte A_umlaut[8] = {B01010,B00000,B01110,B10001,B11111,B10001,B10001,B00000}; // 'Ä' + byte AE_capital[8] = {B01111,B10100,B10100,B11110,B10100,B10100,B10111,B00000}; // 'Æ' + byte OE_capital[8] = {B00001,B01110,B10011,B10101,B11001,B01110,B10000,B00000}; // 'Ø' + byte empty[8] = {B00000,B00000,B00000,B00000,B00000,B00000,B00000,B00000}; // empty + byte AA_capital[8] = {B00100,B00000,B01110,B10001,B11111,B10001,B10001,B00000}; // 'Ã…' + byte Ntilde[8] = {B01101,B10010,B00000,B11001,B10101,B10011,B10001,B00000}; // 'Ñ' + + + + // upload 8 charaters to the lcd + lcd.createChar(0, U_umlaut); // German + lcd.createChar(1, O_umlaut); // German, Swedish + lcd.createChar(2, A_umlaut); // German, Swedish + lcd.createChar(3, AE_capital); // Danish, Norwegian + lcd.createChar(4, OE_capital); // Danish, Norwegian + lcd.createChar(5, empty); // For some reason this one needs to display nothing - otherwise it will display in pauses on serial interface + lcd.createChar(6, AA_capital); // Danish, Norwegian, Swedish + lcd.createChar(7, Ntilde); // Spanish + lcd.clear(); // you have to ;o) + #endif //OPTION_DISPLAY_NON_ENGLISH_EXTENSIONS + + if (LCD_COLUMNS < 9) { + lcd_center_print_timed("K3NGKeyr", 0, 4000); + } else { + lcd_center_print_timed("K3NG Keyer", 0, 4000); + #ifdef OPTION_PERSONALIZED_STARTUP_SCREEN + if (LCD_ROWS == 2) { + lcd_center_print_timed(custom_startup_field, 1, 4000); // display the custom field on the second line of the display, maximum field length is the number of columns + } else if (LCD_ROWS > 2) { + lcd_center_print_timed("hi", 1, 4000); // display 'hi' on the 2nd line anyway + lcd_center_print_timed(custom_startup_field, 2, 4000); // display the custom field on the third line of the display, maximum field length is the number of columns + } + #else + lcd_center_print_timed("hi", 1, 4000); + #endif // OPTION_PERSONALIZED_STARTUP_SCREEN + if (LCD_ROWS > 3) lcd_center_print_timed("V: " + String(CODE_VERSION), 3, 4000); // display the code version on the fourth line of the display + } + #endif //FEATURE_DISPLAY + + if (keyer_machine_mode != BEACON) { + #ifndef OPTION_DO_NOT_SAY_HI + // sound out HI + // store current setting (compliments of DL2SBA - http://dl2sba.com/ ) + byte oldKey = key_tx; + byte oldSideTone = configuration.sidetone_mode; + key_tx = 0; + configuration.sidetone_mode = SIDETONE_ON; + send_char('H',KEYER_NORMAL); + send_char('I',KEYER_NORMAL); + configuration.sidetone_mode = oldSideTone; + key_tx = oldKey; + #endif //OPTION_DO_NOT_SAY_HI + #ifdef OPTION_BLINK_HI_ON_PTT + blink_ptt_dits_and_dahs(".... .."); + #endif + } +} + +//------------------------------------------------------------------------------------------------------- +#if defined(OPTION_BLINK_HI_ON_PTT) || (defined(OPTION_WINKEY_BLINK_PTT_ON_HOST_OPEN) && defined(FEATURE_WINKEY_EMULATION)) +void blink_ptt_dits_and_dahs(char const * cw_to_send){ + + + sending_mode = AUTOMATIC_SENDING; + + for (int x = 0;x < strlen(cw_to_send);x++){ + switch(cw_to_send[x]){ + case '.': + ptt_key(); + delay(100); + ptt_unkey(); + delay(100); + break; + case '-': + ptt_key(); + delay(300); + ptt_unkey(); + delay(100); + break; + case ' ': + delay(400); + break; + } + + + #ifdef OPTION_WATCHDOG_TIMER + wdt_reset(); + #endif //OPTION_WATCHDOG_TIMER + + } + +} +#endif //defined(OPTION_BLINK_HI_ON_PTT) || (defined(OPTION_WINKEY_BLINK_PTT_ON_HOST_OPEN) && defined(FEATURE_WINKEY_EMULATION)) + +//--------------------------------------------------------------------- +#ifdef FEATURE_USB_KEYBOARD +void KbdRptParser::OnKeyDown(uint8_t mod, uint8_t key) +{ + + #ifdef FEATURE_MEMORIES + enum usb_kbd_states {USB_KEYBOARD_NORMAL, USB_KEYBOARD_WPM_ADJUST, USB_KEYBOARD_FARNS_WPM_ADJUST, USB_KEYBOARD_SN_ENTRY, USB_KEYBOARD_PROGRAM_MEM}; + #else + enum usb_kbd_states {USB_KEYBOARD_NORMAL, USB_KEYBOARD_WPM_ADJUST, USB_KEYBOARD_FARNS_WPM_ADJUST, USB_KEYBOARD_SN_ENTRY}; + #endif + + #define USB_KEYBOARD_SPECIAL_MODE_TIMEOUT 5000 + + static byte usb_keyboard_mode = USB_KEYBOARD_NORMAL; + + static byte user_num_input_places = 0; + static int user_num_input_lower_limit = 0; + static int user_num_input_upper_limit = 0; + static byte user_input_index = 0; + static byte user_input_array[255]; + static int user_num_input_number_entered = 0; + byte user_input_process_it = 0; + #ifdef FEATURE_MEMORIES + static byte usb_keyboard_program_memory = 0; + #endif //FEATURE_MEMORIES + int x = 0; + + #ifdef FEATURE_DISPLAY + String lcd_string; + #endif + + MODIFIERKEYS modifier; + *((uint8_t*)&modifier) = mod; + + #ifdef DEBUG_USB_KEYBOARD + debug_serial_port->print(F("KbdRptParser::OnKeyDown: mod:")); + debug_serial_port->print(mod); + debug_serial_port->print(" key:"); + debug_serial_port->print(key); + debug_serial_port->print("\t"); + debug_serial_port->print((modifier.bmLeftCtrl == 1) ? "LeftCtrl" : " "); + debug_serial_port->print((modifier.bmLeftShift == 1) ? "LeftShift" : " "); + debug_serial_port->print((modifier.bmLeftAlt == 1) ? "LeftAlt" : " "); + debug_serial_port->print((modifier.bmLeftGUI == 1) ? "LeftGUI" : " "); + debug_serial_port->print((modifier.bmRightCtrl == 1) ? "RightCtrl" : " "); + debug_serial_port->print((modifier.bmRightShift == 1) ? "RightShift" : " "); + debug_serial_port->print((modifier.bmRightAlt == 1) ? "RightAlt" : " "); + debug_serial_port->print((modifier.bmRightGUI == 1) ? "RightGUI" : " "); + debug_serial_port->print("\t"); + PrintHex(key, 0x80); + debug_serial_port->println(); + #endif //DEBUG_USB_KEYBOARD + + byte usb_keyboard_prosign_flag = 0; + uint8_t keystroke = OemToAscii(mod, key); + byte keyboard_tune_on = 0; + + #ifdef FEATURE_MEMORIES + if (usb_keyboard_mode == USB_KEYBOARD_PROGRAM_MEM){ + + if ((key == 0x2a) && (user_input_index)){ // BACKSPACE + user_input_index--; + #ifdef FEATURE_DISPLAY + keyboard_string = keyboard_string.substring(0,keyboard_string.length()-1); + lcd_center_print_timed(keyboard_string, 1, 0 /*default_display_msg_delay)*/); + #endif + usb_keyboard_special_mode_start_time = millis(); + return; + } + if ((key == 0x28) || (key == 0x58)) {user_input_process_it = 1;} // ENTER + if (key == 0x29) { // ESCAPE + #ifdef FEATURE_DISPLAY + lcd_status = LCD_REVERT; + #else + boop(); + #endif + user_input_index = 0; + usb_keyboard_mode = USB_KEYBOARD_NORMAL; + return; + } + if ((keystroke > 31) && (keystroke < 123)) { + usb_keyboard_special_mode_start_time = millis(); + keystroke = uppercase(keystroke); + #ifdef FEATURE_DISPLAY + keyboard_string.concat(char(keystroke)); + if (keyboard_string.length() > LCD_COLUMNS) { + lcd_center_print_timed(keyboard_string.substring((keyboard_string.length()-LCD_COLUMNS)), 1, default_display_msg_delay); + } else { + lcd_center_print_timed(keyboard_string, 1, default_display_msg_delay); + } + #endif + user_input_array[user_input_index] = keystroke; + user_input_index++; + if (user_input_index > (memory_end(usb_keyboard_program_memory)-memory_start(usb_keyboard_program_memory))) { + user_input_process_it = 1; + } + #ifdef DEBUG_USB_KEYBOARD + debug_serial_port->print(F("KbdRptParser::OnKeyDown: user_input_index: ")); + debug_serial_port->println(user_input_index); + #endif //DEBUG_USB_KEYBOARD + } // if ((keystroke > 31) && (keystroke < 123)) + if (user_input_process_it){ + #ifdef DEBUG_USB_KEYBOARD + debug_serial_port->println(F("KbdRptParser::OnKeyDown: user_input_process_it")); + #endif //DEBUG_USB_KEYBOARD + for (x = 0;x < user_input_index;x++) { // write to memory + EEPROM.write((memory_start(usb_keyboard_program_memory)+x),user_input_array[x]); + if ((memory_start(usb_keyboard_program_memory) + x) == memory_end(usb_keyboard_program_memory)) { // are we at last memory location? + x = user_input_index; + } + } + // write terminating 255 + EEPROM.write((memory_start(usb_keyboard_program_memory)+x),255); + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("Done", 0, default_display_msg_delay); + #else + beep(); + #endif + user_input_process_it = 0; + user_input_index = 0; + usb_keyboard_mode = USB_KEYBOARD_NORMAL; + } //if (user_input_process_it) + return; + } // if (usb_keyboard_mode == USB_KEYBOARD_PROGRAM_MEM) + #endif //FEATURE_MEMORIES + + if ((usb_keyboard_mode == USB_KEYBOARD_WPM_ADJUST) || (usb_keyboard_mode == USB_KEYBOARD_WPM_ADJUST) || (usb_keyboard_mode == USB_KEYBOARD_FARNS_WPM_ADJUST) || (usb_keyboard_mode == USB_KEYBOARD_SN_ENTRY)) { + if ((key > 29) && (key < 40)) { // convert keyboard code to number + if (key == 39) { + user_input_array[user_input_index] = 0; + #ifdef FEATURE_DISPLAY + keyboard_string.concat(String(0)); + #endif + } else { + user_input_array[user_input_index] = key - 29; + #ifdef FEATURE_DISPLAY + keyboard_string.concat(String(key-29)); + #endif + } + #ifdef FEATURE_DISPLAY + lcd_center_print_timed(keyboard_string, 1, default_display_msg_delay); + #endif + user_input_index++; + usb_keyboard_special_mode_start_time = millis(); + } else { // not a number key, is it a special key? + if ((key == 0x2a) && (user_input_index)){ //BACKSPACE + user_input_index--; + #ifdef FEATURE_DISPLAY + keyboard_string = keyboard_string.substring(0,keyboard_string.length()-1); + lcd_center_print_timed(keyboard_string, 1, default_display_msg_delay); + #endif + } + if ((key == 0x28) || (key == 0x58)) {user_input_process_it = 1;} // ENTER + if (key == 0x29) { // ESCAPE + user_input_index = 0; + usb_keyboard_mode = USB_KEYBOARD_NORMAL; + } + } + + if ((user_input_index >= user_num_input_places) || (user_input_process_it)){ // is the user input ready to be processed? + user_num_input_number_entered = 0; + int y = 1; + for (x = (user_input_index-1); x >= 0; x--){ + user_num_input_number_entered = user_num_input_number_entered + (user_input_array[x] * y); + y = y * 10; + } + if ((user_num_input_number_entered > user_num_input_lower_limit) && (user_num_input_number_entered < user_num_input_upper_limit)){ + switch(usb_keyboard_mode){ + case USB_KEYBOARD_WPM_ADJUST: + speed_set(user_num_input_number_entered); + #ifdef FEATURE_DISPLAY + lcd_status = LCD_REVERT; + #else + beep(); + #endif + config_dirty = 1; + break; + #ifdef FEATURE_FARNSWORTH + case USB_KEYBOARD_FARNS_WPM_ADJUST: + configuration.wpm_farnsworth = user_num_input_number_entered; + #ifdef FEATURE_DISPLAY + lcd_status = LCD_REVERT; + #else + beep(); + #endif + config_dirty = 1; + break; + #endif //FEATURE_FARNSWORTH + case USB_KEYBOARD_SN_ENTRY: + serial_number = user_num_input_number_entered; + #ifdef FEATURE_DISPLAY + lcd_status = LCD_REVERT; + #else + beep(); + #endif + break; + default: boop(); break; + } + } else { + boop(); // bad user input! + } + // reinitialize everything for the next go around + user_input_index = 0; + usb_keyboard_mode = USB_KEYBOARD_NORMAL; + } + + return; + } + + // grab the keypad / and * for dit and dah paddling + if (key == 0x54) {usb_dit = 1; return;} + if (key == 0x55) {usb_dah = 1; return;} + if (key == 0x58) {sending_mode = MANUAL_SENDING;tx_and_sidetone_key(1);return;} + + if ((modifier.bmLeftShift) || (modifier.bmRightShift)) { + switch(key){ + case 0x2a: // BACKSPACE - decrement serial number + serial_number--; + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("SN " + String(serial_number), 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Serial: " + String(serial_number), 0, default_display_msg_delay); + } + #endif + return; + break; + + } // switch(key) + #ifdef FEATURE_MEMORIES + if ((key >= 0x3a) && (key <= 0x45)){ // SHIFT F1-F12 : program memories + usb_keyboard_program_memory = key - 0x3a; // convert key scan code to memory number; F1 = 0 + if (usb_keyboard_program_memory > (number_of_memories - 1)) { + boop(); + return; + } + usb_keyboard_special_mode_start_time = millis(); + usb_keyboard_mode = USB_KEYBOARD_PROGRAM_MEM; + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_string = "PgmMem"; + } else { + lcd_string = "Program Memory"; + } + if (usb_keyboard_program_memory < 9) { + lcd_string.concat(' '); + } + keyboard_string = ""; + lcd_string.concat(usb_keyboard_program_memory+1); + lcd_center_print_timed(lcd_string, 0, default_display_msg_delay); + #else + boop_beep(); + #endif + + repeat_memory = 255; + return; + } + #endif //FEATURE_MEMORIES + + } // if ((modifier.bmLeftShift) || (modifier.bmRightShift)) + + if ((modifier.bmLeftAlt) || (modifier.bmRightAlt)) { + switch(key){ + #ifdef FEATURE_MEMORIES + case 0x3a: if (number_of_memories > 0) {repeat_memory_msg(0);} return; break; // F1 + case 0x3b: if (number_of_memories > 1) {repeat_memory_msg(1);} return; break; + case 0x3c: if (number_of_memories > 2) {repeat_memory_msg(2);} return; break; + case 0x3d: if (number_of_memories > 3) {repeat_memory_msg(3);} return; break; + case 0x3e: if (number_of_memories > 4) {repeat_memory_msg(4);} return; break; + case 0x3f: if (number_of_memories > 5) {repeat_memory_msg(5);} return; break; + case 0x40: if (number_of_memories > 6) {repeat_memory_msg(6);} return; break; + case 0x41: if (number_of_memories > 7) {repeat_memory_msg(7);} return; break; + case 0x42: if (number_of_memories > 8) {repeat_memory_msg(8);} return; break; + case 0x43: if (number_of_memories > 9) {repeat_memory_msg(9);} return; break; + case 0x44: if (number_of_memories > 10) {repeat_memory_msg(10);} return; break; + case 0x45: if (number_of_memories > 11) {repeat_memory_msg(11);} return; break; + #endif + } //switch(key) + } // if ((modifier.bmLeftAlt) || (modifier.bmRightAlt)) + + if ((modifier.bmLeftCtrl) || (modifier.bmRightCtrl)) { + #ifdef DEBUG_USB_KEYBOARD + debug_serial_port->print(F("KbdRptParser::OnKeyDown: CTRL-")); + debug_serial_port->println(keystroke); + #endif //DEBUG_USB_KEYBOARD + switch(key){ + case 0x04 : // CTRL-A + configuration.keyer_mode = IAMBIC_A; + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("Iambic A", 0, default_display_msg_delay); + #endif + config_dirty = 1; + break; + + case 0x05 : // CTRL-B + configuration.keyer_mode = IAMBIC_B; + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("Iambic B", 0, default_display_msg_delay); + #endif + config_dirty = 1; + break; + + case 0x06 : // CTRL-C + configuration.keyer_mode = SINGLE_PADDLE; + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("SnglePdl", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Single Paddle", 0, default_display_msg_delay); + } + #endif + config_dirty = 1; + break; + + #ifndef OPTION_NO_ULTIMATIC + case 0x07 : // CTRL-D + configuration.keyer_mode = ULTIMATIC; + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("Ultimatc", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Ultimatic", 0, default_display_msg_delay); + } + #endif + config_dirty = 1; + break; + #endif + + case 0x08 : // CTRL-E + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("Entr SN", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Enter Serial #", 0, default_display_msg_delay); + } + #else + boop_beep(); + #endif + usb_keyboard_mode = USB_KEYBOARD_SN_ENTRY; + user_num_input_places = 4; + user_num_input_lower_limit = 0; + user_num_input_upper_limit = 10000; + usb_keyboard_special_mode_start_time = millis(); + break; + + case 0x0a : // CTRL-G + configuration.keyer_mode = BUG; + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("Bug", 0, default_display_msg_delay); + #endif + config_dirty = 1; + break; + + case 0x0b : // CTRL-H + #ifdef FEATURE_HELL + if (char_send_mode == CW) { + char_send_mode = HELL; + beep(); + } else { + char_send_mode = CW; + beep(); + } + #endif //FEATURE_HELL + break; + + case 0x0c : // CTRL-I + if (key_tx && keyer_machine_mode != KEYER_COMMAND_MODE) { //Added check that keyer is NOT in command mode or keyer might be enabled for paddle commands (WD9DMP-1) + key_tx = 0; + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("TX Off", 0, default_display_msg_delay); + #endif + + } else if (!key_tx && keyer_machine_mode != KEYER_COMMAND_MODE) { //Added check that keyer is NOT in command mode or keyer might be enabled for paddle commands (WD9DMP) + key_tx = 1; + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("TX On", 0, default_display_msg_delay); + #endif + } + break; + + case 0x10: // CTRL-M + #ifdef FEATURE_FARNSWORTH + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("Frns WPM", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Farnsworth WPM", 0, default_display_msg_delay); + } + #else + boop_beep(); + #endif + usb_keyboard_mode = USB_KEYBOARD_FARNS_WPM_ADJUST; + user_num_input_places = 3; + user_num_input_lower_limit = -1; + user_num_input_upper_limit = 1000; + usb_keyboard_special_mode_start_time = millis(); + #endif + + break; + + case 0x11 : // CTRL-N + if (configuration.paddle_mode == PADDLE_NORMAL) { + configuration.paddle_mode = PADDLE_REVERSE; + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("Pdl Rev", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Paddle Reverse", 0, default_display_msg_delay); + } + #endif + } else { + configuration.paddle_mode = PADDLE_NORMAL; + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("Pdl Norm", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Paddle Normal", 0, default_display_msg_delay); + } + #endif + } + config_dirty = 1; + break; + + case 0x12 : // CTRL-O - cycle through sidetone modes on, paddle only and off - enhanced by Marc-Andre, VE2EVN + if (configuration.sidetone_mode == SIDETONE_PADDLE_ONLY) { + configuration.sidetone_mode = SIDETONE_OFF; + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("ST Off", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Sidetone Off", 0, default_display_msg_delay); + } + #endif + boop(); + } else if (configuration.sidetone_mode == SIDETONE_ON) { + configuration.sidetone_mode = SIDETONE_PADDLE_ONLY; + beep(); + delay(200); + beep(); + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("ST Pdl O", 0, default_display_msg_delay); + } + if (LCD_COLUMNS > 19){ + lcd_center_print_timed("Sidetone Paddle Only", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Sidetone", 0, default_display_msg_delay); + lcd_center_print_timed("Paddle Only", 1, default_display_msg_delay); + } + #endif + } else { + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("ST On", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Sidetone On", 0, default_display_msg_delay); + } + #endif + configuration.sidetone_mode = SIDETONE_ON; + beep(); + } + config_dirty = 1; + break; + + #if defined(FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING) + case 0x16 : // CTRL-S + if (configuration.cmos_super_keyer_iambic_b_timing_on){ + configuration.cmos_super_keyer_iambic_b_timing_on = 0; + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("CMOS Off", 0, default_display_msg_delay); + } + if (LCD_COLUMNS > 18){ + lcd_center_print_timed("CMOS Superkeyer Off", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("CMOS SK Off", 0, default_display_msg_delay); + } + #endif + } else { + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("CMOS On", 0, default_display_msg_delay); + } + if (LCD_COLUMNS > 17){ + lcd_center_print_timed("CMOS Superkeyer On", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("CMOS SK On", 0, default_display_msg_delay); + } + #endif + configuration.cmos_super_keyer_iambic_b_timing_on = 1; + } + config_dirty = 1; + break; + #endif //FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING + + + case 0x17 : // CTRL-T + #ifdef FEATURE_MEMORIES + repeat_memory = 255; + #endif + if (keyboard_tune_on) { + sending_mode = MANUAL_SENDING; + tx_and_sidetone_key(0); + keyboard_tune_on = 0; + #ifdef FEATURE_DISPLAY + lcd_status = LCD_REVERT; + #endif // FEATURE_DISPLAY + } else { + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("Tune", 0, default_display_msg_delay); + #endif + sending_mode = MANUAL_SENDING; + tx_and_sidetone_key(1); + keyboard_tune_on = 1; + } + break; + + case 0x18 : // CTRL-U + if (ptt_line_activated) { + manual_ptt_invoke = 0; + ptt_unkey(); + #ifdef FEATURE_DISPLAY + lcd_status = LCD_REVERT; + #endif // FEATURE_DISPLAY + } else { + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("PTTInvke", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("PTT Invoke", 0, default_display_msg_delay); + } + #endif + manual_ptt_invoke = 1; + ptt_key(); + } + break; + + case 0x1a : // CTRL-W + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("WPM Adj", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("WPM Adjust", 0, default_display_msg_delay); + } + #else + boop_beep(); + #endif + usb_keyboard_mode = USB_KEYBOARD_WPM_ADJUST; + user_num_input_places = 3; + user_num_input_lower_limit = 0; + user_num_input_upper_limit = 1000; + usb_keyboard_special_mode_start_time = millis(); + break; + + case 0x3a : // CTRL-F1 + switch_to_tx_silent(1); + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("TX 1", 0, default_display_msg_delay); + #endif + break; + + case 0x3b : // CTRL-F2 + if ((ptt_tx_2) || (tx_key_line_2)) { + switch_to_tx_silent(2); + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("TX 2", 0, default_display_msg_delay); + #endif + } + break; + + case 0x3c : // CTRL-F3 + if ((ptt_tx_3) || (tx_key_line_3)) { + switch_to_tx_silent(3); + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("TX 3", 0, default_display_msg_delay); + #endif + } + break; + + case 0x3d : // CTRL-F4 + if ((ptt_tx_4) || (tx_key_line_4)) { + switch_to_tx_silent(4); + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("TX 4", 0, default_display_msg_delay); + #endif + } + break; + + case 0x3e : // CTRL-F5 + if ((ptt_tx_5) || (tx_key_line_5)) { + switch_to_tx_silent(5); + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("TX 5", 0, default_display_msg_delay); + #endif + } + break; + + case 0x3f : // CTRL-F6 + if ((ptt_tx_6) || (tx_key_line_6)) { + switch_to_tx_silent(6); + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("TX 6", 0, default_display_msg_delay); + #endif + } + break; + + #ifdef FEATURE_AUTOSPACE + case 0x1d: // CTRL-Z + if (configuration.autospace_active) { + configuration.autospace_active = 0; + config_dirty = 1; + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("AutoSOff", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Autospace Off", 0, default_display_msg_delay); + } + #endif + } else { + configuration.autospace_active = 1; + config_dirty = 1; + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("AutoS On", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Autospace On", 0, default_display_msg_delay); + } + #endif + } + break; + #endif + } //switch(keystroke) + return; + } //if ((modifier.bmLeftCtrl) || (modifier.bmRightCtrl)) + + // special keys with no modifiers + switch(key){ + case 0x4b: case 0x61: sidetone_adj(20); return; break; + case 0x4e: case 0x5b: sidetone_adj(-20); return; break; + case 0x4f: case 0x5e: adjust_dah_to_dit_ratio(int(configuration.dah_to_dit_ratio/10)); return; break; + case 0x50: case 0x5c: adjust_dah_to_dit_ratio(-1*int(configuration.dah_to_dit_ratio/10)); return; break; + case 0x52: case 0x60: speed_set(configuration.wpm+1); return; break; + case 0x51: case 0x5a: speed_set(configuration.wpm-1); return; break; + case 0x4a: case 0x5f: //HOME + configuration.dah_to_dit_ratio = initial_dah_to_dit_ratio; + key_tx = 1; + config_dirty = 1; + #ifdef FEATURE_DISPLAY + #ifdef OPTION_MORE_DISPLAY_MSGS + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("DfltRtio", 0, default_display_msg_delay); + } else { + lcd_center_print_timed("Default ratio", 0, default_display_msg_delay); + } + service_display(); + #endif + #endif + return; + break; + case 0x2b: case 0x48: // TAB, PAUSE + if (pause_sending_buffer) { + pause_sending_buffer = 0; + #ifdef FEATURE_DISPLAY + #ifdef OPTION_MORE_DISPLAY_MSGS + lcd_center_print_timed("Resume", 0, default_display_msg_delay); + #endif + #endif + } else { + pause_sending_buffer = 1; + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("Pause", 0, default_display_msg_delay); + #endif + } + return; + break; // pause + + case 0x47: // SCROLL - Prosign next two characters + usb_keyboard_prosign_flag = 1; + #ifdef FEATURE_DISPLAY + #ifdef OPTION_MORE_DISPLAY_MSGS + lcd_center_print_timed("Prosign", 0, default_display_msg_delay); + #endif + #endif + return; + break; + + case 0x46: if (send_buffer_bytes) { send_buffer_bytes--; } return; break; // DEL + case 0x29 : // ESC - clear the serial send buffer and a bunch of other stuff + if (manual_ptt_invoke) { + manual_ptt_invoke = 0; + ptt_unkey(); + } + if (keyboard_tune_on) { + sending_mode = MANUAL_SENDING; + tx_and_sidetone_key(0); + keyboard_tune_on = 0; + } + if (pause_sending_buffer) { + pause_sending_buffer = 0; + } + clear_send_buffer(); + #ifdef FEATURE_MEMORIES + //clear_memory_button_buffer(); + play_memory_prempt = 1; + repeat_memory = 255; + #endif + #ifdef FEATURE_DISPLAY + lcd_center_print_timed("Abort", 0, default_display_msg_delay); + #endif + return; + break; + + case 0x49: case 0x62: // INSERT - send serial number and increment + put_serial_number_in_send_buffer(); + serial_number++; + return; + break; + + case 0x4d: case 0x59: // END - send serial number no increment + put_serial_number_in_send_buffer(); + return; + break; + + #ifdef FEATURE_MEMORIES + case 0x3a: ps2_usb_keyboard_play_memory(0); return; break; // F1 + case 0x3b: ps2_usb_keyboard_play_memory(1); return; break; + case 0x3c: ps2_usb_keyboard_play_memory(2); return; break; + case 0x3d: ps2_usb_keyboard_play_memory(3); return; break; + case 0x3e: ps2_usb_keyboard_play_memory(4); return; break; + case 0x3f: ps2_usb_keyboard_play_memory(5); return; break; + case 0x40: ps2_usb_keyboard_play_memory(6); return; break; + case 0x41: ps2_usb_keyboard_play_memory(7); return; break; + case 0x42: ps2_usb_keyboard_play_memory(8); return; break; + case 0x43: ps2_usb_keyboard_play_memory(9); return; break; + case 0x44: ps2_usb_keyboard_play_memory(10); return; break; + case 0x45: ps2_usb_keyboard_play_memory(11); return; break; + #endif + + } // switch(key) + + + + // regular keys + if (keystroke) { + if ((keystroke > 31) && (keystroke < 123)) { + if (usb_keyboard_prosign_flag) { + add_to_send_buffer(SERIAL_SEND_BUFFER_PROSIGN); + usb_keyboard_prosign_flag = 0; + } + keystroke = uppercase(keystroke); + add_to_send_buffer(keystroke); + #ifdef FEATURE_MEMORIES + repeat_memory = 255; + #endif + } + } //if (keystroke) + + // have we been in a special mode too long? + if ((usb_keyboard_mode != USB_KEYBOARD_NORMAL) && ((millis() - usb_keyboard_special_mode_start_time) > USB_KEYBOARD_SPECIAL_MODE_TIMEOUT)) { + usb_keyboard_mode = USB_KEYBOARD_NORMAL; + user_input_index = 0; + #ifdef DEBUG_USB_KEYBOARD + debug_serial_port->println(F("KbdRptParser::OnKeyDown: usb_keyboard_mode timeout")); + #endif //DEBUG_USB_KEYBOARD + return; + } + +} +#endif //FEATURE_USB_KEYBOARD + +//--------------------------------------------------------------------- +#ifdef FEATURE_USB_KEYBOARD +void KbdRptParser::OnKeyUp(uint8_t mod, uint8_t key) +{ + + // grab the keypad / and * for dit and dah paddling + if (key == 0x54) {usb_dit = 0; return;} + if (key == 0x55) {usb_dah = 0; return;} + if (key == 0x58) {sending_mode = MANUAL_SENDING;tx_and_sidetone_key(0);return;} + +} +#endif //FEATURE_USB_KEYBOARD + +//--------------------------------------------------------------------- + +void initialize_usb() +{ + + #if defined(FEATURE_USB_KEYBOARD) || defined(FEATURE_USB_MOUSE) + if (Usb.Init() == -1) { + #ifdef DEBUG_USB + debug_serial_port->println(F("\rinitialize_usb: OSC did not start.")); + #endif //DEBUG_USB + return; + } else { + #ifdef DEBUG_USB + debug_serial_port->println(F("\rinitialize_usb: initializing")); + #endif //DEBUG_USB + } + delay(200); + next_time = millis() + 5000; + #endif // (FEATURE_USB_KEYBOARD) || defined(FEATURE_USB_MOUSE) + + #ifdef FEATURE_USB_KEYBOARD + HidKeyboard.SetReportParser(0, (HIDReportParser*)&KeyboardPrs); + #endif //FEATURE_USB_KEYBOARD + + #ifdef FEATURE_USB_MOUSE + HidMouse.SetReportParser(0,(HIDReportParser*)&MousePrs); + #endif //FEATURE_USB_MOUSE + + #if defined(FEATURE_USB_KEYBOARD) || defined(FEATURE_USB_MOUSE) + unsigned long start_init = millis(); + while ((millis() - start_init) < 2000){ + Usb.Task(); + } + #ifdef DEBUG_USB + debug_serial_port->println(F("intialize_usb: initialized")); + #endif //DEBUG_USB + #endif // (FEATURE_USB_KEYBOARD) || defined(FEATURE_USB_MOUSE) +} +//--------------------------------------------------------------------- +#if defined(FEATURE_USB_KEYBOARD) || defined(FEATURE_USB_MOUSE) +void service_usb(){ + + Usb.Task(); + +} +#endif //FEATURE_USB_KEYBOARD || FEATURE_USB_MOUSE + +//--------------------------------------------------------------------- +#ifdef FEATURE_USB_MOUSE +void MouseRptParser::OnMouseMove(MOUSEINFO *mi){ + + /* + debug_serial_port->print("dx="); + debug_serial_port->print(mi->dX, DEC); + debug_serial_port->print(" dy="); + debug_serial_port->println(mi->dY, DEC); + */ + + /* this is just me fooling around */ + + #ifdef OPTION_MOUSE_MOVEMENT_PADDLE + static int last_dX = 0; + static int last_dY = 0; + int current_dX = (mi->dX); + int current_dY = (mi->dY); + + /* X/Y method - doesn't work too well + if ((current_dX != last_dX) && (abs(current_dX) > abs(current_dY)) && (abs(current_dX) > 3)){ + dit_buffer = 1; + } + if ((current_dY != last_dY) && (abs(current_dY) > abs(current_dX)) && (abs(current_dY) > 3)){ + dah_buffer = 1; + } + */ + + /* X only method */ + if ((current_dX != last_dX) && (abs(current_dX) > 8)){ + if (current_dX < 0) { + dit_buffer = 1; + } else { + dah_buffer = 1; + } + } + + last_dX = current_dX; + last_dY = current_dY; + #endif //OPTION_MOUSE_MOVEMENT_PADDLE + +}; + +void MouseRptParser::OnLeftButtonUp(MOUSEINFO *mi){ + usb_dit = 0; +}; +void MouseRptParser::OnLeftButtonDown(MOUSEINFO *mi){ + usb_dit = 1; +}; +void MouseRptParser::OnRightButtonUp(MOUSEINFO *mi){ + usb_dah = 0; +}; +void MouseRptParser::OnRightButtonDown(MOUSEINFO *mi){ + usb_dah = 1; +}; +void MouseRptParser::OnMiddleButtonUp(MOUSEINFO *mi){ + sending_mode = MANUAL_SENDING; + tx_and_sidetone_key(0); +}; +void MouseRptParser::OnMiddleButtonDown(MOUSEINFO *mi){ + sending_mode = MANUAL_SENDING; + tx_and_sidetone_key(1); +}; +#endif //FEATURE_USB_MOUSE +//--------------------------------------------------------------------- + +#ifdef FEATURE_CAPACITIVE_PADDLE_PINS +uint8_t read_capacitive_pin(int pinToMeasure) { + + /* + + This code is from http://playground.arduino.cc/Code/CapacitiveSensor + + Original code by Mario Becker, Fraunhofer IGD, 2007 http://www.igd.fhg.de/igd-a4 + + Updated by: Alan Chatham http://unojoy.tumblr.com + + Updated by Paul Stoffregen: Replaced '328 specific code with portOutputRegister, etc for compatibility with Arduino Mega, Teensy, Sanguino and other boards + + Gratuitous optimization to improve sensitivity by Casey Rodarmor. + + */ + + + + // Variables used to translate from Arduino to AVR pin naming + + volatile uint8_t* port; + volatile uint8_t* ddr; + volatile uint8_t* pin; + + // Here we translate the input pin number from + // Arduino pin number to the AVR PORT, PIN, DDR, + // and which bit of those registers we care about. + + byte bitmask; + port = portOutputRegister(digitalPinToPort(pinToMeasure)); + ddr = portModeRegister(digitalPinToPort(pinToMeasure)); + bitmask = digitalPinToBitMask(pinToMeasure); + pin = portInputRegister(digitalPinToPort(pinToMeasure)); + // Discharge the pin first by setting it low and output + *port &= ~(bitmask); + *ddr |= bitmask; + delay(1); + // Prevent the timer IRQ from disturbing our measurement + noInterrupts(); + // Make the pin an input with the internal pull-up on + *ddr &= ~(bitmask); + *port |= bitmask; + + // Now see how long the pin to get pulled up. This manual unrolling of the loop + // decreases the number of hardware cycles between each read of the pin, + // thus increasing sensitivity. + uint8_t cycles = 17; + /* if (*pin & bitmask) { cycles = 0;} + else if (*pin & bitmask) { cycles = 1;} + else if (*pin & bitmask) { cycles = 2;} + else if (*pin & bitmask) { cycles = 3;} + else if (*pin & bitmask) { cycles = 4;} + else if (*pin & bitmask) { cycles = 5;} + else if (*pin & bitmask) { cycles = 6;} + else if (*pin & bitmask) { cycles = 7;} + else if (*pin & bitmask) { cycles = 8;} + else if (*pin & bitmask) { cycles = 9;} + else if (*pin & bitmask) { cycles = 10;} + else if (*pin & bitmask) { cycles = 11;} + else if (*pin & bitmask) { cycles = 12;} + else if (*pin & bitmask) { cycles = 13;} + else if (*pin & bitmask) { cycles = 14;} + else if (*pin & bitmask) { cycles = 15;} + else if (*pin & bitmask) { cycles = 16;}*/ + + + if (*pin & bitmask) { + cycles = 0; + } else { + if (*pin & bitmask) { + cycles = 1; + } else { + if (*pin & bitmask) { + cycles = 2; + } else { + if (*pin & bitmask) { + cycles = 3; + } else { + if (*pin & bitmask) { + cycles = 4; + } else { + if (*pin & bitmask) { + cycles = 5; + } else { + if (*pin & bitmask) { + cycles = 6; + } else { + if (*pin & bitmask) { + cycles = 7; + } else { + if (*pin & bitmask) { + cycles = 8; + } else { + if (*pin & bitmask) { + cycles = 9; + } else { + if (*pin & bitmask) { + cycles = 10; + } else { + if (*pin & bitmask) { + cycles = 11; + } else { + if (*pin & bitmask) { + cycles = 12; + } else { + if (*pin & bitmask) { + cycles = 13; + } else { + if (*pin & bitmask) { + cycles = 14; + } else { + if (*pin & bitmask) { + cycles = 15; + } else { + if (*pin & bitmask) { + cycles = 16; + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + + // End of timing-critical section + interrupts(); + + // Discharge the pin again by setting it low and output + // It's important to leave the pins low if you want to + // be able to touch more than 1 sensor at a time - if + // the sensor is left pulled high, when you touch + // two sensors, your body will transfer the charge between + // sensors. + *port &= ~(bitmask); + *ddr |= bitmask; + + #ifdef DEBUG_CAPACITIVE_PADDLE + static unsigned long last_cap_paddle_debug = 0; + if ((millis() - last_cap_paddle_debug) > 250){ + debug_serial_port->flush(); + debug_serial_port->print("read_capacitive_pin: pin:"); + debug_serial_port->print(pinToMeasure); + debug_serial_port->print(" cyc:"); + debug_serial_port->println(cycles); + last_cap_paddle_debug = millis(); + } + #endif //DEBUG_CAPACITIVE_PADDLE + + return cycles; + +} + +#endif //FEATURE_CAPACITIVE_PADDLE_PINS + +//--------------------------------------------------------------------- +#ifdef FEATURE_LED_RING +void update_led_ring(){ + + static int last_leds = 0; + int leds = 0; + + + + leds = map(configuration.wpm,led_ring_low_limit,led_ring_high_limit,0,15); + if (leds < 0){leds = 0;} + if (leds > 15){leds = 15;} + + if (leds != last_leds){ + digitalWrite(led_ring_le,LOW); + + digitalWrite(led_ring_sdi,LOW); + digitalWrite(led_ring_clk,HIGH); + digitalWrite(led_ring_clk,LOW); + + + for (int x = 15;x > 0;x--){ + if (x <= leds){ + digitalWrite(led_ring_sdi,HIGH); + } else { + digitalWrite(led_ring_sdi,LOW); + } + digitalWrite(led_ring_clk,HIGH); + digitalWrite(led_ring_clk,LOW); + } + + //shiftOut(led_ring_sdi,led_ring_clk,MSBFIRST,(sequence[y][x] >> 8)); //High byte first + //shiftOut(led_ring_sdi,led_ring_clk,MSBFIRST,sequence[y][x]); //Low byte second + digitalWrite(led_ring_le,HIGH); + + last_leds = leds; + digitalWrite(led_ring_sdi,LOW); + } + +} +#endif //FEATURE_LED_RING +//--------------------------------------------------------------------- +int paddle_pin_read(int pin_to_read){ + + // Updated code provided by Fred, VK2EFL + // + // Note on OPTION_DIRECT_PADDLE_PIN_READS_MEGA, OPTION_DIRECT_PADDLE_PIN_READS_UNO, OPTION_SAVE_MEMORY_NANOKEYER + // For Mega2560 and Uno/Nano speed up paddle pin reads by direct read of the register + // it saves about 340 bytes of code too + + + #ifndef FEATURE_CAPACITIVE_PADDLE_PINS + #ifndef OPTION_INVERT_PADDLE_PIN_LOGIC + #ifdef OPTION_DIRECT_PADDLE_PIN_READS_MEGA // after April 2019, if this option is not defined then a direct read of the pins can never occur + switch(pin_to_read) { + case 2: return(bitRead(PINE, 4)); break; + case 5: return(bitRead(PINE, 3)); break; + } // end switch + #endif // OPTION_DIRECT_PADDLE_READS_MEGA + #ifdef OPTION_DIRECT_PADDLE_PIN_READS_UNO // since with this verion, April 2019, this option is not defined then a direct read of the pins can never occur + return (bitRead(PIND, pin_to_read)); // use this line on Unos and Nanos + #endif // OPTION_DIRECT_PADDLE_PIN_READS_UNO + #ifdef OPTION_SAVE_MEMORY_NANOKEYER // + switch(pin_to_read) { + case 2: return(bitRead(PIND, 2)); break; + case 5: return(bitRead(PIND, 5)); break; + case 8: return(bitRead(PINB, 0)); break; + } // end switch + #endif // OPTION_SAVE_MEMORY_NANOKEYER + #if !defined(OPTION_DIRECT_PADDLE_PIN_READS_UNO) && !defined(OPTION_DIRECT_PADDLE_PIN_READS_MEGA) && !defined(OPTION_SAVE_MEMORY_NANOKEYER) + return digitalRead(pin_to_read); // code using digitalRead + #endif // !defined(OPTION_DIRECT_PADDLE_PIN_READS_UNO) && !defined(OPTION_DIRECT_PADDLE_PIN_READS_MEGA) + #else // !OPTION_INVERT_PADDLE_PIN_LOGIC + return !digitalRead(pin_to_read); // we do the regular digitalRead() if none of the direct register read options are valid + #endif // !OPTION_INVERT_PADDLE_PIN_LOGIC + #else // !FEATURE_CAPACITIVE_PADDLE_PINS + if (capactive_paddle_pin_inhibit_pin) { + if (digitalRead(capactive_paddle_pin_inhibit_pin) == HIGH) { + return digitalRead(pin_to_read); + } // end if + } // end if (capactive_paddle_pin_inhibit_pin) + if (read_capacitive_pin(pin_to_read) > capacitance_threshold) return LOW; + else return HIGH; + #endif // !FEATURE_CAPACITIVE_PADDLE_PINS + + + + // #ifndef FEATURE_CAPACITIVE_PADDLE_PINS + // #ifndef OPTION_INVERT_PADDLE_PIN_LOGIC + // #if defined(OPTION_DIRECT_PADDLE_PIN_READS_MEGA) + // switch(pin_to_read){ + // case 2: return(bitRead(PINE,4));break; + // case 5: return(bitRead(PINE,3));break; + // } + // #else //OPTION_DIRECT_PADDLE_READS_MEGA + // return digitalRead(pin_to_read); + // #endif //OPTION_DIRECT_PADDLE_READS_MEGA + // #else + // return !digitalRead(pin_to_read); + // #endif + // #else + // if (capactive_paddle_pin_inhibit_pin){ + // if (digitalRead(capactive_paddle_pin_inhibit_pin) == HIGH){ + // return digitalRead(pin_to_read); + // } + + // } + + // if (read_capacitive_pin(pin_to_read) > capacitance_threshold) { + // return LOW; + // } else { + // return HIGH; + // } + + // #endif //FEATURE_CAPACITIVE_PADDLE_PINS + +} +//--------------------------------------------------------------------- +#ifdef FEATURE_ALPHABET_SEND_PRACTICE +void command_alphabet_send_practice(){ + + // contributed by Ryan, KC2ZWM + + int cw_char; + char letter = 'A'; + + do + { + cw_char = get_cw_input_from_user(0); + if (letter == (char)(convert_cw_number_to_ascii(cw_char))){ + if (correct_answer_led) { + digitalWrite(correct_answer_led, HIGH); + } + if (wrong_answer_led) { + digitalWrite(wrong_answer_led, LOW); + } + beep(); + + //send_dit(); + if (letter < 'Z') + letter++; + else + letter = 'A'; + } + else + if (cw_char != 9) { + if (wrong_answer_led) { + digitalWrite(wrong_answer_led, HIGH); + } + if (correct_answer_led) { + digitalWrite(correct_answer_led, LOW); + } + boop(); + boop(); + //send_dah(); + } + } while (cw_char != 9); + + + if (correct_answer_led) { + digitalWrite(correct_answer_led, LOW); + } + if (wrong_answer_led) { + digitalWrite(wrong_answer_led, LOW); + } + +} +#endif //FEATURE_ALPHABET_SEND_PRACTICE + + +//--------------------------------------------------------------------- +#ifdef FEATURE_PTT_INTERLOCK +void service_ptt_interlock(){ + + static unsigned long last_ptt_interlock_check = 0; + + if ((millis() - last_ptt_interlock_check) > ptt_interlock_check_every_ms){ + if (digitalRead(ptt_interlock) == ptt_interlock_active_state){ + if (!ptt_interlock_active){ + ptt_interlock_active = 1; + #ifdef FEATURE_DISPLAY + if (LCD_COLUMNS < 9){ + lcd_center_print_timed("PTT Lock",0,2000); + } else { + lcd_center_print_timed("PTT Interlock",0,2000); + } + #endif //FEATURE_DISPLAY + } + } else { + if (ptt_interlock_active){ + ptt_interlock_active = 0; + } + } + last_ptt_interlock_check = millis(); + } +} +#endif //FEATURE_PTT_INTERLOCK + + + + +//--------------------------------------------------------------------- + +#if defined(OPTION_WINKEY_SEND_BREAKIN_STATUS_BYTE) && defined(FEATURE_WINKEY_EMULATION) +void service_winkey_breakin(){ + + + if (send_winkey_breakin_byte_flag){ + winkey_port_write(0xc2|winkey_sending|winkey_xoff,0); // 0xc2 - BREAKIN bit set high + winkey_interrupted = 1; + send_winkey_breakin_byte_flag = 0; + #ifdef DEBUG_WINKEY + debug_serial_port->println("service_winkey_breakin: winkey_interrupted = 1"); + #endif + } + +} +#endif //defined(OPTION_WINKEY_SEND_BREAKIN_STATUS_BYTE) && defined(FEATURE_WINKEY_EMULATION) + +//--------------------------------------------------------------------- + +void initialize_ethernet_variables(){ + + #if defined(FEATURE_ETHERNET) + for (int x = 0;x < 4;x++){ + configuration.ip[x] = default_ip[x]; + configuration.gateway[x] = default_gateway[x]; + configuration.subnet[x] = default_subnet[x]; + for (int y = 0;y < FEATURE_INTERNET_LINK_MAX_LINKS;y++){ + configuration.link_send_ip[x][y] = 0; + configuration.link_send_enabled[y] = 0; + configuration.link_send_udp_port[y] = FEATURE_INTERNET_LINK_DEFAULT_RCV_UDP_PORT; + } + } + configuration.link_receive_udp_port = FEATURE_INTERNET_LINK_DEFAULT_RCV_UDP_PORT; + configuration.link_receive_enabled = 0; + #endif //FEATURE_ETHERNET +} + +//------------------------------------------------------------------------------------------------------- +void initialize_ethernet(){ + + #if defined(FEATURE_ETHERNET) + Ethernet.begin(mac, configuration.ip, configuration.dns_server, configuration.gateway, configuration.subnet); + #endif + +} + + + +//------------------------------------------------------------------------------------------------------- + +void initialize_udp(){ + + #if defined(FEATURE_UDP) + int udpbegin_result = Udp.begin(udp_listener_port); + + #if defined(DEBUG_UDP) + if (!udpbegin_result){ + debug_serial_port->println("initialize_udp: Udp.begin error"); + } + #endif + #endif //FEATURE_UDP +} + + +//------------------------------------------------------------------------------------------------------- + + +void initialize_web_server(){ + #if defined(FEATURE_WEB_SERVER) + + server.begin(); + + #ifdef DEBUG_WEB_SERVER + debug_serial_port->print(F("initialize_web_server: server is at ")); + debug_serial_port->println(Ethernet.localIP()); + #endif + + #endif //FEATURE_WEB_SERVER +} + + +//------------------------------------------------------------------------------------------------------- + + + +#if defined(FEATURE_ETHERNET) +void check_for_network_restart(){ + + if (restart_networking){ + initialize_web_server(); + restart_networking = 0; + } +} +#endif //FEATURE_ETHERNET + +//------------------------------------------------------------------------------------------------------- + +#if defined(FEATURE_WEB_SERVER) +void service_web_server() { + + + if ((web_control_tx_key_time > 0) && ((millis()-web_control_tx_key_time) > (WEB_SERVER_CONTROL_TX_KEY_TIME_LIMIT_SECS*1000))){ + tx_and_sidetone_key(0); + web_control_tx_key_time = 0; + } + + // Create a client connection + EthernetClient client = server.available(); + if (client) { + + valid_request = 0; + + while (client.connected()){ + if (client.available()){ + char c = client.read(); + + //read char by char HTTP request + if (web_server_incoming_string.length() < MAX_WEB_REQUEST){ + //store characters to string + web_server_incoming_string += c; + #if defined(DEBUG_WEB_SERVER_READS) + debug_serial_port->print("service_web_server: read: "); + debug_serial_port->print(c); + #endif //DEBUG_WEB_SERVER_READS + } else { + // web_server_incoming_string = ""; + } + + //has HTTP request ended? + if (c == '\n'){ + + #if defined(DEBUG_WEB_SERVER_READS) + debug_serial_port->println(web_server_incoming_string); //print to serial monitor for debuging + #endif //DEBUG_WEB_SERVER_READS + + if (web_server_incoming_string.startsWith("GET / ")){ + valid_request = 1; + web_print_page_main_menu(client); + } + + if (web_server_incoming_string.startsWith("GET /About")){ + valid_request = 1; + web_print_page_about(client); + } + + if (web_server_incoming_string.startsWith("GET /KeyerSettings")){ + valid_request = 1; + // are there form results being posted? + if (web_server_incoming_string.indexOf("?") > 0){ + web_print_page_keyer_settings_process(client); + } else { + web_print_page_keyer_settings(client); + } + } + + if (web_server_incoming_string.startsWith("GET /NetworkSettings")){ + valid_request = 1; + // are there form results being posted? + if (web_server_incoming_string.indexOf("?ip0=") > 0){ + web_print_page_network_settings_process(client); + } else { + web_print_page_network_settings(client); + } + } + + #if defined(FEATURE_INTERNET_LINK) + if (web_server_incoming_string.startsWith("GET /LinkSettings")){ + valid_request = 1; + // are there form results being posted? + if (web_server_incoming_string.indexOf("?ip") > 0){ + web_print_page_link_settings_process(client); + } else { + web_print_page_link_settings(client); + } + } + #endif //FEATURE_INTERNET_LINK + if (web_server_incoming_string.startsWith("GET /ctrl")){ + valid_request = 1; + web_print_page_control(client); + } + + #if defined(FEATURE_MEMORIES) + if (web_server_incoming_string.startsWith("GET /mem")){ + valid_request = 1; + // are there form results being posted? + // if (web_server_incoming_string.indexOf("?") > 0){ + // web_print_page_memories_process(client); + // } else { + web_print_page_memories(client); + // } + } + #endif //FEATURE_MEMORIES + + + if (!valid_request){ + web_print_page_404(client); + } + + delay(1); + client.stop(); + web_server_incoming_string = ""; + } + } + } + } + +} +#endif //FEATURE_WEB_SERVER +//------------------------------------------------------------------------------------------------------- +#if defined(FEATURE_WEB_SERVER) +void web_print_200OK(EthernetClient client){ + + web_client_print(client,F("HTTP/1.1 200 OK\nContent-Type: text/html\n\n")); + +} +#endif //FEATURE_WEB_SERVER +//------------------------------------------------------------------------------------------------------- +#if defined(FEATURE_WEB_SERVER) +void web_print_header(EthernetClient client){ + + web_print_200OK(client); + web_client_println(client,F("")); + +} +#endif //FEATURE_WEB_SERVER +//------------------------------------------------------------------------------------------------------- +#if defined(FEATURE_WEB_SERVER) +void web_print_style_sheet(EthernetClient client){ + + web_client_print(client,F("")); + +} +#endif //FEATURE_WEB_SERVER +//------------------------------------------------------------------------------------------------------- +#if defined(FEATURE_WEB_SERVER) +void web_print_home_link(EthernetClient client){ + + web_client_println(client,F("
Home
")); + +} +#endif //FEATURE_WEB_SERVER +//------------------------------------------------------------------------------------------------------- +#if defined(FEATURE_WEB_SERVER) +void web_print_footer(EthernetClient client){ + + + web_client_println(client,F("
")); + + +} +#endif //FEATURE_WEB_SERVER +//------------------------------------------------------------------------------------------------------- +#if defined(FEATURE_WEB_SERVER) +void web_print_title(EthernetClient client){ + + + web_client_println(client,F("K3NG CW Keyer")); + +} +#endif //FEATURE_WEB_SERVER + +//------------------------------------------------------------------------------------------------------- +#if defined(FEATURE_WEB_SERVER) + +void web_print_page_network_settings(EthernetClient client){ + + web_print_header(client); + + web_print_style_sheet(client); + + web_print_title(client); + + web_client_println(client,F("

Network Settings



")); + + // input form + web_client_print(client,F("

IP: ..."); + + + web_client_print(client,F("

Gateway: ..."); + + web_client_print(client,F("

Subnet Mask: ...

"); + + + web_print_home_link(client); + + web_print_footer(client); + +} + + +#endif //FEATURE_WEB_SERVER + +//------------------------------------------------------------------------------------------------------- +#if defined(FEATURE_WEB_SERVER) && defined(FEATURE_INTERNET_LINK) + +void web_print_page_link_settings(EthernetClient client){ + + web_print_header(client); + + web_print_style_sheet(client); + + web_print_title(client); + + web_client_println(client,F("

Link Settings



Link Send Settings

")); + + + // input form + for (int x = 0;x < FEATURE_INTERNET_LINK_MAX_LINKS;x++){ + web_client_print(client,"

Link "); + web_client_print(client,x+1); + web_client_print(client,": IP: ..."); + + web_client_print(client," UDP Port: "); + + // link active/inactive radio buttons + web_client_print(client,"Enabled Disabled
"); + + } + + web_client_print(client,F("

Link Receive Settings



UDP Receive Port: "); + + web_client_print(client,"Enabled Disabled
"); + + web_client_println(client,"

"); + + web_print_home_link(client); + + web_print_footer(client); + +} + + +#endif //FEATURE_WEB_SERVER + +//------------------------------------------------------------------------------------------------------- + +#if defined(FEATURE_WEB_SERVER) + +void web_print_page_404(EthernetClient client){ + + web_client_println(client,F("HTTP/1.1 404 NOT FOUND")); + web_client_println(client,F("Content-Type: text/html\n")); + web_client_println(client,F("Sorry, dude. Page not found.")); + web_print_home_link(client); + web_print_footer(client); + +} + +#endif //FEATURE_WEB_SERVER +//------------------------------------------------------------------------------------------------------- + +#if defined(FEATURE_WEB_SERVER) + +void web_print_page_about(EthernetClient client){ + + web_print_header(client); + + web_client_println(client,F("")); + + web_print_style_sheet(client); + + web_print_title(client); + + web_client_println(client,F("

About



")); + web_client_println(client,CODE_VERSION); + web_client_println(client,"
"); + + #if !defined(HARDWARE_GENERIC_STM32F103C) + void* HP = malloc(4); + if (HP){ + free (HP); + } + unsigned long free = (unsigned long)SP - (unsigned long)HP; + + // web_client_print(client,"Heap = 0x"); + // web_client_println(client,(unsigned long)HP,HEX); + // web_client_println(client,"
"); + // web_client_print(client,"Stack = 0x"); + // web_client_println(client,(unsigned long)SP,HEX); + // web_client_println(client,"
"); + + web_client_print(client,free); + web_client_println(client,F(" bytes free

")); + #endif + + + + + unsigned long seconds = (millis() / 1000L) + ((pow(2,32)/ 1000L) * millis_rollover); + + + int days = seconds / 86400L; + seconds = seconds - (long(days) * 86400L); + + int hours = seconds / 3600L; + seconds = seconds - (long(hours) * 3600L); + + int minutes = seconds / 60L; + seconds = seconds - (minutes * 60); + + web_client_print(client,days); + web_client_print(client,":"); + if (hours < 10) {web_client_print(client,"0");} + web_client_print(client,hours); + web_client_print(client,":"); + if (minutes < 10) {web_client_print(client,"0");} + web_client_print(client,minutes); + web_client_print(client,":"); + if (seconds < 10) {web_client_print(client,"0");} + web_client_print(client,seconds); + web_client_println(client,F(" dd:hh:mm:ss uptime
")); + + web_client_println(client,F("


Anthony Good, K3NG
anthony.good@gmail.com
Radio Artisan

")); + + web_print_home_link(client); + + web_print_footer(client); +} + +#endif //FEATURE_WEB_SERVER + +//------------------------------------------------------------------------------------------------------- +#if defined(FEATURE_WEB_SERVER) +void parse_get(String str){ + + String workstring = ""; + String parameter = ""; + String value = ""; + + for(int x = 0;x < MAX_PARSE_RESULTS;x++){ + parse_get_results[x].parameter = ""; + parse_get_results[x].value_string = ""; + parse_get_results[x].value_long = 0; + } + parse_get_results_index = 0; + + #if defined(DEBUG_WEB_PARSE_GET) + debug_serial_port->print("parse_get: raw workstring: "); + Serial.println(str); + #endif + + workstring = str.substring(str.indexOf("?")+1); + + #if defined(DEBUG_WEB_PARSE_GET) + debug_serial_port->print("parse_get: workstring: "); + Serial.println(workstring); + #endif + + while(workstring.indexOf("=") > 0){ + parameter = workstring.substring(0,workstring.indexOf("=")); + if(workstring.indexOf("&") > 0){ + value = workstring.substring(workstring.indexOf("=")+1,workstring.indexOf("&")); + workstring = workstring.substring(workstring.indexOf("&")+1); + } else { + value = workstring.substring(workstring.indexOf("=")+1,workstring.indexOf(" ")); + // value = workstring.substring(workstring.indexOf("=")+1); + workstring = ""; + } + #if defined(DEBUG_WEB_PARSE_GET) + debug_serial_port->print("parse_get: parameter: "); + debug_serial_port->print(parameter); + debug_serial_port->print(" value: "); + debug_serial_port->println(value); + #endif //DEBUG_WEB_PARSE_GET + + if (parse_get_results_index < MAX_PARSE_RESULTS){ + parse_get_results[parse_get_results_index].parameter = parameter; + parse_get_results[parse_get_results_index].value_string = value; + parse_get_results[parse_get_results_index].value_long = value.toInt(); + + // Serial.print(parse_get_results_index); + // Serial.print(":"); + // Serial.print(parse_get_results[parse_get_results_index].parameter); + // Serial.print(":"); + // Serial.print(parse_get_results[parse_get_results_index].value_string); + // Serial.print(":"); + // Serial.print(parse_get_results[parse_get_results_index].value_long); + // Serial.println("$"); + + parse_get_results_index++; + } + } + + +} +#endif //FEATURE_WEB_SERVER + +//------------------------------------------------------------------------------------------------------- +#if defined(FEATURE_WEB_SERVER) + +void web_print_page_main_menu(EthernetClient client){ + + + web_print_header(client); + + web_print_style_sheet(client); + + web_print_title(client); + + web_client_println(client,F("

K3NG CW Keyer




Control

")); + #if defined(FEATURE_MEMORIES) + web_client_println(client,F("Memories

")); + #endif //FEATURE_MEMORIES + web_client_println(client,F("Keyer Settings

")); + #if defined(FEATURE_INTERNET_LINK) + web_client_println(client,F("Link Settings

")); + #endif //FEATURE_INTERNET_LINK + web_client_println(client,F("Network Settings

About
")); + + web_print_footer(client); + +} + +#endif //FEATURE_WEB_SERVER + + +//------------------------------------------------------------------------------------------------------- +#if defined(FEATURE_WEB_SERVER) + +void web_print_control_radio(EthernetClient client,const char *name,int value,uint8_t checked,const char *caption){ + + web_client_print(client,F("")); + +} +#endif //FEATURE_WEB_SERVER + +//------------------------------------------------------------------------------------------------------- +#if defined(FEATURE_WEB_SERVER) + +void web_print_control_checkbox(EthernetClient client,const char *name,uint8_t checked,const char *caption){ + + web_client_print(client,F("")); + +} +#endif //FEATURE_WEB_SERVER + +//------------------------------------------------------------------------------------------------------- +#if defined(FEATURE_WEB_SERVER) + +void web_print_control_textbox(EthernetClient client,const char *name,const char *textbox_class,int textbox_value,const char *front_caption,const char *back_caption){ + + web_client_print(client,F("")); + +} +#endif //FEATURE_WEB_SERVER + +//------------------------------------------------------------------------------------------------------- +#if defined(FEATURE_WEB_SERVER) + +void web_print_control_textbox(EthernetClient client,const char *name,const char *textbox_class,float textbox_value,const char *front_caption,const char *back_caption){ + + web_client_print(client,F("")); + +} +#endif //FEATURE_WEB_SERVER + +//------------------------------------------------------------------------------------------------------- +#if defined(FEATURE_WEB_SERVER) + +void web_print_page_keyer_settings(EthernetClient client){ + + uint8_t pin_read = 0; + + web_print_header(client); + + web_print_style_sheet(client); + + web_print_title(client); + + web_client_println(client,F("

Keyer Settings



")); + + web_print_control_radio(client,"md",IAMBIC_A,(configuration.keyer_mode == IAMBIC_A)?1:0,"Iambic A "); + web_print_control_radio(client,"md",IAMBIC_B,(configuration.keyer_mode == IAMBIC_B)?1:0,"Iambic B "); + web_print_control_radio(client,"md",BUG,(configuration.keyer_mode == BUG)?1:0,"Bug "); + web_print_control_radio(client,"md",STRAIGHT,(configuration.keyer_mode == STRAIGHT)?1:0,"Straight Key"); + #ifndef OPTION_NO_ULTIMATIC + web_print_control_radio(client,"md",ULTIMATIC,(configuration.keyer_mode == ULTIMATIC)?1:0,"Ultimatic"); + #endif + web_print_control_radio(client,"md",SINGLE_PADDLE,(configuration.keyer_mode == SINGLE_PADDLE)?1:0,"Single Paddle"); + web_client_println(client,"
"); + + + #ifdef FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING + web_print_control_checkbox(client,"cs",(configuration.cmos_super_keyer_iambic_b_timing_on)?1:0," CMOS Superkeyer Iambic B Timing "); + web_print_control_textbox(client,"cp","addr",configuration.cmos_super_keyer_iambic_b_timing_percent,"","%"); + web_client_println(client,"
"); + #endif + + //web_print_control_checkbox(client,"di",(!configuration.dit_buffer_off)?1:0," Dit Buffer "); // couldn't get checkboxes working correctly 2016-12-11 + + web_client_print(client,"Dit Buffer"); + web_print_control_radio(client,"di",0,(configuration.dit_buffer_off)?0:1,"On "); + web_print_control_radio(client,"di",1,(configuration.dit_buffer_off)?1:0,"Off "); + web_client_println(client,"
"); + + // web_print_control_checkbox(client,"da",(!configuration.dah_buffer_off)?1:0," Dah Buffer
"); + + web_client_print(client,"Dah Buffer"); + web_print_control_radio(client,"da",0,(configuration.dah_buffer_off)?0:1,"On "); + web_print_control_radio(client,"da",1,(configuration.dah_buffer_off)?1:0,"Off"); + web_client_println(client,"
"); + + web_print_control_radio(client,"sm",SPEED_NORMAL,(speed_mode == SPEED_NORMAL)?1:0,"Normal Speed Mode "); + + web_print_control_textbox(client,"wp","addr",(int)configuration.wpm,""," WPM "); + + #if defined(FEATURE_FARNSWORTH) + web_print_control_textbox(client,"fw","addr",(int)configuration.wpm_farnsworth,""," Farnsworth WPM"); + #endif //FEATURE_FARNSWORTH + + web_client_println(client,"
"); + + web_print_control_radio(client,"sm",SPEED_QRSS,(speed_mode == SPEED_QRSS)?1:0,"QRSS Mode Dit Length "); + web_client_print(client,F("")); + web_client_println(client,F("
")); + + web_client_print(client,F("Sidetone: ")); + web_print_control_radio(client,"st",SIDETONE_ON,(configuration.sidetone_mode == SIDETONE_ON)?1:0,"On "); + web_print_control_radio(client,"st",SIDETONE_OFF,(configuration.sidetone_mode == SIDETONE_OFF)?1:0,"Off "); + web_print_control_radio(client,"st",SIDETONE_PADDLE_ONLY,(configuration.sidetone_mode == SIDETONE_PADDLE_ONLY)?1:0,"Paddle Only "); + web_print_control_textbox(client,"hz","addr",(int)configuration.hz_sidetone,""," hz"); + web_client_println(client,F("
")); + + web_print_control_textbox(client,"dd","addr",float(configuration.dah_to_dit_ratio/100.0),""," Dah/Dit Ratio"); + web_client_println(client,F("
")); + + web_print_control_textbox(client,"wt","addr",(int)configuration.weighting,"Weighting ",""); + web_client_println(client,F("
")); + + #ifdef FEATURE_COMMAND_LINE_INTERFACE + web_print_control_textbox(client,"sn","addr",(int)serial_number,"Serial # ",""); + web_client_println(client,F("
"));; + #endif + + #if defined(FEATURE_POTENTIOMETER) + //web_print_control_textbox(client,"po","addr",(int)pot_value_wpm(),"Potentiometer "," WPM "); + //web_print_control_checkbox(client,"pa",(configuration.pot_activated)?1:0," Active"); + web_client_print(client,"Potentiometer "); + web_print_control_radio(client,"pa",1,(configuration.pot_activated)?1:0,"Active "); + web_print_control_radio(client,"pa",0,(configuration.pot_activated)?0:1,"Inactive"); + web_client_println(client,F("
")); + #endif + + #if defined(FEATURE_AUTOSPACE) + //web_print_control_checkbox(client,"as",(configuration.autospace_active)?1:0," Autospace
"); + web_client_print(client,"Autospace"); + web_print_control_radio(client,"as",1,(configuration.autospace_active)?1:0,"On "); + web_print_control_radio(client,"as",0,(configuration.autospace_active)?0:1,"Off"); + web_client_println(client,"
"); + #endif //FEATURE_AUTOSPACE + + web_print_control_textbox(client,"ws","addr",(int)configuration.length_wordspace,"Wordspace ",""); + web_client_println(client,F("
")); + + web_print_control_textbox(client,"tx","addr",(int)configuration.current_tx,"TX ",""); + web_client_println(client,F("
")); + + #if defined(FEATURE_QLF) + //web_print_control_checkbox(client,"ql",(qlf_active)?1:0," QLF
"); + + web_client_print(client,"QLF"); + web_print_control_radio(client,"ql",1,(qlf_active)?1:0,"On "); + web_print_control_radio(client,"ql",0,(qlf_active)?0:1,"Off"); + web_client_println(client,"
"); + + #endif //FEATURE_QLF + + web_client_println(client,F("

")); + + web_print_home_link(client); + + web_print_footer(client); + +} +#endif //FEATURE_WEB_SERVER + +//------------------------------------------------------------------------------------------------------- + + + +#if defined(FEATURE_WEB_SERVER) + +void web_print_page_keyer_settings_process(EthernetClient client){ + + + uint8_t invalid_data = 0; + + unsigned int ud = 0; + + uint8_t temp_keyer_mode = 0; + uint8_t temp_dit_buffer_off = 0; + uint8_t temp_dah_buffer_off = 0; + uint8_t temp_speed_mode = 0; + unsigned int temp_wpm = 0; + unsigned int temp_qrss_dit_length = 0; + uint8_t temp_sidetone_mode = 0; + unsigned int temp_sidetone_hz = 0; + String temp_string_dit_dah_ratio; + uint8_t temp_weight = 0; + unsigned int temp_serial = 0; + uint8_t temp_wordspace = 0; + uint8_t temp_tx = 0; + + #if defined(FEATURE_QLF) + uint8_t temp_qlf = 0; + #endif //FEATURE_QLF + + #if defined(FEATURE_POTENTIOMETER) + uint8_t temp_pot_activated = 0; + #endif //FEATURE_POTENTIOMETER + + #if defined(FEATURE_AUTOSPACE) + uint8_t temp_autospace_active = 0; + #endif //FEATURE_AUTOSPACE + + #if defined(FEATURE_FARNSWORTH) + unsigned int temp_farnsworth = 0; + #endif //FEATURE_FARNSWORTH + + parse_get(web_server_incoming_string); + + if (parse_get_results_index){ + + + + for (int x = 0; x < parse_get_results_index; x++){ + if (parse_get_results[x].parameter == "md"){temp_keyer_mode = parse_get_results[x].value_long;} + if (parse_get_results[x].parameter == "di"){temp_dit_buffer_off = parse_get_results[x].value_long;} + if (parse_get_results[x].parameter == "da"){temp_dah_buffer_off = parse_get_results[x].value_long;} + if (parse_get_results[x].parameter == "sm"){temp_speed_mode = parse_get_results[x].value_long;} + if (parse_get_results[x].parameter == "wp"){temp_wpm = parse_get_results[x].value_long;} + #if defined(FEATURE_FARNSWORTH) + if (parse_get_results[x].parameter == "fw"){temp_farnsworth = parse_get_results[x].value_long;} + #endif //FEATURE_FARNSWORTH + if (parse_get_results[x].parameter == "qd"){temp_qrss_dit_length = parse_get_results[x].value_long;} + if (parse_get_results[x].parameter == "st"){temp_sidetone_mode = parse_get_results[x].value_long;} + if (parse_get_results[x].parameter == "hz"){temp_sidetone_hz = parse_get_results[x].value_long;} + if (parse_get_results[x].parameter == "dd"){temp_string_dit_dah_ratio = parse_get_results[x].value_string;} + if (parse_get_results[x].parameter == "wt"){temp_weight = parse_get_results[x].value_long;} + if (parse_get_results[x].parameter == "sn"){temp_serial = parse_get_results[x].value_long;} + // po - nothing to do for potentiometer value + #if defined(FEATURE_POTENTIOMETER) + if (parse_get_results[x].parameter == "pa"){temp_pot_activated = parse_get_results[x].value_long;} + #endif //FEATURE_POTENTIOMETER + #if defined(FEATURE_AUTOSPACE) + if (parse_get_results[x].parameter == "as"){temp_autospace_active = parse_get_results[x].value_long;} + #endif //FEATURE_AUTOSPACE + if (parse_get_results[x].parameter == "ws"){temp_wordspace = parse_get_results[x].value_long;} + if (parse_get_results[x].parameter == "tx"){temp_tx = parse_get_results[x].value_long;} + #if defined(FEATURE_QLF) + if (parse_get_results[x].parameter == "ql"){temp_qlf = parse_get_results[x].value_long;} + #endif //FEATURE_QLF + + + } + + + // data validation + + + // TODO ! data validation + + + if (invalid_data){ + + web_print_header(client); + web_print_meta_refresh(client,configuration.ip[0],configuration.ip[1],configuration.ip[2],configuration.ip[3],2); + web_client_println(client,F("\/KeyerSettings'\" />")); + web_print_style_sheet(client); + web_print_title(client); + web_client_println(client,F("
Bad data!
")); + web_print_home_link(client); + web_print_footer(client); + + } else { + + // assign to variables + + configuration.keyer_mode = temp_keyer_mode; + configuration.dit_buffer_off = temp_dit_buffer_off; + configuration.dah_buffer_off = temp_dah_buffer_off; + speed_mode = temp_speed_mode; + configuration.wpm = temp_wpm; + qrss_dit_length = temp_qrss_dit_length; + configuration.sidetone_mode = temp_sidetone_mode; + configuration.hz_sidetone = temp_sidetone_hz; + temp_string_dit_dah_ratio.replace(".",""); + configuration.dah_to_dit_ratio = temp_string_dit_dah_ratio.toInt(); + configuration.weighting = temp_weight; + #if defined(FEATURE_COMMAND_MODE) + serial_number = temp_serial; + #endif + configuration.length_wordspace = temp_wordspace; + configuration.current_tx = temp_tx; + #if defined(FEATURE_QLF) + qlf_active = temp_qlf; + #endif //FEATURE_QLF + #if defined(FEATURE_POTENTIOMETER) + configuration.pot_activated = temp_pot_activated; + #endif //FEATURE_POTENTIOMETER + #if defined(FEATURE_AUTOSPACE) + configuration.autospace_active = temp_autospace_active; + #endif //FEATURE_AUTOSPACE + #if defined(FEATURE_FARNSWORTH) + configuration.wpm_farnsworth = temp_farnsworth; + #endif //FEATURE_FARNSWORTH + + web_print_header(client); + web_print_meta_refresh(client,configuration.ip[0],configuration.ip[1],configuration.ip[2],configuration.ip[3],2); + web_client_println(client,F("\/KeyerSettings'\" />")); + web_print_style_sheet(client); + web_print_title(client); + web_client_println(client,F("
Configuration saved

")); + web_print_home_link(client); + web_print_footer(client); + config_dirty = 1; + } + } +} +#endif //FEATURE_WEB_SERVER + +//------------------------------------------------------------------------------------------------------- + + +#if defined(FEATURE_WEB_SERVER) && defined(FEATURE_MEMORIES) + +void web_print_page_memories(EthernetClient client){ + + + + int memory_number_to_send = 0; + int last_memory_location; + + #if defined(OPTION_PROSIGN_SUPPORT) + byte eeprom_temp = 0; + static char * prosign_temp = 0; + #endif + + + + + web_print_header(client); + + web_print_style_sheet(client); + + web_print_title(client); + + web_client_println(client,F("

Memories



")); + + web_client_print(client,F("

")); + + //if (web_server_incoming_string.length() > 14){web_server_incoming_string.remove(14);} + + if ((web_server_incoming_string.indexOf("?m") > 0) && (web_server_incoming_string.length() > (web_server_incoming_string.indexOf("?m")+2))) { + memory_number_to_send = ((web_server_incoming_string.charAt(web_server_incoming_string.indexOf("?m")+2)-48)*10) + (web_server_incoming_string.charAt(web_server_incoming_string.indexOf("?m")+3)-48); + + +// web_client_print(client,web_server_incoming_string); +// web_client_print(client,F("

")); + +// web_client_print(client,F("mem number: ")); +// web_client_print(client,memory_number_to_send); +// web_client_print(client,F("

")); + +// web_client_print(client,web_server_incoming_string.charAt(web_server_incoming_string.indexOf("?m")+1)); +// web_client_print(client,web_server_incoming_string.charAt(web_server_incoming_string.indexOf("?m")+2)); +// web_client_print(client,F("

")); + + add_to_send_buffer(SERIAL_SEND_BUFFER_MEMORY_NUMBER); + add_to_send_buffer(memory_number_to_send-1); + } + + + for(int i = 0;i < number_of_memories;i++){ + web_client_print(client,F(""); + web_client_print(client,i+1); + + + + last_memory_location = memory_end(i) + 1; + + if (EEPROM.read(memory_start(i)) == 255) { + // web_client_print(client,F("{empty}")); + web_client_print(client,F(" ")); + } else { + web_client_print(client,") "); + for (int y = (memory_start(i)); (y < last_memory_location); y++) { + if (EEPROM.read(y) < 255) { + #if defined(OPTION_PROSIGN_SUPPORT) + eeprom_temp = EEPROM.read(y); + if ((eeprom_temp > PROSIGN_START) && (eeprom_temp < PROSIGN_END)){ + prosign_temp = convert_prosign(eeprom_temp); + web_client_write(client,prosign_temp[0]); + web_client_write(client,prosign_temp[1]); + } else { + web_client_write(client,eeprom_temp); + } + #else + web_client_write(client,EEPROM.read(y)); + #endif //OPTION_PROSIGN_SUPPORT + } else { + y = last_memory_location; + } + } + } + + + web_client_print(client,""); + // web_client_print(client,"


"); + if (number_of_memories > 4){ + if (((i+1) % 4) == 0){web_client_print(client,"


");} + } + } + + web_client_print(client,F("
")); + + web_print_home_link(client); + + web_print_footer(client); + + + + +} +#endif //FEATURE_WEB_SERVER && FEATURE_MEMORIES + + +//------------------------------------------------------------------------------------------------------- + + +#if defined(FEATURE_WEB_SERVER) + +void web_print_page_control(EthernetClient client){ + + /* + + /ctrl - regular page + + /ctrlnd - no display + + /ctrlnd?st/ + + http://192.168.1.178/ctrlnd?sttest/ + + + */ + + uint8_t pin_read = 0; + + #if defined(FEATURE_MEMORIES) + uint8_t memory_number_to_send = 0; + #endif //FEATURE_MEMORIES + + int search_string_start_position = 0; + + String url_sub_string; + + if ((web_server_incoming_string.indexOf("ctrl?") > 0) || (web_server_incoming_string.indexOf("ctrlnd?") > 0)){ + url_sub_string = web_server_incoming_string; + if (url_sub_string.length() > 14){url_sub_string.remove(14);} + if (url_sub_string.indexOf("?ky") > 0){ + sending_mode = AUTOMATIC_SENDING; + web_control_tx_key_time = millis(); + tx_and_sidetone_key(1); + } + if (url_sub_string.indexOf("?uk") > 0){ + sending_mode = AUTOMATIC_SENDING; + tx_and_sidetone_key(0); + web_control_tx_key_time = 0; + } + if (url_sub_string.indexOf("?wn") > 0){ + speed_change(-2); + } + if (url_sub_string.indexOf("?wp") > 0){ + speed_change(2); + } + #if defined(FEATURE_MEMORIES) + if ((web_server_incoming_string.indexOf("?m") > 0) & (web_server_incoming_string.length() > (web_server_incoming_string.indexOf("?m")+2))) { + memory_number_to_send = ((web_server_incoming_string.charAt(web_server_incoming_string.indexOf("m")+1)-48)*10) + (web_server_incoming_string.charAt(web_server_incoming_string.indexOf("m")+2)-48); + add_to_send_buffer(SERIAL_SEND_BUFFER_MEMORY_NUMBER); + add_to_send_buffer(memory_number_to_send-1); + } + #endif //FEATURE_MEMORIES + if (url_sub_string.indexOf("?st") > 0){ + for (int x = (web_server_incoming_string.indexOf("st")+2);x < web_server_incoming_string.length();x++){ + if (web_server_incoming_string.charAt(x) == '/'){ + x = web_server_incoming_string.length(); + } else { + if (web_server_incoming_string.charAt(x) == '%'){ // do we have a http hex code? + add_to_send_buffer((((uint8_t)web_server_incoming_string.charAt(x+1)-48)<<4)+((uint8_t)web_server_incoming_string.charAt(x+2)-48)); + x = x + 2; + } else { + add_to_send_buffer(uppercase(web_server_incoming_string.charAt(x))); + } + } + } + } + } + + + + if (web_server_incoming_string.indexOf("nd") > 0){ // no display option + + web_print_200OK(client); + + } else { + + web_print_header(client); + + web_print_style_sheet(client); + + web_print_title(client); + + web_client_println(client,F("

Control



")); + + +// web_client_print(client,"web_server_incoming_string: "); +// web_client_print(client,web_server_incoming_string); +// web_client_print(client,"url_sub_string: "); +// web_client_print(client,url_sub_string); +// web_client_println(client,F("

")); + + #if defined(FEATURE_MEMORIES) + web_client_print(client,F("

")); + for(int i = 0;i < number_of_memories;i++){ + web_client_print(client,F(""); + web_client_print(client,i+1); + web_client_print(client,""); + if (number_of_memories > 4){ + if (((i+1) % 4) == 0){web_client_print(client,"


");} + } + } + + web_client_print(client,F("
")); + + #endif //FEATURE_MEMORIES + + web_client_println(client,F("
WPM -2WPM +2


")); + + web_client_println(client,F("
KeyUnkey


")); + + web_print_home_link(client); + + web_print_footer(client); + + } + + + +} +#endif //FEATURE_WEB_SERVER + + +//------------------------------------------------------------------------------------------------------- + +#if defined(FEATURE_WEB_SERVER) +void web_client_println(EthernetClient client,const __FlashStringHelper *str){ + + web_client_print(client,str); + client.println(); + +} + +#endif //FEATURE_WEB_SERVER + +//------------------------------------------------------------------------------------------------------- + +#if defined(FEATURE_WEB_SERVER) +void web_client_print(EthernetClient client,const __FlashStringHelper *str){ + + char c; + if(!str) return; + char charstring[255] = ""; + int charstringindex = 0; + + /* since str is a const we can't increment it, so do this instead */ + char *p = (char *)str; + + /* keep going until we find the null */ + while((c = pgm_read_byte(p++))){ + if (charstringindex < 254){ + charstring[charstringindex] = c; + charstringindex++; + } + + } + charstring[charstringindex] = 0; + client.print(charstring); + +} + +#endif //FEATURE_WEB_SERVER + + +//------------------------------------------------------------------------------------------------------- + +#if defined(FEATURE_WEB_SERVER) +void web_client_print(EthernetClient client,String str){ + + client.print(str); + +} + +#endif //FEATURE_WEB_SERVER + +//------------------------------------------------------------------------------------------------------- + +#if defined(FEATURE_WEB_SERVER) +void web_client_print(EthernetClient client,const char *str){ + + client.print(str); + +} + +#endif //FEATURE_WEB_SERVER + +//------------------------------------------------------------------------------------------------------- + +#if defined(FEATURE_WEB_SERVER) +void web_client_println(EthernetClient client,const char *str){ + + client.println(str); + +} + +#endif //FEATURE_WEB_SERVER + +//------------------------------------------------------------------------------------------------------- + +#if defined(FEATURE_WEB_SERVER) +void web_client_print(EthernetClient client,int i){ + + client.print(i); + +} +#endif //FEATURE_WEB_SERVER + + + +//------------------------------------------------------------------------------------------------------- + +#if defined(FEATURE_WEB_SERVER) +void web_client_print(EthernetClient client,float f){ + + client.print(f); + +} +#endif //FEATURE_WEB_SERVER + +//------------------------------------------------------------------------------------------------------- + +#if defined(FEATURE_WEB_SERVER) +void web_client_print(EthernetClient client,unsigned long i){ + + client.print(i); + +} +#endif //FEATURE_WEB_SERVER + +//------------------------------------------------------------------------------------------------------- + +#if defined(FEATURE_WEB_SERVER) +void web_client_print(EthernetClient client,unsigned int i){ + + client.print(i); + +} +#endif //FEATURE_WEB_SERVER + +//------------------------------------------------------------------------------------------------------- + +#if defined(FEATURE_WEB_SERVER) +void web_client_println(EthernetClient client,unsigned long i){ + + client.println(i); + +} +#endif //FEATURE_WEB_SERVER +//------------------------------------------------------------------------------------------------------- + +#if defined(FEATURE_WEB_SERVER) +void web_client_println(EthernetClient client,unsigned long i,int something){ + + client.println(i,something); + +} +#endif //FEATURE_WEB_SERVER +//------------------------------------------------------------------------------------------------------- + +#if defined(FEATURE_WEB_SERVER) +void web_client_write(EthernetClient client,uint8_t i){ + + client.write(i); + +} +#endif //FEATURE_WEB_SERVER + +//------------------------------------------------------------------------------------------------------- +#if defined(FEATURE_WEB_SERVER) + +void web_print_page_link_settings_process(EthernetClient client){ + + uint8_t parsed_link_ip[4][FEATURE_INTERNET_LINK_MAX_LINKS]; + uint8_t parsed_link_enabled[FEATURE_INTERNET_LINK_MAX_LINKS]; + int parsed_link_send_udp_port[FEATURE_INTERNET_LINK_MAX_LINKS]; + int parsed_link_receive_udp_port = 0; + uint8_t parsed_link_receive_enabled = 0; + + + uint8_t invalid_data = 0; + + unsigned int ud = 0; + + parse_get(web_server_incoming_string); + if (parse_get_results_index){ + + for (int x = 0; x < parse_get_results_index; x++){ // TODO - rewrite this to scale... + if (parse_get_results[x].parameter == "ip00"){parsed_link_ip[0][0] = parse_get_results[x].value_long;} + if (parse_get_results[x].parameter == "ip01"){parsed_link_ip[1][0] = parse_get_results[x].value_long;} + if (parse_get_results[x].parameter == "ip02"){parsed_link_ip[2][0] = parse_get_results[x].value_long;} + if (parse_get_results[x].parameter == "ip03"){parsed_link_ip[3][0] = parse_get_results[x].value_long;} + + if (parse_get_results[x].parameter == "ip10"){parsed_link_ip[0][1] = parse_get_results[x].value_long;} + if (parse_get_results[x].parameter == "ip11"){parsed_link_ip[1][1] = parse_get_results[x].value_long;} + if (parse_get_results[x].parameter == "ip12"){parsed_link_ip[2][1] = parse_get_results[x].value_long;} + if (parse_get_results[x].parameter == "ip13"){parsed_link_ip[3][1] = parse_get_results[x].value_long;} + + if (parse_get_results[x].parameter == "ip20"){parsed_link_ip[0][2] = parse_get_results[x].value_long;} + if (parse_get_results[x].parameter == "ip21"){parsed_link_ip[1][2] = parse_get_results[x].value_long;} + if (parse_get_results[x].parameter == "ip22"){parsed_link_ip[2][2] = parse_get_results[x].value_long;} + if (parse_get_results[x].parameter == "ip23"){parsed_link_ip[3][2] = parse_get_results[x].value_long;} + + if (parse_get_results[x].parameter == "act0"){parsed_link_enabled[0] = parse_get_results[x].value_long;} + if (parse_get_results[x].parameter == "act1"){parsed_link_enabled[1] = parse_get_results[x].value_long;} + if (parse_get_results[x].parameter == "act2"){parsed_link_enabled[2] = parse_get_results[x].value_long;} + if (parse_get_results[x].parameter == "act3"){parsed_link_enabled[3] = parse_get_results[x].value_long;} + + if (parse_get_results[x].parameter == "sp0"){parsed_link_send_udp_port[0] = parse_get_results[x].value_long;} + if (parse_get_results[x].parameter == "sp1"){parsed_link_send_udp_port[1] = parse_get_results[x].value_long;} + if (parse_get_results[x].parameter == "sp2"){parsed_link_send_udp_port[2] = parse_get_results[x].value_long;} + if (parse_get_results[x].parameter == "sp3"){parsed_link_send_udp_port[3] = parse_get_results[x].value_long;} + + if (parse_get_results[x].parameter == "ud"){parsed_link_receive_udp_port = parse_get_results[x].value_long;} + if (parse_get_results[x].parameter == "lr"){parsed_link_receive_enabled = parse_get_results[x].value_long;} + + } + + + // data validation + + for (int x = 0;x < FEATURE_INTERNET_LINK_MAX_LINKS;x++){ + if (parsed_link_enabled[x]){ + for (int y = 0;y < 4;y++){ + if ((parsed_link_ip[y][x] < 0) || (parsed_link_ip[y][x] > 255)){ + invalid_data = 1; + } + } + if ((parsed_link_ip[3][x] == 0) || (parsed_link_ip[3][x] == 255) || (parsed_link_ip[0][x] == 0) || (parsed_link_ip[0][x] == 255)){ + invalid_data = 1; + } + if ((parsed_link_send_udp_port[x] < 1) || (parsed_link_send_udp_port[x] > 65535)){ + invalid_data = 1; + } + } + } + + + if (invalid_data){ + + web_print_header(client); + web_print_meta_refresh(client,configuration.ip[0],configuration.ip[1],configuration.ip[2],configuration.ip[3],2); + web_client_println(client,F("\/LinkSettings'\" />")); + web_print_style_sheet(client); + web_print_title(client); + web_client_println(client,F("
Bad data!
")); + web_print_home_link(client); + web_print_footer(client); + + } else { + + for (int x = 0;x < FEATURE_INTERNET_LINK_MAX_LINKS;x++){ + + configuration.link_send_ip[0][x] = parsed_link_ip[0][x]; + configuration.link_send_ip[1][x] = parsed_link_ip[1][x]; + configuration.link_send_ip[2][x] = parsed_link_ip[2][x]; + configuration.link_send_ip[3][x] = parsed_link_ip[3][x]; + + configuration.link_send_udp_port[x] = parsed_link_send_udp_port[x]; + + configuration.link_send_enabled[x] = parsed_link_enabled[x]; + } + + configuration.link_receive_udp_port = parsed_link_receive_udp_port; + configuration.link_receive_enabled = parsed_link_receive_enabled; + + + web_print_header(client); + web_print_meta_refresh(client,configuration.ip[0],configuration.ip[1],configuration.ip[2],configuration.ip[3],5); + web_client_println(client,F("\/LinkSettings'\" />")); + web_print_style_sheet(client); + web_print_title(client); + web_client_println(client,F("
Configuration saved

")); + web_print_home_link(client); + web_print_footer(client); + config_dirty = 1; + } + } +} +#endif //FEATURE_WEB_SERVER + +//------------------------------------------------------------------------------------------------------- +#if defined(FEATURE_WEB_SERVER) + +void web_print_page_network_settings_process(EthernetClient client){ + + uint8_t ip0 = 0; + uint8_t ip1 = 0; + uint8_t ip2 = 0; + uint8_t ip3 = 0; + uint8_t gw0 = 0; + uint8_t gw1 = 0; + uint8_t gw2 = 0; + uint8_t gw3 = 0; + uint8_t sn0 = 0; + uint8_t sn1 = 0; + uint8_t sn2 = 0; + uint8_t sn3 = 0; + + uint8_t invalid_data = 0; + + unsigned int ud = 0; + + parse_get(web_server_incoming_string); + if (parse_get_results_index){ + + for (int x = 0; x < parse_get_results_index; x++){ + if (parse_get_results[x].parameter == "ip0"){ip0 = parse_get_results[x].value_long;} + if (parse_get_results[x].parameter == "ip1"){ip1 = parse_get_results[x].value_long;} + if (parse_get_results[x].parameter == "ip2"){ip2 = parse_get_results[x].value_long;} + if (parse_get_results[x].parameter == "ip3"){ip3 = parse_get_results[x].value_long;} + + if (parse_get_results[x].parameter == "gw0"){gw0 = parse_get_results[x].value_long;} + if (parse_get_results[x].parameter == "gw1"){gw1 = parse_get_results[x].value_long;} + if (parse_get_results[x].parameter == "gw2"){gw2 = parse_get_results[x].value_long;} + if (parse_get_results[x].parameter == "gw3"){gw3 = parse_get_results[x].value_long;} + + if (parse_get_results[x].parameter == "sn0"){sn0 = parse_get_results[x].value_long;} + if (parse_get_results[x].parameter == "sn1"){sn1 = parse_get_results[x].value_long;} + if (parse_get_results[x].parameter == "sn2"){sn2 = parse_get_results[x].value_long;} + if (parse_get_results[x].parameter == "sn3"){sn3 = parse_get_results[x].value_long;} + + + } + + //invalid_data = 1; + + + // data validation + + if ((ip0 == 0) || (ip3 == 255) || (ip3 == 0)) {invalid_data = 1;} + if (((ip0 & sn0) != (gw0 & sn0)) || ((ip1 & sn1) != (gw1 & sn1)) || ((ip2 & sn2) != (gw2 & sn2)) || ((ip3 & sn3) != (gw3 & sn3))) {invalid_data = 1;} + if ((sn0 == 0) || (sn1 > sn0) || (sn2 > sn1) || (sn3 > sn2) || (sn3 > 252)) {invalid_data = 1;} + + if (invalid_data){ + + web_print_header(client); + web_print_style_sheet(client); + web_print_title(client); + web_client_println(client,F("
Bad data!
")); + web_print_home_link(client); + web_print_footer(client); + + } else { + + configuration.ip[0] = ip0; + configuration.ip[1] = ip1; + configuration.ip[2] = ip2; + configuration.ip[3] = ip3; + + configuration.gateway[0] = gw0; + configuration.gateway[1] = gw1; + configuration.gateway[2] = gw2; + configuration.gateway[3] = gw3; + + configuration.subnet[0] = sn0; + configuration.subnet[1] = sn1; + configuration.subnet[2] = sn2; + configuration.subnet[3] = sn3; + + + web_print_header(client); + web_print_meta_refresh(client,ip0,ip1,ip2,ip3,5); + web_client_println(client,F("'\" />")); + web_print_style_sheet(client); + web_print_title(client); + web_client_println(client,F("
Configuration saved
Restarting networking

You will be redirected to new address in 5 seconds...
")); + web_print_home_link(client); + web_print_footer(client); + restart_networking = 1; + config_dirty = 1; + } + } +} +#endif //FEATURE_WEB_SERVER + +//------------------------------------------------------------------------------------------------------- +#if defined(FEATURE_WEB_SERVER) + +void web_print_meta_refresh(EthernetClient client,uint8_t ip0,uint8_t ip1,uint8_t ip2,uint8_t ip3,uint8_t refresh_time){ + + web_client_print(client,F("print("link_key: V"); + #endif //DEBUG_INTERNET_LINKING_SEND + bytes_to_send[0] = 'V'; + add_to_udp_send_buffer(bytes_to_send,1); + buffered_key_down = 0; + } else { + #if defined(DEBUG_INTERNET_LINKING_SEND) + debug_serial_port->print("link_key: U"); + #endif //DEBUG_INTERNET_LINKING_SEND + bytes_to_send[0] = 'U'; + add_to_udp_send_buffer(bytes_to_send,1); + } + } + #if defined(DEBUG_INTERNET_LINKING_SEND) + debug_serial_port->print(millis()-last_link_key_action_time); + #endif //DEBUG_INTERNET_LINKING_SEND + unsigned int number_to_send = millis()-last_link_key_action_time; + if ((number_to_send / 10000) > 0){ + bytes_to_send[0] = (number_to_send / 10000) + 48; + number_to_send = number_to_send % 10000; + bytes_to_send_counter++; + } + if ((number_to_send / 1000) > 0){ + bytes_to_send[bytes_to_send_counter] = (number_to_send / 1000) + 48; + number_to_send = number_to_send % 1000; + bytes_to_send_counter++; + } + if ((number_to_send / 100) > 0){ + bytes_to_send[bytes_to_send_counter] = (number_to_send / 100) + 48; + number_to_send = number_to_send % 100; + bytes_to_send_counter++; + } + if ((number_to_send / 10) > 0){ + bytes_to_send[bytes_to_send_counter] = (number_to_send / 10) + 48; + number_to_send = number_to_send % 10; + bytes_to_send_counter++; + } + bytes_to_send[bytes_to_send_counter] = number_to_send + 48; + bytes_to_send_counter++; + add_to_udp_send_buffer(bytes_to_send,bytes_to_send_counter); + } else { + buffered_key_down = 1; + } + #if defined(DEBUG_INTERNET_LINKING_SEND) + debug_serial_port->println(""); + #endif //DEBUG_INTERNET_LINKING_SEND + current_link_key_state = link_key_state; + last_link_key_action_time = millis(); + } +} + + +#endif //FEATURE_INTERNET_LINK + +//------------------------------------------------------------------------------------------------------- +#if defined(FEATURE_INTERNET_LINK) +void add_to_udp_send_buffer(uint8_t bytes_to_send[8],uint8_t number_of_bytes){ + + for (int x = 0;x < number_of_bytes;x++){ + if (udp_send_buffer_bytes < FEATURE_UDP_SEND_BUFFER_SIZE){ + udp_send_buffer[udp_send_buffer_bytes] = bytes_to_send[x]; + udp_send_buffer_bytes++; + } + } +} + +#endif //FEATURE_INTERNET_LINK +//------------------------------------------------------------------------------------------------------- +#if defined(FEATURE_INTERNET_LINK) +void service_udp_send_buffer(){ + + static uint8_t link_send_buffer[FEATURE_INTERNET_LINK_MAX_LINKS][FEATURE_UDP_SEND_BUFFER_SIZE]; + static uint8_t link_send_buffer_bytes[FEATURE_INTERNET_LINK_MAX_LINKS]; + static uint8_t link_send_buffer_bytes_initialized = 0; + + if (!link_send_buffer_bytes_initialized){ + for (int x = 0;x < FEATURE_INTERNET_LINK_MAX_LINKS;x++){ + link_send_buffer_bytes[x] = 0; + } + link_send_buffer_bytes_initialized = 1; + } + + // load up the bytes sitting in the udp_send_buffer into the individual link buffers + if (udp_send_buffer_bytes){ + for (int y = 0;y < FEATURE_INTERNET_LINK_MAX_LINKS;y++){ // enumerate the individual links + for (int x = 0;x < udp_send_buffer_bytes;x++){ // loop through the bytes in the udp_send_buffer + if (configuration.link_send_enabled[y]){ + if (link_send_buffer_bytes[y] < FEATURE_UDP_SEND_BUFFER_SIZE){ + link_send_buffer[y][link_send_buffer_bytes[y]] = udp_send_buffer[x]; + link_send_buffer_bytes[y]++; + } else { + #if defined(DEBUG_UDP) + debug_serial_port->println("service_udp_send_buffer: link_send_buffer_overflow"); + #endif + } + } + } + } + udp_send_buffer_bytes = 0; + return; + } + + // send out a packet for the first link that has packets in the buffer (don't do them all at once so we don't hog up the CPU) + for (int y = 0;y < FEATURE_INTERNET_LINK_MAX_LINKS;y++){ + if ((configuration.link_send_enabled[y]) && (link_send_buffer_bytes[y])){ + IPAddress ip(configuration.link_send_ip[0][y],configuration.link_send_ip[1][y],configuration.link_send_ip[2][y],configuration.link_send_ip[3][y]); + #if defined(DEBUG_UDP) + debug_serial_port->print(F("service_udp_send_buffer: beginPacket ")); + debug_serial_port->print(configuration.link_send_ip[0][y]); + debug_serial_port->print(F(".")); + debug_serial_port->print(configuration.link_send_ip[1][y]); + debug_serial_port->print(F(".")); + debug_serial_port->print(configuration.link_send_ip[2][y]); + debug_serial_port->print(F(".")); + debug_serial_port->print(configuration.link_send_ip[3][y]); + debug_serial_port->print(F(":")); + debug_serial_port->println(configuration.link_send_udp_port[y]); + #endif + + Udp.beginPacket(ip, configuration.link_send_udp_port[y]); + + for (int x = 0;x < link_send_buffer_bytes[y];x++){ + udp_write(link_send_buffer[y][x]); + } + #if defined(DEBUG_UDP) + debug_serial_port->print("\n\rservice_udp_send_buffer: endPacket "); + unsigned long beginPacket_start = millis(); + #endif + int endpacket_result = Udp.endPacket(); + #if defined(DEBUG_UDP) + unsigned long beginPacket_end = millis(); + if (!endpacket_result){ + debug_serial_port->print("error"); + } else { + debug_serial_port->print("OK"); + } + debug_serial_port->print(" time:"); + debug_serial_port->print(beginPacket_end - beginPacket_start); + debug_serial_port->println(" mS"); + #endif + link_send_buffer_bytes[y] = 0; + y = FEATURE_INTERNET_LINK_MAX_LINKS; // exit after we've process one buffer with bytes + } + + } + +} + +#endif //FEATURE_INTERNET_LINK + +//------------------------------------------------------------------------------------------------------- + +#if defined(FEATURE_UDP) +void udp_write(uint8_t byte_to_write){ + + Udp.write(byte_to_write); + + #if defined(DEBUG_UDP_WRITE) + + static char ascii_sent[17] = ""; + + debug_serial_port->print(" "); + if (byte_to_write < 16){ + debug_serial_port->print("0"); + } + debug_serial_port->print(byte_to_write,HEX); + debug_serial_port->print(" "); + debug_serial_port->write(byte_to_write); + #endif //DEBUG_UDP_WRITE + +} +#endif //FEATURE_UDP + +//------------------------------------------------------------------------------------------------------- + +#if defined(FEATURE_UDP) +void service_udp_receive(){ + + char udp_char_receive_packet_buffer[FEATURE_UDP_RECEIVE_BUFFER_SIZE]; + + + if (configuration.link_receive_enabled){ + int packet_size = Udp.parsePacket(); + if (packet_size) { + + Udp.read(udp_char_receive_packet_buffer, FEATURE_UDP_RECEIVE_BUFFER_SIZE); + + #if defined(DEBUG_UDP_PACKET_RECEIVE) + debug_serial_port->print(F("service_udp_receive: received packet: size ")); + debug_serial_port->print(packet_size); + debug_serial_port->print(" from "); + IPAddress remote = Udp.remoteIP(); + for (int i = 0; i < 4; i++) { + debug_serial_port->print(remote[i], DEC); + if (i < 3) { + debug_serial_port->print("."); + } + } + debug_serial_port->print(":"); + debug_serial_port->print(Udp.remotePort()); + debug_serial_port->print(" contents: "); + for (int x = 0;x < packet_size;x++){ + debug_serial_port->print(udp_char_receive_packet_buffer[x]); + } + debug_serial_port->println("$"); + #endif //DEBUG_UDP + + if (packet_size > FEATURE_UDP_RECEIVE_BUFFER_SIZE){ packet_size = FEATURE_UDP_RECEIVE_BUFFER_SIZE;} + + for (int x = 0; x < packet_size; x++){ + if (udp_receive_packet_buffer_bytes < FEATURE_UDP_RECEIVE_BUFFER_SIZE){ + udp_receive_packet_buffer[udp_receive_packet_buffer_bytes] = udp_char_receive_packet_buffer[x]; + udp_receive_packet_buffer_bytes++; + } + } + } + } +} + +#endif //FEATURE_UDP + +//------------------------------------------------------------------------------------------------------- + +#if defined(FEATURE_UDP) +uint8_t get_udp_receive_buffer_byte(){ + + + if (udp_receive_packet_buffer_bytes){ + + uint8_t byte_to_return = udp_receive_packet_buffer[0]; + + udp_receive_packet_buffer_bytes--; + + if (udp_receive_packet_buffer_bytes){ + for (int x = 0; x < udp_receive_packet_buffer_bytes; x++){ + udp_receive_packet_buffer[x] = udp_receive_packet_buffer[x+1]; + } + } + + #if defined(DEBUG_UDP_PACKET_RECEIVE) + debug_serial_port->print(F("get_udp_receive_buffer_byte: returning: ")); + debug_serial_port->write(byte_to_return); + debug_serial_port->print(F(" udp_receive_packet_buffer_bytes: ")); + debug_serial_port->println(udp_receive_packet_buffer_bytes); + #endif //DEBUG_UDP_PACKET_RECEIVE + + + return byte_to_return; + + } else { + return 0; + } + + +} +#endif //FEATURE_UDP +//------------------------------------------------------------------------------------------------------- +#if defined(FEATURE_UDP) +uint8_t get_udp_receive_buffer_link_command(uint8_t * command,unsigned int * parameter){ + + // this extracts received link commands from the udp_receive_packet_buffer + + uint8_t incoming_byte = 0; + uint8_t return_value = 0; + static uint8_t static_return_value = 0; + static uint8_t command_value = 0; + static uint8_t hit_vdu_command = 0; + static unsigned int parameter_value = 0; + static uint8_t digits = 0; + + static unsigned long last_byte_receive_time = 0; + + if (((millis() - last_byte_receive_time) > 500) && (hit_vdu_command)){ + #if defined(DEBUG_INTERNET_LINKING_RECEIVE) + if (static_return_value){ + debug_serial_port->println(F("get_udp_receive_buffer_link_command: expired buffer")); + } + #endif //DEBUG_INTERNET_LINKING_RECEIVE + parameter_value = 0; + hit_vdu_command = 0; + digits = 0; + //command_value = 0; + static_return_value = 0; + } + + + if (udp_receive_packet_buffer_bytes){ + + for (int x = 0;((x < udp_receive_packet_buffer_bytes) && (static_return_value == 0)); x++){ + incoming_byte = get_udp_receive_buffer_byte(); + last_byte_receive_time = millis(); + #if defined(DEBUG_INTERNET_LINKING_RECEIVE) + // debug_serial_port->print(F("get_udp_receive_buffer_link_command: incoming_byte: ")); + // debug_serial_port->write(incoming_byte); + // debug_serial_port->print(F(" hit_vdu_command: ")); + // debug_serial_port->println(hit_vdu_command); + #endif //DEBUG_INTERNET_LINKING_RECEIVE + if (!hit_vdu_command){ + #if defined(DEBUG_INTERNET_LINKING_RECEIVE) + // debug_serial_port->println(F("get_udp_receive_buffer_link_command: looking for V D U")); + #endif //DEBUG_INTERNET_LINKING_RECEIVE + if ((incoming_byte == 'V') || (incoming_byte == 'D') || (incoming_byte == 'U')) { + command_value = incoming_byte; + hit_vdu_command = 1; + parameter_value = 0; + digits = 0; + #if defined(DEBUG_INTERNET_LINKING_RECEIVE) + // debug_serial_port->println(F("get_udp_receive_buffer_link_command: hit_vdu_command")); + #endif //DEBUG_INTERNET_LINKING_RECEIVE + } + } else { // we've hit a V, D, or U command + #if defined(DEBUG_INTERNET_LINKING_RECEIVE) + // debug_serial_port->println(F("get_udp_receive_buffer_link_command: looking for a number")); + #endif //DEBUG_INTERNET_LINKING_RECEIVE + if ((incoming_byte > 47) && (incoming_byte < 58)){ + parameter_value = (parameter_value * 10) + (incoming_byte - 48); + digits++; + #if defined(DEBUG_INTERNET_LINKING_RECEIVE) + // debug_serial_port->print(F("get_udp_receive_buffer_link_command: parameter_value: ")); + // debug_serial_port->print(parameter_value); + // debug_serial_port->print(F(" digits: ")); + // debug_serial_port->println(digits); + #endif //DEBUG_INTERNET_LINKING_RECEIVE + // peek at next byte to see if we're at the end + service_udp_receive(); + if (((udp_receive_packet_buffer_bytes > 0) && ((udp_receive_packet_buffer[0] == 'V') || (udp_receive_packet_buffer[0] == 'D') || (udp_receive_packet_buffer[0] == 'U'))) || + (udp_receive_packet_buffer_bytes == 0) || (digits > 4)) { + static_return_value = 1; + } + } else { //something bogus came in - reset everything + #if defined(DEBUG_INTERNET_LINKING_RECEIVE) + debug_serial_port->print(F("get_udp_receive_buffer_link_command: reset digits:")); + debug_serial_port->print(digits); + debug_serial_port->print(F(" incoming_byte:")); + debug_serial_port->write(incoming_byte); + debug_serial_port->println(); + #endif //DEBUG_INTERNET_LINKING_RECEIVE + //parameter_value = 0; + //digits = 0; + //command_value = 0; + hit_vdu_command = 0; + } + } + } + + + } + + #if defined(DEBUG_INTERNET_LINKING_RECEIVE) + if (static_return_value){ + debug_serial_port->print(F("get_udp_receive_buffer_link_command: exiting: cmd: ")); + debug_serial_port->write(command_value); + debug_serial_port->print(F(" parameter: ")); + debug_serial_port->println(parameter_value); + } + #endif //DEBUG_INTERNET_LINKING_RECEIVE + + if (static_return_value){ + *command = command_value; + *parameter = parameter_value; + //parameter_value = 0; + //digits = 0; + //command_value = 0; + static_return_value = 0; + hit_vdu_command = 0; + return_value = 1; + } + + return return_value; + + +} +#endif //FEATURE_UDP +//------------------------------------------------------------------------------------------------------- + +#if defined(FEATURE_UDP) +void service_internet_link_udp_receive_buffer(){ + + // Vxxxxx = key down immediately, stay keyed down for xxxxx mS, then key up + // Dxxxxx = key down xxxxx mS after last command + // Uxxxxx = key up xxxxx mS after last command + + + #define LINK_NO_COMMAND 0 + #define LINK_V_COMMAND_IN_PROGRESS 1 + #define LINK_U_COMMAND_BUFFERED 2 + #define LINK_D_COMMAND_BUFFERED 3 + + uint8_t incoming_link_command = 0; + unsigned int incoming_link_command_parameter = 0; + + static uint8_t current_link_control_state = LINK_NO_COMMAND; + static unsigned long v_command_key_down_expire_time = 0; + static unsigned long last_command_completion_time = 0; + static unsigned long buffered_command_execution_time = 0; + static unsigned long key_down_time = 0; + + + if ((key_down_time > 0) && ((millis()-key_down_time) > (FEATURE_INTERNET_LINK_KEY_DOWN_TIMEOUT_SECS * 1000))){ + tx_and_sidetone_key(0); + key_down_time = 0; + } + + + switch(current_link_control_state){ + case LINK_NO_COMMAND: + // is there a command in the buffer, if so read it and execute + if (get_udp_receive_buffer_link_command(&incoming_link_command, &incoming_link_command_parameter)){ + #if defined(DEBUG_INTERNET_LINKING_RECEIVE) + debug_serial_port->print(F("service_internet_link_udp_receive_buffer: incoming_link_command: ")); + debug_serial_port->write(incoming_link_command); + debug_serial_port->print(F(" incoming_link_command_parameter: ")); + debug_serial_port->println(incoming_link_command_parameter); + #endif //DEBUG_INTERNET_LINKING_RECEIVE + if (incoming_link_command == 'V'){ // key down immediately for incoming_link_parameter mS + tx_and_sidetone_key(1); + key_down_time = millis(); + #if defined(DEBUG_INTERNET_LINKING_RECEIVE) + debug_serial_port->println(F("service_internet_link_udp_receive_buffer: LINK_V_COMMAND_IN_PROGRESS tx_and_sidetone_key: 1")); + #endif //DEBUG_INTERNET_LINKING_RECEIVE + v_command_key_down_expire_time = millis() + incoming_link_command_parameter; + current_link_control_state = LINK_V_COMMAND_IN_PROGRESS; + } + if (incoming_link_command == 'U'){ + current_link_control_state = LINK_U_COMMAND_BUFFERED; + buffered_command_execution_time = last_command_completion_time + incoming_link_command_parameter; + #if defined(DEBUG_INTERNET_LINKING_RECEIVE) + debug_serial_port->println(F("service_internet_link_udp_receive_buffer: LINK_U_COMMAND_BUFFERED")); + #endif //DEBUG_INTERNET_LINKING_RECEIVE + } + if (incoming_link_command == 'D'){ + current_link_control_state = LINK_D_COMMAND_BUFFERED; + buffered_command_execution_time = last_command_completion_time + incoming_link_command_parameter; + #if defined(DEBUG_INTERNET_LINKING_RECEIVE) + debug_serial_port->println(F("service_internet_link_udp_receive_buffer: LINK_D_COMMAND_BUFFERED")); + #endif //DEBUG_INTERNET_LINKING_RECEIVE + } + } + break; + case LINK_U_COMMAND_BUFFERED: // key up after last command time has passed + if (millis() >= buffered_command_execution_time){ + tx_and_sidetone_key(0); + key_down_time = 0; + last_command_completion_time = millis(); + current_link_control_state = LINK_NO_COMMAND; + #if defined(DEBUG_INTERNET_LINKING_RECEIVE) + debug_serial_port->println(F("service_internet_link_udp_receive_buffer: LINK_U_COMMAND_BUFFERED->LINK_NO_COMMAND tx_and_sidetone_key: 0")); + #endif //DEBUG_INTERNET_LINKING_RECEIVE + } + break; + case LINK_D_COMMAND_BUFFERED: // key down after last command time has passed + if (millis() >= buffered_command_execution_time){ + tx_and_sidetone_key(1); + key_down_time = millis(); + last_command_completion_time = millis(); + current_link_control_state = LINK_NO_COMMAND; + #if defined(DEBUG_INTERNET_LINKING_RECEIVE) + debug_serial_port->println(F("service_internet_link_udp_receive_buffer: LINK_D_COMMAND_BUFFERED->LINK_NO_COMMAND tx_and_sidetone_key: 1")); + #endif //DEBUG_INTERNET_LINKING_RECEIVE + } + break; + case LINK_V_COMMAND_IN_PROGRESS: // we're in key down, check if it time to key up and complete + if (millis() >= v_command_key_down_expire_time){ + tx_and_sidetone_key(0); + key_down_time = 0; + v_command_key_down_expire_time = 0; + last_command_completion_time = millis(); + current_link_control_state = LINK_NO_COMMAND; + #if defined(DEBUG_INTERNET_LINKING_RECEIVE) + debug_serial_port->println(F("service_internet_link_udp_receive_buffer: LINK_V_COMMAND_IN_PROGRESS->LINK_NO_COMMAND tx_and_sidetone_key: 0")); + #endif //DEBUG_INTERNET_LINKING_RECEIVE + } + break; + + } //switch(current_link_control_state) + +} + +#endif //FEATURE_UDP + +//------------------------------------------------------------------------------------------------------- + +void service_millis_rollover(){ + + static unsigned long last_millis = 0; + + if (millis() < last_millis){ + millis_rollover++; + } + last_millis = millis(); + +} +//------------------------------------------------------------------------------------------------------- +#ifdef OPTION_NON_ENGLISH_EXTENSIONS +byte convert_unicode_to_send_char_code(byte first_byte,byte second_byte){ + + + if (first_byte == 195){ + switch(second_byte){ + case 133: return 197; // Å AA_capital (OZ, LA, SM) + case 134: return 198; // Æ (OZ, LA) + case 152: return 216; // Ø (OZ, LA) + case 128: return 192; // À - A accent + case 132: return 196; // Ä - A_umlaut (D, SM, OH, ...) + case 145: return 209; // Ñ - (EA) + case 150: return 214; // Ö – O_umlaut (D, SM, OH, ...) + case 146: return 211; // Ò - O accent + case 156: return 220; // Ü - U_umlaut (D, ...) + case 135: return 199; // Ç + case 144: return 208; // à + case 136: return 200; // È + case 137: return 201; // É + } + + if (first_byte == 197){ + switch(second_byte){ + case 189: return 142; // Ž + } + } + + } + + return(0); + +} +#endif + +//------------------------------------------------------------------------------------------------------- + +void initialize_sd_card(){ + + #if defined(FEATURE_SD_CARD_SUPPORT) + if (!SD.begin(sd_card_spi_ss_line)) { + #if defined(DEBUG_SD_CARD) + debug_serial_port->println(F("initialize_sd_card: initialization failed")); + #endif + return; + } + + sd_card_state = SD_CARD_AVAILABLE; + + // This causes a problem with directory listing... + // if (!SD.exists("/keyer")){ + // SD.mkdir("/keyer"); + // #if defined(DEBUG_SD_CARD) + // debug_serial_port->println(F("initialize_sd_card: created /keyer")); + // #endif + // } + + if (SD.exists("/keyer/beacon.txt")){ + sd_card_state = SD_CARD_AVAILABLE_BEACON_FILE_FOUND; + } + + #if defined(DEBUG_SD_CARD) + debug_serial_port->println(F("initialize_sd_card: initialization done")); + #endif + + #endif //FEATURE_SD_CARD_SUPPORT + +} + +//------------------------------------------------------------------------------------------------------- +#if defined(FEATURE_SD_CARD_SUPPORT) +void service_sd_card(){ + + static unsigned long last_sd_log_file_save = 0; + + if (sd_card_state == SD_CARD_AVAILABLE_BEACON_FILE_RUNNING){ + if (dit_buffer || dah_buffer){ + sd_card_state = SD_CARD_AVAILABLE; + sdfile.close(); + } else { + if (send_buffer_bytes == 0){ + if (sdfile.available()){ + add_to_send_buffer(uppercase(sdfile.read())); + } else { + sdfile.seek(0); + } + } + } + } + + + if (sd_card_state == SD_CARD_AVAILABLE_BEACON_FILE_FOUND){ + sdfile = SD.open("/keyer/beacon.txt"); + if (sdfile){ + sd_card_state = SD_CARD_AVAILABLE_BEACON_FILE_RUNNING; + } else { + sd_card_state = SD_CARD_ERROR; + } + } + + + if ((sd_card_log_state == SD_CARD_LOG_OPEN) && ((millis() - last_sd_log_file_save) > 60000)){ + sdlogfile.flush(); + last_sd_log_file_save = millis(); + } + +} +#endif //FEATURE_SD_CARD_SUPPORT +//------------------------------------------------------------------------------------------------------- +byte is_visible_character(byte char_in){ + + if((char_in > 31) || (char_in == 9) || (char_in == 10) || (char_in == 13)){ + return 1; + } else { + return 0; + } + +} + + +//------------------------------------------------------------------------------------------------------- +#if defined(FEATURE_SD_CARD_SUPPORT) +void sd_card_log(String string_to_log,byte byte_to_log){ + + char logchar[10]; + + if (sd_card_log_state == SD_CARD_LOG_OPEN){ + if (string_to_log.length() > 0){ + string_to_log.toCharArray(logchar,9); + sdlogfile.print(logchar); + } else { + if (is_visible_character(byte_to_log)){ + sdlogfile.write(byte_to_log); + } + } + } + + + + if ((sd_card_log_state == SD_CARD_LOG_NOT_OPEN) && (sd_card_state == SD_CARD_AVAILABLE)){ + sdlogfile = SD.open("/keyer/keyer.log",FILE_WRITE); + if (!sdlogfile){ + sd_card_log_state = SD_CARD_LOG_ERROR; + } else { + sd_card_log_state = SD_CARD_LOG_OPEN; + sdlogfile.println("\r\nstart of log "); + if (configuration.cli_mode == CLI_MILL_MODE_PADDLE_SEND){ + sdlogfile.print("TX:"); + sdlogfile.flush(); + } else { + sdlogfile.print("RX:"); + sdlogfile.flush(); + } + } + } + +} +#endif //FEATURE_SD_CARD_SUPPORT + +//------------------------------------------------------------------------------------------------------- + +#ifdef FEATURE_SO2R_BASE +void so2r_set_tx() { + if (so2r_tx == SO2R_TX_1) { + if (so2r_tx_1) { + digitalWrite(so2r_tx_1, HIGH); + } + + if (so2r_tx_2) { + digitalWrite(so2r_tx_2, LOW); + } + + current_tx_ptt_line = ptt_tx_1; + current_tx_key_line = tx_key_line_1; + } + else { + if (so2r_tx_1) { + digitalWrite(so2r_tx_1, LOW); + } + + if (so2r_tx_2) { + digitalWrite(so2r_tx_2, HIGH); + } + + if (ptt_tx_2) { + current_tx_ptt_line = ptt_tx_2; + } else { + current_tx_ptt_line = ptt_tx_1; + } + current_tx_key_line = tx_key_line_2; + } +} + +//------------------------------------------------------------------------------------------------------- + +void so2r_set_rx() { + uint8_t rx; + if (so2r_latch && (so2r_ptt || (ptt_line_activated && (sending_mode == AUTOMATIC_SENDING)))) { + if (so2r_tx == 1) { + rx = 2; + } else { + rx = 1; + } + } + else { + rx = so2r_rx; + } + + switch (rx) { + case SO2R_RX_1: + // Receive on radio 1 only + if (so2r_rx_1) { + digitalWrite(so2r_rx_1, HIGH); + } + + if (so2r_rx_1s) { + digitalWrite(so2r_rx_1s, HIGH); + } + + if (so2r_rx_2) { + digitalWrite(so2r_rx_2, LOW); + } + + if (so2r_rx_2s) { + digitalWrite(so2r_rx_2s, LOW); + } + + if (so2r_rx_s) { + digitalWrite(so2r_rx_s, LOW); + } + break; + + case SO2R_RX_2: + // Receive on radio 2 only + if (so2r_rx_1) { + digitalWrite(so2r_rx_1, LOW); + } + + if (so2r_rx_1s) { + digitalWrite(so2r_rx_1s, LOW); + } + + if (so2r_rx_2) { + digitalWrite(so2r_rx_2, HIGH); + } + + if (so2r_rx_2s) { + digitalWrite(so2r_rx_2s, HIGH); + } + + if (so2r_rx_s) { + digitalWrite(so2r_rx_s, LOW); + } + break; + + case SO2R_RX_S: + case SO2R_RX_R: + // Receive on radio 1 and 2 (stereo) + if (so2r_rx_1) { + digitalWrite(so2r_rx_1, LOW); + } + + if (so2r_rx_1s) { + digitalWrite(so2r_rx_1s, HIGH); + } + + if (so2r_rx_2) { + digitalWrite(so2r_rx_2, LOW); + } + + if (so2r_rx_2s) { + digitalWrite(so2r_rx_2s, HIGH); + } + + if (so2r_rx_s) { + digitalWrite(so2r_rx_s, HIGH); + } + break; + } +} + +//------------------------------------------------------------------------------------------------------- + +void so2r_command() { + if ((incoming_serial_byte & 0xf0) == 0x90) + { + // 0 is RX 1 + // 1 is RX 2 + // 2 is RX 1 and RX2 stereo + // 3 is RX 1 and RX2 stereo (reverse if possible but this box doesn't have that capability) + so2r_rx = (incoming_serial_byte & 3) + 1; + so2r_set_rx(); + + byte tx = SO2R_TX_1; + if (incoming_serial_byte & 4) { + tx = SO2R_TX_2; + } + + // Don't switch transmitter while transmitting. + if (tx == so2r_tx) { + so2r_pending_tx = 0; + } + else { + if (ptt_line_activated) { + so2r_pending_tx = tx; + #ifdef FEATURE_WINKEY_EMULATION + if (winkey_sending && winkey_host_open) { + // Fake a paddle interrupt to stop computer sending + winkey_port_write(0xc2|winkey_sending|winkey_xoff,0); // 0xc2 - BREAKIN bit set high + winkey_interrupted = 1; + } + #endif + + } else { + so2r_tx = tx; + so2r_set_tx(); + } + } + return; + } + + #ifdef FEATURE_SO2R_ANTENNA + if ((incoming_serial_byte & 0xf0) == 0xa0) + { + so2r_antenna_1 = incoming_serial_byte & 0x0f; + // TBD: Provide antenna information outputs + return; + } + + if ((incoming_serial_byte & 0xf0) == 0xb0) + { + so2r_antenna_2 = incoming_serial_byte & 0x0f; + // TBD: Provide antenna information outputs + return; + } + #endif //FEATURE_SO2R_ANTENNA + + switch (incoming_serial_byte) { + case 0x80: // SO2R Close + #ifdef FEATURE_SO2R_SWITCHES + so2r_open = 0; + so2r_debounce = 0; + so2r_latch = 0; + #endif + break; + + case 0x81: // SO2R Open + #ifdef FEATURE_SO2R_SWITCHES + so2r_open = 1; + #endif + break; + + case 0x82: // PTT Off + so2r_ptt = 0; + break; + + case 0x83: // PTT On + so2r_ptt = 1; + ptt_key(); + break; + + case 0x84: // Latch Off + so2r_latch = 0; + so2r_set_rx(); + break; + + case 0x85: // Latch On + so2r_latch = 1; + so2r_set_rx(); + break; + } +} + +//------------------------------------------------------------------------------------------------------- + +#ifdef FEATURE_SO2R_SWITCHES + void so2r_switches() + { + if (so2r_open) { + return; + } + + if (so2r_debounce) { + if ((so2r_debounce_time - millis()) < 20) { + return; + } + so2r_debounce = 0; + } + + if (so2r_tx_switch) { + uint8_t tx = 1; + if (digitalRead(so2r_tx_switch) != LOW) { + tx = 2; + } + if (tx != so2r_tx) + { + if (ptt_line_activated) { + #ifdef FEATURE_WINKEY_EMULATION + if (winkey_sending && winkey_host_open) { + // Fake a paddle interrupt to stop computer sending + winkey_port_write(0xc2|winkey_sending|winkey_xoff,0); // 0xc2 - BREAKIN bit set high + winkey_interrupted = 1; + } + #endif + } + else { + so2r_tx = tx; + so2r_set_tx(); + so2r_debounce_time = millis(); + so2r_debounce = 1; + } + } + + if (so2r_rx1_switch) { + uint8_t rx = 1; // RX 1 + if (digitalRead(so2r_rx1_switch) != LOW) { + if (so2r_rx2_switch && (digitalRead(so2r_rx2_switch) != LOW)) { + rx = 3; // Stereo + } + else { + rx = 2; // RX 2 + } + } + + if (rx != so2r_rx) { + so2r_rx = rx; + so2r_set_rx(); + so2r_debounce_time = millis(); + so2r_debounce = 1; + } + } + } + } +#endif // FEATURE_SO2R_SWITCHES +#endif //FEATURE_SO2R_BASE + +//------------------------------------------------------------------------------------------------------- + + +// DL2DBG contributed code (adapted into code by Goody K3NG) +// Based on https://forum.arduino.cc/index.php?topic=446209.15 + + +// #if defined(FEATURE_SINEWAVE_SIDETONE) + +// void compute_sinetone(int hz, int volume){ //dl2dbg + + +// omega = 2*pi*hz ; +// c1 = (8.0 - 2.0*pow(omega*T/1000000.0,2))/(4.0+pow(omega*T/1000000.0,2)); +// #if defined(FEATURE_SINEWAVE_SIDETONE_USING_TIMER_1) +// Timer1.detachInterrupt(); +// #endif +// #if defined(FEATURE_SINEWAVE_SIDETONE_USING_TIMER_3) +// Timer3.detachInterrupt(); +// #endif +// a[0]= 0.0; +// a[1]= volume*sin(omega*T/1000000.0); +// a[2]= 0.0; +// #if defined(FEATURE_SINEWAVE_SIDETONE_USING_TIMER_1) +// Timer1.attachInterrupt(sinewave_interrupt_compute); +// #endif +// #if defined(FEATURE_SINEWAVE_SIDETONE_USING_TIMER_3) +// Timer3.attachInterrupt(sinewave_interrupt_compute); +// #endif + + +// } +// #endif //FEATURE_SINEWAVE_SIDETONE + +//------------------------------------------------------------------------------------------------------- + +// #if defined(FEATURE_SINEWAVE_SIDETONE) + +// void sinewave_interrupt_compute(){ //dl2dbg + +// a[2] = c1 * a[1] - a[0]; +// a[0] = a[1] ; +// a[1] = a[2] ; +// #if defined(FEATURE_SINEWAVE_SIDETONE_USING_TIMER_1) +// Timer1.setPwmDuty(sidetone_line, map( a[2],-512, 512, 0, 1000)); +// #endif +// #if defined(FEATURE_SINEWAVE_SIDETONE_USING_TIMER_3) +// Timer3.setPwmDuty(sidetone_line, map( a[2],-512, 512, 0, 1000)); +// #endif + + + +// } +// #endif //FEATURE_SINEWAVE_SIDETONE + +//------------------------------------------------------------------------------------------------------- +// #if defined(FEATURE_SINEWAVE_SIDETONE) + +// void initialize_tonsin(){ //dl2dbg + +// //configuration.sidetone_volume = sidetone_volume_low_limit + ((sidetone_volume_high_limit - sidetone_volume_low_limit) / 2); +// compute_sinetone(configuration.hz_sidetone,configuration.sidetone_volume); +// #if defined(FEATURE_SINEWAVE_SIDETONE_USING_TIMER_1) +// Timer1.initialize(T); // set sample time for discrete tone signal +// Timer1.pwm(sidetone_line, 0, T); +// Timer1.attachInterrupt(sinewave_interrupt_compute); +// Timer1.stop(); +// #endif +// #if defined(FEATURE_SINEWAVE_SIDETONE_USING_TIMER_3) +// Timer3.initialize(T); // set sample time for discrete tone signal +// Timer3.pwm(sidetone_line, 0, T); +// Timer3.attachInterrupt(sinewave_interrupt_compute); +// Timer3.stop(); +// #endif + + + + + +// } +// #endif //FEATURE_SINEWAVE_SIDETONE +//------------------------------------------------------------------------------------------------------- + +// #if defined(FEATURE_SINEWAVE_SIDETONE) +// void sinetone(uint8_t pin_dummy_variable, unsigned short freq){ //dl2dbg + +// static int last_freq; +// static int last_volume; + +// if ((freq != last_freq) || (configuration.sidetone_volume != last_volume)){ +// compute_sinetone(freq,configuration.sidetone_volume); +// last_freq = freq; +// last_volume = configuration.sidetone_volume; +// } + +// //delay (2); compute_sinetone(freq,sidetone_volume/4); +// //delay (2); compute_sinetone(freq,sidetone_volume/2); +// //compute_sinetone(freq,configuration.sidetone_volume); + +// #if defined(FEATURE_SINEWAVE_SIDETONE_USING_TIMER_1) +// Timer1.restart(); +// #endif +// #if defined(FEATURE_SINEWAVE_SIDETONE_USING_TIMER_3) +// Timer3.restart(); +// #endif + + + + +// } +// #endif //FEATURE_SINEWAVE_SIDETONE + +//------------------------------------------------------------------------------------------------------- + +// #if defined(FEATURE_SINEWAVE_SIDETONE) +// void nosineTone(uint8_t pin_dummy_variable){ // disable tone on specified pin, if any dl2dbg + +// //delay (2); compute_sinetone(freq,sidetone_volume/2); +// //delay (2); compute_sinetone(freq,sidetone_volume/4); +// // compute_sinetone(configuration.hz_sidetone,0); + + +// #if defined(FEATURE_SINEWAVE_SIDETONE_USING_TIMER_1) +// Timer1.stop(); +// #endif +// #if defined(FEATURE_SINEWAVE_SIDETONE_USING_TIMER_3) +// Timer3.stop(); +// #endif + + +// //digitalWrite(sidetone_line,LOW); + +// } + +// #endif //FEATURE_SINEWAVE_SIDETONE + +//------------------------------------------------------------------------------------------------------- + +void debug_blink(){ + #if defined(DEBUG_STARTUP_BLINKS) + digitalWrite(13,HIGH); + delay(250); + digitalWrite(13,LOW); + delay(1000); + #endif //DEBUG_STARTUP +} + +//------------------------------------------------------------------------------------------------------- + +// +// +// Congratulations. You've gotten to the end. But this is just the beginning. +// +// + + + + + + +/* + + + + #ifdef FEATURE_CLOCK + int temp_year = 0; + byte temp_month = 0; + byte temp_day = 0; + byte temp_minute = 0; + byte temp_hour = 0; + byte negative_flag = 0; + #endif // FEATURE_CLOCK + + + + #ifdef FEATURE_CLOCK + case 'C': // show clock + update_time(); + sprintf(return_string, "%s", timezone_modified_clock_string()); + + + break; + case 'O': // set clock UTC time + temp_year = ((input_buffer[2] - 48) * 1000) + ((input_buffer[3] - 48) * 100) + ((input_buffer[4] - 48) * 10) + (input_buffer[5] - 48); + temp_month = ((input_buffer[6] - 48) * 10) + (input_buffer[7] - 48); + temp_day = ((input_buffer[8] - 48) * 10) + (input_buffer[9] - 48); + temp_hour = ((input_buffer[10] - 48) * 10) + (input_buffer[11] - 48); + temp_minute = ((input_buffer[12] - 48) * 10) + (input_buffer[13] - 48); + if ((temp_year > 2013) && (temp_year < 2070) && + (temp_month > 0) && (temp_month < 13) && + (temp_day > 0) && (temp_day < 32) && + (temp_hour >= 0) && (temp_hour < 24) && + (temp_minute >= 0) && (temp_minute < 60) && + (input_buffer_index == 14)) { + + clock_year_set = temp_year; + clock_month_set = temp_month; + clock_day_set = temp_day; + clock_hour_set = temp_hour; + clock_min_set = temp_minute; + clock_sec_set = 0; + millis_at_last_calibration = millis(); + + #if defined(FEATURE_RTC_DS1307) + rtc.adjust(DateTime(temp_year, temp_month, temp_day, temp_hour, temp_minute, 0)); + #endif // defined(FEATURE_RTC_DS1307) + #if defined(FEATURE_RTC_PCF8583) + rtc.year = temp_year; + rtc.month = temp_month; + rtc.day = temp_day; + rtc.hour = temp_hour; + rtc.minute = temp_minute; + rtc.second = 0; + rtc.set_time(); + #endif // defined(FEATURE_RTC_PCF8583) + + #if (!defined(FEATURE_RTC_DS1307) && !defined(FEATURE_RTC_PCF8583)) + strcpy(return_string, "Clock set to "); + update_time(); + strcat(return_string, timezone_modified_clock_string()); + #else + strcpy(return_string, "Internal clock and RTC set to "); + update_time(); + strcat(return_string, timezone_modified_clock_string()); + #endif + } else { + strcpy(return_string, "Error. Usage: \\OYYYYMMDDHHmm"); + } + break; + + case 'V': // \Vx[xxx][.][xxxx] Set time zone offset + negative_flag = 0; + place_multiplier = 1; + for (int x = input_buffer_index - 1; x > 1; x--) { + if (char(input_buffer[x]) == '-') { + negative_flag = 1; + } else { + if (char(input_buffer[x]) != '.') { + tempfloat += (input_buffer[x] - 48) * place_multiplier; + place_multiplier = place_multiplier * 10; + } else { + decimalplace = x; + } + } + } + if (decimalplace) { + tempfloat = tempfloat / pow(10, (input_buffer_index - decimalplace - 1)); + } + if (negative_flag){tempfloat = tempfloat * -1.0;} + if ((tempfloat >= -24.0) && (tempfloat <= 24.0)) { + configuration.clock_timezone_offset = tempfloat; + configuration_dirty = 1; + strcpy(return_string, "Timezone offset set to "); + dtostrf(tempfloat, 0, 2, temp_string); + strcat(return_string, temp_string); + } else { + strcpy(return_string, "Error."); + } + break; + #endif // FEATURE_CLOCK + +// -------------------------------------------------------------- + +#ifdef FEATURE_CLOCK +char * timezone_modified_clock_string(){ + + static char return_string[32] = ""; + char temp_string[16] = ""; + + + dtostrf(local_clock_years, 0, 0, temp_string); + strcpy(return_string, temp_string); + strcat(return_string, "-"); + if (local_clock_months < 10) { + strcat(return_string, "0"); + } + dtostrf(local_clock_months, 0, 0, temp_string); + strcat(return_string, temp_string); + strcat(return_string, "-"); + if (local_clock_days < 10) { + strcat(return_string, "0"); + } + dtostrf(local_clock_days, 0, 0, temp_string); + strcat(return_string, temp_string); + strcat(return_string, " "); + + if (local_clock_hours < 10) { + strcat(return_string, "0"); + } + dtostrf(local_clock_hours, 0, 0, temp_string); + strcat(return_string, temp_string); + strcat(return_string, ":"); + if (local_clock_minutes < 10) { + strcat(return_string, "0"); + } + dtostrf(local_clock_minutes, 0, 0, temp_string); + strcat(return_string, temp_string); + strcat(return_string, ":"); + if (local_clock_seconds < 10) { + strcat(return_string, "0"); + } + dtostrf(local_clock_seconds, 0, 0, temp_string); + strcat(return_string, temp_string); + if (configuration.clock_timezone_offset == 0){ + strcat(return_string,"Z"); + } + return return_string; + +} // clock_string +#endif // FEATURE_CLOCK + +// -------------------------------------------------------------- + +#ifdef FEATURE_CLOCK +char * zulu_clock_string(){ + + static char return_string[32] = ""; + char temp_string[16] = ""; + + + dtostrf(clock_years, 0, 0, temp_string); + strcpy(return_string, temp_string); + strcat(return_string, "-"); + if (clock_months < 10) { + strcat(return_string, "0"); + } + dtostrf(clock_months, 0, 0, temp_string); + strcat(return_string, temp_string); + strcat(return_string, "-"); + if (clock_days < 10) { + strcat(return_string, "0"); + } + dtostrf(clock_days, 0, 0, temp_string); + strcat(return_string, temp_string); + strcat(return_string, " "); + + if (clock_hours < 10) { + strcat(return_string, "0"); + } + dtostrf(clock_hours, 0, 0, temp_string); + strcat(return_string, temp_string); + strcat(return_string, ":"); + if (clock_minutes < 10) { + strcat(return_string, "0"); + } + dtostrf(clock_minutes, 0, 0, temp_string); + strcat(return_string, temp_string); + strcat(return_string, ":"); + if (clock_seconds < 10) { + strcat(return_string, "0"); + } + dtostrf(clock_seconds, 0, 0, temp_string); + strcat(return_string, temp_string); + strcat(return_string,"Z"); + return return_string; + +} // zulu_clock_string +#endif // FEATURE_CLOCK + +// -------------------------------------------------------------- + +#ifdef FEATURE_CLOCK +void update_time(){ + unsigned long runtime = millis() - millis_at_last_calibration; + + // calculate UTC + + unsigned long time = (3600L * clock_hour_set) + (60L * clock_min_set) + clock_sec_set + ((runtime + (runtime * INTERNAL_CLOCK_CORRECTION)) / 1000.0); + + clock_years = clock_year_set; + clock_months = clock_month_set; + clock_days = time / 86400L; + time -= clock_days * 86400L; + clock_days += clock_day_set; + clock_hours = time / 3600L; + + switch (clock_months) { + + case 1: + case 3: + case 5: + case 7: + case 8: + case 10: + case 12: + if (clock_days > 31) { + clock_days = 1; clock_months++; + } + break; + + case 2: + if ((float(clock_years) / 4.0) == 0.0) { // do we have a leap year? + if (clock_days > 29) { + clock_days = 1; clock_months++; + } + } else { + if (clock_days > 28) { + clock_days = 1; clock_months++; + } + } + break; + + case 4: + case 6: + case 9: + case 11: + if (clock_days > 30) { + clock_days = 1; clock_months++; + } + break; + } // switch + + if (clock_months > 12) { + clock_months = 1; clock_years++; + } + + time -= clock_hours * 3600L; + clock_minutes = time / 60L; + time -= clock_minutes * 60L; + clock_seconds = time; + + + // calculate local time + + long local_time = (configuration.clock_timezone_offset * 60L * 60L) + (3600L * clock_hour_set) + (60L * clock_min_set) + clock_sec_set + ((runtime + (runtime * INTERNAL_CLOCK_CORRECTION)) / 1000.0); + + local_clock_years = clock_year_set; + local_clock_months = clock_month_set; + local_clock_days = clock_day_set; + + if (local_time < 0){ + local_time = local_time + (24L * 60L * 60L) - 1; + local_clock_days--; + if (local_clock_days < 1){ + local_clock_months--; + switch (local_clock_months) { + case 0: + local_clock_months = 12; + local_clock_days = 31; + local_clock_years--; + break; + case 1: + case 3: + case 5: + case 7: + case 8: + case 10: + case 12: + local_clock_days = 31; + break; + case 2: //February + if ((float(local_clock_years) / 4.0) == 0.0) { // do we have a leap year? + local_clock_days = 29; + } else { + local_clock_days = 28; + } + break; + case 4: + case 6: + case 9: + case 11: + local_clock_days = 30; + break; + } // switch + } + local_clock_hours = local_time / 3600L; + local_time -= local_clock_hours * 3600L; + local_clock_minutes = local_time / 60L; + local_time -= local_clock_minutes * 60L; + local_clock_seconds = local_time; + + } else { //(local_time < 0) + + local_clock_days = local_time / 86400L; + local_time -= local_clock_days * 86400L; + local_clock_days += clock_day_set; + local_clock_hours = local_time / 3600L; + + switch (local_clock_months) { + + case 1: + case 3: + case 5: + case 7: + case 8: + case 10: + case 12: + if (local_clock_days > 31) { + local_clock_days = 1; + local_clock_months++; + } + break; + + case 2: + if ((float(local_clock_years) / 4.0) == 0.0) { // do we have a leap year? + if (local_clock_days > 29) { + local_clock_days = 1; + local_clock_months++; + } + } else { + if (local_clock_days > 28) { + local_clock_days = 1; + local_clock_months++; + } + } + break; + + case 4: + case 6: + case 9: + case 11: + if (local_clock_days > 30) { + local_clock_days = 1; + local_clock_months++; + } + break; + } // switch + + if (local_clock_months > 12) { + local_clock_months = 1; + local_clock_years++; + } + + local_time -= local_clock_hours * 3600L; + local_clock_minutes = local_time / 60L; + local_time -= local_clock_minutes * 60L; + local_clock_seconds = local_time; + + + } //(local_time < 0) + + +} // update_time +#endif // FEATURE_CLOCK +// -------------------------------------------------------------- +*/ diff --git a/k3ng_keyer/keyer.h b/k3ng_keyer/keyer.h new file mode 100755 index 0000000..a858685 --- /dev/null +++ b/k3ng_keyer/keyer.h @@ -0,0 +1,226 @@ +#ifndef keyer_h +#define keyer_h + +// Do not change these ! + +// Variable macros +#define STRAIGHT 1 +#define IAMBIC_B 2 +#define IAMBIC_A 3 +#define BUG 4 +#define ULTIMATIC 5 +#define SINGLE_PADDLE 6 + +#define PADDLE_NORMAL 0 +#define PADDLE_REVERSE 1 + +#define KEYER_NORMAL 0 +#define BEACON 1 +#define KEYER_COMMAND_MODE 2 +#define KEYER_COMMAND_MODE_SPEED_OVERRIDE 3 + +#define OMIT_LETTERSPACE 1 + +#define SIDETONE_OFF 0 +#define SIDETONE_ON 1 +#define SIDETONE_PADDLE_ONLY 2 + +#define SENDING_NOTHING 0 +#define SENDING_DIT 1 +#define SENDING_DAH 2 + +#define SPEED_NORMAL 0 +#define SPEED_QRSS 1 + +#define CW 0 +#define HELL 1 +#define AMERICAN_MORSE 2 + +#ifdef FEATURE_PS2_KEYBOARD + #define PS2_KEYBOARD_NORMAL 0 +#endif //FEATURE_PS2_KEYBOARD + +#define SERIAL_CLI 0 +#define SERIAL_WINKEY_EMULATION 1 + +#define SERIAL_SEND_BUFFER_SPECIAL_START 13 +#define SERIAL_SEND_BUFFER_WPM_CHANGE 14 +#define SERIAL_SEND_BUFFER_PTT_ON 15 +#define SERIAL_SEND_BUFFER_PTT_OFF 16 +#define SERIAL_SEND_BUFFER_TIMED_KEY_DOWN 17 +#define SERIAL_SEND_BUFFER_TIMED_WAIT 18 +#define SERIAL_SEND_BUFFER_NULL 19 +#define SERIAL_SEND_BUFFER_PROSIGN 20 +#define SERIAL_SEND_BUFFER_HOLD_SEND 21 +#define SERIAL_SEND_BUFFER_HOLD_SEND_RELEASE 22 +#define SERIAL_SEND_BUFFER_MEMORY_NUMBER 23 +#define SERIAL_SEND_BUFFER_TX_CHANGE 24 +#define SERIAL_SEND_BUFFER_SPECIAL_END 25 + +#if defined(OPTION_PROSIGN_SUPPORT) + #define PROSIGN_START 127 + #define PROSIGN_AA 128 + #define PROSIGN_AS 129 + #define PROSIGN_BK 130 + #define PROSIGN_CL 131 + #define PROSIGN_CT 132 + #define PROSIGN_KN 133 + #define PROSIGN_NJ 134 + #define PROSIGN_SK 135 + #define PROSIGN_SN 136 + #define PROSIGN_HH 137 // iz0rus + #define PROSIGN_END 138 // iz0rus +#endif + +#define SERIAL_SEND_BUFFER_NORMAL 0 +#define SERIAL_SEND_BUFFER_TIMED_COMMAND 1 +#define SERIAL_SEND_BUFFER_HOLD 2 + + +#define WINKEY_NO_COMMAND_IN_PROGRESS 0 +#define WINKEY_UNBUFFERED_SPEED_COMMAND 1 +#define WINKEY_UNSUPPORTED_COMMAND 2 +#define WINKEY_POINTER_COMMAND 3 +#define WINKEY_ADMIN_COMMAND 4 +#define WINKEY_PAUSE_COMMAND 5 +#define WINKEY_KEY_COMMAND 6 +#define WINKEY_SETMODE_COMMAND 7 +#define WINKEY_SIDETONE_FREQ_COMMAND 8 +#define WINKEY_ADMIN_COMMAND_ECHO 9 +#define WINKEY_BUFFERED_SPEED_COMMAND 10 +#define WINKEY_DAH_TO_DIT_RATIO_COMMAND 11 +#define WINKEY_KEYING_COMPENSATION_COMMAND 12 +#define WINKEY_FIRST_EXTENSION_COMMAND 13 +#define WINKEY_PTT_TIMES_PARM1_COMMAND 14 +#define WINKEY_PTT_TIMES_PARM2_COMMAND 15 +#define WINKEY_SET_POT_PARM1_COMMAND 16 +#define WINKEY_SET_POT_PARM2_COMMAND 17 +#define WINKEY_SET_POT_PARM3_COMMAND 18 +#define WINKEY_SOFTWARE_PADDLE_COMMAND 19 +//#define WINKEY_CANCEL_BUFFERED_SPEED_COMMAND 20 +#define WINKEY_BUFFFERED_PTT_COMMMAND 21 +#define WINKEY_HSCW_COMMAND 22 +#define WINKEY_BUFFERED_HSCW_COMMAND 23 +#define WINKEY_WEIGHTING_COMMAND 24 +#define WINKEY_KEY_BUFFERED_COMMAND 25 +#define WINKEY_WAIT_BUFFERED_COMMAND 26 +#define WINKEY_POINTER_01_COMMAND 27 +#define WINKEY_POINTER_02_COMMAND 28 +#define WINKEY_POINTER_03_COMMAND 29 +#define WINKEY_FARNSWORTH_COMMAND 30 +#define WINKEY_MERGE_COMMAND 31 +#define WINKEY_MERGE_PARM_2_COMMAND 32 +#define WINKEY_SET_PINCONFIG_COMMAND 33 +#define WINKEY_EXTENDED_COMMAND 34 +#define WINKEY_SEND_MSG 35 +#define WINKEY_LOAD_SETTINGS_PARM_1_COMMAND 101 +#define WINKEY_LOAD_SETTINGS_PARM_2_COMMAND 102 +#define WINKEY_LOAD_SETTINGS_PARM_3_COMMAND 103 +#define WINKEY_LOAD_SETTINGS_PARM_4_COMMAND 104 +#define WINKEY_LOAD_SETTINGS_PARM_5_COMMAND 105 +#define WINKEY_LOAD_SETTINGS_PARM_6_COMMAND 106 +#define WINKEY_LOAD_SETTINGS_PARM_7_COMMAND 107 +#define WINKEY_LOAD_SETTINGS_PARM_8_COMMAND 108 +#define WINKEY_LOAD_SETTINGS_PARM_9_COMMAND 109 +#define WINKEY_LOAD_SETTINGS_PARM_10_COMMAND 110 +#define WINKEY_LOAD_SETTINGS_PARM_11_COMMAND 111 +#define WINKEY_LOAD_SETTINGS_PARM_12_COMMAND 112 +#define WINKEY_LOAD_SETTINGS_PARM_13_COMMAND 113 +#define WINKEY_LOAD_SETTINGS_PARM_14_COMMAND 114 +#define WINKEY_LOAD_SETTINGS_PARM_15_COMMAND 115 + +#define WINKEY_HOUSEKEEPING 0 +#define SERVICE_SERIAL_BYTE 1 + +#define WINKEY_UNBUFFERED_SPEED 0 +#define WINKEY_BUFFERED_SPEED 1 + + +#define UNDEFINED_SENDING 0 +#define AUTOMATIC_SENDING 1 +#define MANUAL_SENDING 2 +#define AUTOMATIC_SENDING_INTERRUPTED 3 + +#define ULTIMATIC_NORMAL 0 +#define ULTIMATIC_DIT_PRIORITY 1 +#define ULTIMATIC_DAH_PRIORITY 2 + + +#define PRINTCHAR 0 +#define NOPRINT 1 + +#define DONT_RAISE_ERROR_MSG 0 +#define RAISE_ERROR_MSG 1 + +#if !defined(HID_PROTOCOL_KEYBOARD) && !defined(HID_PROTOCOL_MOUSE) + #define HID_PROTOCOL_KEYBOARD 1 + #define HID_PROTOCOL_MOUSE 2 +#endif + +#define WORDSWORTH_2_CHAR_WORDS 2 +#define WORDSWORTH_3_CHAR_WORDS 3 +#define WORDSWORTH_4_CHAR_WORDS 4 +#define WORDSWORTH_NAMES 5 +#define WORDSWORTH_QSO_WORDS 6 +#define WORDSWORTH_MIXED 99 + +#define WORDSWORTH_WORDSPACE 1 +#define WORDSWORTH_WPM 2 +#define WORDSWORTH_REPETITION 3 + +#define CALLSIGN_INTERNATIONAL 0 +#define CALLSIGN_US 1 +#define CALLSIGN_EUROPEAN 2 +#define CALLSIGN_CANADA 3 + +#define ECHO_PROGRESSIVE_5 40 +#define ECHO_2_CHAR_WORDS 41 +#define ECHO_3_CHAR_WORDS 42 +#define ECHO_4_CHAR_WORDS 43 +#define ECHO_NAMES 44 +#define ECHO_QSO_WORDS 45 +#define ECHO_MIXED 49 + +#define PRACTICE_NON_INTERACTIVE 0 +#define PRACTICE_INTERACTIVE 1 + +#define RANDOM_LETTER_GROUPS 61 +#define RANDOM_NUMBER_GROUPS 62 +#define RANDOM_MIXED_GROUPS 63 + +#define PRACTICE_2_CHAR_WORDS 51 +#define PRACTICE_3_CHAR_WORDS 52 +#define PRACTICE_4_CHAR_WORDS 53 +#define PRACTICE_NAMES 54 +#define PRACTICE_QSO_WORDS 55 +#define PRACTICE_MIXED 59 + +#define CLI_NORMAL_MODE 0 +#define CLI_MILL_MODE_PADDLE_SEND 1 +#define CLI_MILL_MODE_KEYBOARD_RECEIVE 2 + +#define COMMAND_SPEED_MODE_KEYER_WPM 0 +#define COMMAND_SPEED_MODE_COMMAND_MODE_WPM 1 + +#define SD_CARD_UNINITIALIZED 0 +#define SD_CARD_AVAILABLE 1 +#define SD_CARD_AVAILABLE_BEACON_FILE_FOUND 2 +#define SD_CARD_AVAILABLE_BEACON_FILE_RUNNING 3 +#define SD_CARD_ERROR 254 + +#define SD_CARD_LOG_NOT_OPEN 0 +#define SD_CARD_LOG_OPEN 1 +#define SD_CARD_LOG_ERROR 254 + +#define COMMAND_PL 1 +#define COMMAND_PT 2 +#define COMMAND_PA 3 +#define COMMAND_PI 4 + +#define SO2R_TX_1 1 // Transmitter 1 +#define SO2R_TX_2 2 // Transmitter 2 +#define SO2R_RX_1 1 // Receiver 1 +#define SO2R_RX_2 2 // Receiver 2 +#define SO2R_RX_S 3 // Stereo 1 and 2 +#define SO2R_RX_R 4 // Reverse stereo (implemented as stereo) +#endif //keyer_h \ No newline at end of file diff --git a/k3ng_keyer/keyer_callsign_prefixes.h b/k3ng_keyer/keyer_callsign_prefixes.h new file mode 100755 index 0000000..a3bfe93 --- /dev/null +++ b/k3ng_keyer/keyer_callsign_prefixes.h @@ -0,0 +1,414 @@ + + +const char canadian_prefix_1[] PROGMEM = "VE1"; +const char canadian_prefix_2[] PROGMEM = "VE2"; +const char canadian_prefix_3[] PROGMEM = "VE3"; +const char canadian_prefix_4[] PROGMEM = "VE4"; +const char canadian_prefix_5[] PROGMEM = "VE5"; +const char canadian_prefix_6[] PROGMEM = "VE6"; +const char canadian_prefix_7[] PROGMEM = "VE7"; +const char canadian_prefix_8[] PROGMEM = "VE8"; +const char canadian_prefix_9[] PROGMEM = "VE9"; +const char canadian_prefix_10[] PROGMEM = "VY0"; +const char canadian_prefix_11[] PROGMEM = "VY1"; +const char canadian_prefix_12[] PROGMEM = "VY2"; +const char canadian_prefix_13[] PROGMEM = "VO1"; +const char canadian_prefix_14[] PROGMEM = "VO2"; +const byte canadian_prefix_size = 14; +const char* const canadian_prefix_table[] PROGMEM = + {canadian_prefix_1,canadian_prefix_2,canadian_prefix_3,canadian_prefix_4,canadian_prefix_5,canadian_prefix_6,canadian_prefix_7,canadian_prefix_8,canadian_prefix_9,canadian_prefix_10, + canadian_prefix_11,canadian_prefix_12,canadian_prefix_13,canadian_prefix_14}; + +// European callsign prefix table +// +// Note 2017-05-06: This is weird. If I put "LB" in the array, avrdude trips up with a timeout error when uploading to a Mega. +// That's why LB isn't in this table + +const char eu_prefix_1[] PROGMEM = "3A"; +const char eu_prefix_2[] PROGMEM = "4O"; +const char eu_prefix_3[] PROGMEM = "4U"; +const char eu_prefix_4[] PROGMEM = "9A"; +const char eu_prefix_5[] PROGMEM = "9H"; +const char eu_prefix_6[] PROGMEM = "C3"; +const char eu_prefix_7[] PROGMEM = "CT"; +const char eu_prefix_8[] PROGMEM = "CU"; +const char eu_prefix_9[] PROGMEM = "DA"; +const char eu_prefix_10[] PROGMEM = "DB"; +const char eu_prefix_11[] PROGMEM = "DC"; +const char eu_prefix_12[] PROGMEM = "DD"; +const char eu_prefix_13[] PROGMEM = "DE"; +const char eu_prefix_14[] PROGMEM = "DF"; +const char eu_prefix_15[] PROGMEM = "DG"; +const char eu_prefix_16[] PROGMEM = "DH"; +const char eu_prefix_17[] PROGMEM = "DI"; +const char eu_prefix_18[] PROGMEM = "DJ"; +const char eu_prefix_19[] PROGMEM = "DK"; +const char eu_prefix_20[] PROGMEM = "DL"; +const char eu_prefix_21[] PROGMEM = "EI"; +const char eu_prefix_22[] PROGMEM = "ER"; +const char eu_prefix_23[] PROGMEM = "ES"; +const char eu_prefix_24[] PROGMEM = "EU"; +const char eu_prefix_25[] PROGMEM = "EV"; +const char eu_prefix_26[] PROGMEM = "EW"; +const char eu_prefix_27[] PROGMEM = "F"; +const char eu_prefix_28[] PROGMEM = "G"; +const char eu_prefix_29[] PROGMEM = "GX"; +const char eu_prefix_30[] PROGMEM = "GD"; +const char eu_prefix_31[] PROGMEM = "GT"; +const char eu_prefix_32[] PROGMEM = "GI"; +const char eu_prefix_33[] PROGMEM = "GN"; +const char eu_prefix_34[] PROGMEM = "GJ"; +const char eu_prefix_35[] PROGMEM = "GH"; +const char eu_prefix_36[] PROGMEM = "GM"; +const char eu_prefix_37[] PROGMEM = "GS"; +const char eu_prefix_38[] PROGMEM = "GU"; +const char eu_prefix_39[] PROGMEM = "GP"; +const char eu_prefix_40[] PROGMEM = "GW"; +const char eu_prefix_41[] PROGMEM = "GC"; +const char eu_prefix_42[] PROGMEM = "HB"; +const char eu_prefix_43[] PROGMEM = "HV"; +const char eu_prefix_44[] PROGMEM = "I"; +const char eu_prefix_45[] PROGMEM = "IS"; +const char eu_prefix_46[] PROGMEM = "IM"; +const char eu_prefix_47[] PROGMEM = "JW"; +const char eu_prefix_48[] PROGMEM = "JX"; +const char eu_prefix_49[] PROGMEM = "LX"; +const char eu_prefix_50[] PROGMEM = "LY"; +const char eu_prefix_51[] PROGMEM = "LZ"; +const char eu_prefix_52[] PROGMEM = "OY"; +const char eu_prefix_53[] PROGMEM = "OZ"; +const char eu_prefix_54[] PROGMEM = "S5"; +const char eu_prefix_55[] PROGMEM = "YU"; +const char eu_prefix_56[] PROGMEM = "T7"; +const char eu_prefix_57[] PROGMEM = "T9"; +const char eu_prefix_58[] PROGMEM = "TF"; +const char eu_prefix_59[] PROGMEM = "TK"; +const char eu_prefix_60[] PROGMEM = "EM"; +const char eu_prefix_61[] PROGMEM = "EO"; +const char eu_prefix_62[] PROGMEM = "YL"; +const char eu_prefix_63[] PROGMEM = "YO"; +const char eu_prefix_64[] PROGMEM = "YT"; +const char eu_prefix_65[] PROGMEM = "YU"; +const char eu_prefix_66[] PROGMEM = "YP"; +const char eu_prefix_67[] PROGMEM = "YQ"; +const char eu_prefix_68[] PROGMEM = "YR"; +const char eu_prefix_69[] PROGMEM = "Z3"; +const char eu_prefix_70[] PROGMEM = "ZA"; +const char eu_prefix_71[] PROGMEM = "YZ"; +const char eu_prefix_72[] PROGMEM = "SA"; +const char eu_prefix_73[] PROGMEM = "SB"; +const char eu_prefix_74[] PROGMEM = "SC"; +const char eu_prefix_75[] PROGMEM = "SD"; +const char eu_prefix_76[] PROGMEM = "SE"; +const char eu_prefix_77[] PROGMEM = "SF"; +const char eu_prefix_78[] PROGMEM = "SG"; +const char eu_prefix_79[] PROGMEM = "SH"; +const char eu_prefix_80[] PROGMEM = "SI"; +const char eu_prefix_81[] PROGMEM = "SJ"; +const char eu_prefix_82[] PROGMEM = "SK"; +const char eu_prefix_83[] PROGMEM = "SL"; +const char eu_prefix_84[] PROGMEM = "SM"; +const char eu_prefix_85[] PROGMEM = "SN"; +const char eu_prefix_86[] PROGMEM = "SO"; +const char eu_prefix_87[] PROGMEM = "SP"; +const char eu_prefix_88[] PROGMEM = "SQ"; +const char eu_prefix_89[] PROGMEM = "SR"; +const char eu_prefix_90[] PROGMEM = "SS"; +const char eu_prefix_91[] PROGMEM = "ST"; +const char eu_prefix_92[] PROGMEM = "SU"; +const char eu_prefix_93[] PROGMEM = "SV"; +const char eu_prefix_94[] PROGMEM = "SW"; +const char eu_prefix_95[] PROGMEM = "SX"; +const char eu_prefix_96[] PROGMEM = "SY"; +const char eu_prefix_97[] PROGMEM = "SZ"; +const char eu_prefix_98[] PROGMEM = "OE"; +const char eu_prefix_99[] PROGMEM = "OF"; +const char eu_prefix_100[] PROGMEM = "OG"; +const char eu_prefix_101[] PROGMEM = "OH"; +const char eu_prefix_102[] PROGMEM = "OI"; +const char eu_prefix_103[] PROGMEM = "OJ"; +const char eu_prefix_104[] PROGMEM = "OK"; +const char eu_prefix_105[] PROGMEM = "OL"; +const char eu_prefix_106[] PROGMEM = "OM"; +const char eu_prefix_107[] PROGMEM = "ON"; +const char eu_prefix_108[] PROGMEM = "OO"; +const char eu_prefix_109[] PROGMEM = "OP"; +const char eu_prefix_110[] PROGMEM = "OQ"; +const char eu_prefix_111[] PROGMEM = "OR"; +const char eu_prefix_112[] PROGMEM = "OS"; +const char eu_prefix_113[] PROGMEM = "OT"; +const char eu_prefix_114[] PROGMEM = "PA"; +const char eu_prefix_115[] PROGMEM = "PB"; +const char eu_prefix_116[] PROGMEM = "PC"; +const char eu_prefix_117[] PROGMEM = "PD"; +const char eu_prefix_118[] PROGMEM = "PE"; +const char eu_prefix_119[] PROGMEM = "PF"; +const char eu_prefix_120[] PROGMEM = "PG"; +const char eu_prefix_121[] PROGMEM = "PH"; +const char eu_prefix_122[] PROGMEM = "PI"; +const char eu_prefix_123[] PROGMEM = "UA"; +const char eu_prefix_124[] PROGMEM = "UB"; +const char eu_prefix_125[] PROGMEM = "UC"; +const char eu_prefix_126[] PROGMEM = "UD"; +const char eu_prefix_127[] PROGMEM = "UE"; +const char eu_prefix_128[] PROGMEM = "UF"; +const char eu_prefix_129[] PROGMEM = "UG"; +const char eu_prefix_130[] PROGMEM = "UH"; +const char eu_prefix_131[] PROGMEM = "UI"; +const char eu_prefix_132[] PROGMEM = "RA"; +const char eu_prefix_133[] PROGMEM = "RB"; +const char eu_prefix_134[] PROGMEM = "RC"; +const char eu_prefix_135[] PROGMEM = "RD"; +const char eu_prefix_136[] PROGMEM = "RE"; +const char eu_prefix_137[] PROGMEM = "RF"; +const char eu_prefix_138[] PROGMEM = "RG"; +const char eu_prefix_139[] PROGMEM = "RH"; +const char eu_prefix_140[] PROGMEM = "RI"; +const char eu_prefix_141[] PROGMEM = "RJ"; +const char eu_prefix_142[] PROGMEM = "RK"; +const char eu_prefix_143[] PROGMEM = "RL"; +const char eu_prefix_144[] PROGMEM = "RM"; +const char eu_prefix_145[] PROGMEM = "RN"; +const char eu_prefix_146[] PROGMEM = "RO"; +const char eu_prefix_147[] PROGMEM = "RP"; +const char eu_prefix_148[] PROGMEM = "RQ"; +const char eu_prefix_149[] PROGMEM = "RR"; +const char eu_prefix_150[] PROGMEM = "RS"; +const char eu_prefix_151[] PROGMEM = "RT"; +const char eu_prefix_152[] PROGMEM = "RU"; +const char eu_prefix_153[] PROGMEM = "RV"; +const char eu_prefix_154[] PROGMEM = "RW"; +const char eu_prefix_155[] PROGMEM = "RX"; +const char eu_prefix_156[] PROGMEM = "RY"; +const char eu_prefix_157[] PROGMEM = "RZ"; +const char eu_prefix_158[] PROGMEM = "UR"; +const char eu_prefix_159[] PROGMEM = "US"; +const char eu_prefix_160[] PROGMEM = "UT"; +const char eu_prefix_161[] PROGMEM = "UU"; +const char eu_prefix_162[] PROGMEM = "UV"; +const char eu_prefix_163[] PROGMEM = "UW"; +const char eu_prefix_164[] PROGMEM = "UX"; +const char eu_prefix_165[] PROGMEM = "UY"; +const char eu_prefix_166[] PROGMEM = "UZ"; +const char eu_prefix_167[] PROGMEM = "EA"; +const char eu_prefix_168[] PROGMEM = "EB"; +const char eu_prefix_169[] PROGMEM = "EC"; +const char eu_prefix_170[] PROGMEM = "ED"; +const char eu_prefix_171[] PROGMEM = "EE"; +const char eu_prefix_172[] PROGMEM = "EF"; +const char eu_prefix_173[] PROGMEM = "EG"; +const char eu_prefix_174[] PROGMEM = "EH"; +const char eu_prefix_175[] PROGMEM = "EJ"; +const char eu_prefix_176[] PROGMEM = "HA"; +const char eu_prefix_177[] PROGMEM = "HG"; +const char eu_prefix_178[] PROGMEM = "LA"; +const char eu_prefix_179[] PROGMEM = "LC"; +const char eu_prefix_180[] PROGMEM = "LD"; +const char eu_prefix_181[] PROGMEM = "LE"; +const char eu_prefix_182[] PROGMEM = "LF"; +const char eu_prefix_183[] PROGMEM = "LG"; +const char eu_prefix_184[] PROGMEM = "LH"; +const char eu_prefix_185[] PROGMEM = "LI"; +const char eu_prefix_186[] PROGMEM = "LJ"; +const char eu_prefix_187[] PROGMEM = "LK"; +const char eu_prefix_188[] PROGMEM = "LL"; +const char eu_prefix_189[] PROGMEM = "LM"; +const char eu_prefix_190[] PROGMEM = "LN"; +/*const char eu_prefix_191[] PROGMEM = "LB"; see note above about "LB"*/ +const byte eu_prefix_size = 190; +const char* const eu_prefix_table[] PROGMEM = { + eu_prefix_1, + eu_prefix_2, + eu_prefix_3, + eu_prefix_4, + eu_prefix_5, + eu_prefix_6, + eu_prefix_7, + eu_prefix_8, + eu_prefix_9, + eu_prefix_10, + eu_prefix_11, + eu_prefix_12, + eu_prefix_13, + eu_prefix_14, + eu_prefix_15, + eu_prefix_16, + eu_prefix_17, + eu_prefix_18, + eu_prefix_19, + eu_prefix_20, + eu_prefix_21, + eu_prefix_22, + eu_prefix_23, + eu_prefix_24, + eu_prefix_25, + eu_prefix_26, + eu_prefix_27, + eu_prefix_28, + eu_prefix_29, + eu_prefix_30, + eu_prefix_31, + eu_prefix_32, + eu_prefix_33, + eu_prefix_34, + eu_prefix_35, + eu_prefix_36, + eu_prefix_37, + eu_prefix_38, + eu_prefix_39, + eu_prefix_40, + eu_prefix_41, + eu_prefix_42, + eu_prefix_43, + eu_prefix_44, + eu_prefix_45, + eu_prefix_46, + eu_prefix_47, + eu_prefix_48, + eu_prefix_49, + eu_prefix_50, + eu_prefix_51, + eu_prefix_52, + eu_prefix_53, + eu_prefix_54, + eu_prefix_55, + eu_prefix_56, + eu_prefix_57, + eu_prefix_58, + eu_prefix_59, + eu_prefix_60, + eu_prefix_61, + eu_prefix_62, + eu_prefix_63, + eu_prefix_64, + eu_prefix_65, + eu_prefix_66, + eu_prefix_67, + eu_prefix_68, + eu_prefix_69, + eu_prefix_70, + eu_prefix_71, + eu_prefix_72, + eu_prefix_73, + eu_prefix_74, + eu_prefix_75, + eu_prefix_76, + eu_prefix_77, + eu_prefix_78, + eu_prefix_79, + eu_prefix_80, + eu_prefix_81, + eu_prefix_82, + eu_prefix_83, + eu_prefix_84, + eu_prefix_85, + eu_prefix_86, + eu_prefix_87, + eu_prefix_88, + eu_prefix_89, + eu_prefix_90, + eu_prefix_91, + eu_prefix_92, + eu_prefix_93, + eu_prefix_94, + eu_prefix_95, + eu_prefix_96, + eu_prefix_97, + eu_prefix_98, + eu_prefix_99, + eu_prefix_100, + eu_prefix_101, + eu_prefix_102, + eu_prefix_103, + eu_prefix_104, + eu_prefix_105, + eu_prefix_106, + eu_prefix_107, + eu_prefix_108, + eu_prefix_109, + eu_prefix_110, + eu_prefix_111, + eu_prefix_112, + eu_prefix_113, + eu_prefix_114, + eu_prefix_115, + eu_prefix_116, + eu_prefix_117, + eu_prefix_118, + eu_prefix_119, + eu_prefix_120, + eu_prefix_121, + eu_prefix_122, + eu_prefix_123, + eu_prefix_124, + eu_prefix_125, + eu_prefix_126, + eu_prefix_127, + eu_prefix_128, + eu_prefix_129, + eu_prefix_130, + eu_prefix_131, + eu_prefix_132, + eu_prefix_133, + eu_prefix_134, + eu_prefix_135, + eu_prefix_136, + eu_prefix_137, + eu_prefix_138, + eu_prefix_139, + eu_prefix_140, + eu_prefix_141, + eu_prefix_142, + eu_prefix_143, + eu_prefix_144, + eu_prefix_145, + eu_prefix_146, + eu_prefix_147, + eu_prefix_148, + eu_prefix_149, + eu_prefix_150, + eu_prefix_151, + eu_prefix_152, + eu_prefix_153, + eu_prefix_154, + eu_prefix_155, + eu_prefix_156, + eu_prefix_157, + eu_prefix_158, + eu_prefix_159, + eu_prefix_160, + eu_prefix_161, + eu_prefix_162, + eu_prefix_163, + eu_prefix_164, + eu_prefix_165, + eu_prefix_166, + eu_prefix_167, + eu_prefix_168, + eu_prefix_169, + eu_prefix_170, + eu_prefix_171, + eu_prefix_172, + eu_prefix_173, + eu_prefix_174, + eu_prefix_175, + eu_prefix_176, + eu_prefix_177, + eu_prefix_178, + eu_prefix_179, + eu_prefix_180, + eu_prefix_181, + eu_prefix_182, + eu_prefix_183, + eu_prefix_184, + eu_prefix_185, + eu_prefix_186, + eu_prefix_187, + eu_prefix_188, + eu_prefix_189, + eu_prefix_190/*, + eu_prefix_191*/ + }; + + + diff --git a/k3ng_keyer/keyer_debug.h b/k3ng_keyer/keyer_debug.h new file mode 100755 index 0000000..e08628c --- /dev/null +++ b/k3ng_keyer/keyer_debug.h @@ -0,0 +1,63 @@ +// don't touch these unless you know what the hell you are doing or you're asked to uncomment these for debug dumps +// #define DEBUG_STARTUP +// #define DEBUG_STARTUP_BLINKS +// #define DEBUG_LOOP +// #define DEBUG_EEPROM +// #define DEBUG_MEMORIES +// #define DEBUG_PLAY_MEMORY +// #define DEBUG_SEND_CHAR +// #define DEBUG_MEMORY_WRITE +// #define DEBUG_MEMORYCHECK +// #define DEBUG_MEMORY_LOCATIONS +// #define DEBUG_CAPTURE_COM_PORT +// #define DEBUG_HELL_TEST +// #define DEBUG_WINKEY_PROTOCOL_USING_CW +// #define DEBUG_CHECK_SERIAL +// #define DEBUG_PS2_KEYBOARD +// #define DEBUG_VARIABLE_DUMP +// #define DEBUG_BUTTONS +// #define DEBUG_COMMAND_MODE +// #define DEBUG_GET_CW_INPUT_FROM_USER +// #define DEBUG_POTENTIOMETER +// #define DEBUG_CW_DECODER +// #define DEBUG_CW_DECODER_WPM +// #define DEBUG_SERIAL_SEND_CW_CALLOUT +// #define DEBUG_SLEEP +// #define DEBUG_BACKLIGHT +// #define DEBUG_BUTTON_ARRAY +// #define DEBUG_USB +// #define DEBUG_USB_KEYBOARD +// #define DEBUG_CAPACITIVE_PADDLE +// #define DEBUG_DISPLAY_SCROLL_PRINT_CHAR +// #define DEBUG_WINKEY // <- to use this you must have a multi-serial port Arduino and use FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT +// #define DEBUG_WINKEY_SEND_ERRANT_BYTE +// #define DEBUG_WINKEY_PORT_WRITE +// #define DEBUG_CW_COMPUTER_KEYBOARD +// #define DEBUG_CW_DECODER_WITH_TONE +// #define DEBUG_OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR +// #define DEBUG_FEATURE_COMPETITION_COMPRESSION_DETECTION +// #define DEBUG_FEATURE_STRAIGHT_KEY_ECHO +// #define DEBUG_UDP +// #define DEBUG_UDP_WRITE +// #define DEBUG_WEB_SERVER +// #define DEBUG_WEB_PARSE_GET +// #define DEBUG_WEB_SERVER_READS +// #define DEBUG_INTERNET_LINKING_RECEIVE +// #define DEBUG_INTERNET_LINKING_SEND +// #define DEBUG_UDP_PACKET_RECEIVE +// #define DEBUG_FORCE_RESET +// #define DEBUG_WINKEY_DISABLE_LEAD_IN_TIME_SETTING +// #define DEBUG_KEYPAD_SERIAL +// #define DEBUG_CALLSIGN_PRACTICE_SHOW_CALLSIGN +// #define DEBUG_WORDSWORTH +// #define DEBUG_PRACTICE_SERIAL +// #define DEBUG_PRACTICE_CMD_MODE +// #define DEBUG_SD_CARD +// #define DEBUG_FARNSWORTH +// #define DEBUG_FARNSWORTH_TIMING +// #define DEBUG_ASYNC_EEPROM_WRITE +// #define DEBUG_SERVICE_SEND_BUFFER +// #define DEBUG_EEPROM_READ_SETTINGS +// #define DEBUG_LOOP_ELEMENT_LENGTHS + +// #define OPTION_WINKEY_IGNORE_FIRST_STATUS_REQUEST diff --git a/k3ng_keyer/keyer_dependencies.h b/k3ng_keyer/keyer_dependencies.h new file mode 100755 index 0000000..04326ff --- /dev/null +++ b/k3ng_keyer/keyer_dependencies.h @@ -0,0 +1,49 @@ +#if defined(FEATURE_LCD_4BIT) || defined(FEATURE_LCD_8BIT) || defined(FEATURE_LCD_ADAFRUIT_I2C) || defined(FEATURE_LCD_ADAFRUIT_BACKPACK) || defined(FEATURE_LCD_YDv1) ||defined(FEATURE_LCD1602_N07DH) || defined(FEATURE_LCD_SAINSMART_I2C) || defined(FEATURE_LCD_FABO_PCF8574) || defined(FEATURE_LCD_MATHERTEL_PCF8574) || defined(FEATURE_LCD_HD44780) || defined(FEATURE_LCD_I2C_FDEBRABANDER) + #define FEATURE_DISPLAY +#endif + +#if defined(FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT) && !defined(FEATURE_COMMAND_LINE_INTERFACE) + #define FEATURE_COMMAND_LINE_INTERFACE +#endif + +#if defined(FEATURE_COMMAND_LINE_INTERFACE) || defined(FEATURE_WINKEY_EMULATION) + #define FEATURE_SERIAL +#endif + +//#if defined(HARDWARE_ARDUINO_DUE) && !defined(FEATURE_EEPROM_E24C1024) && defined(FEATURE_MEMORIES) +#if defined(ARDUINO_SAM_DUE) && !defined(FEATURE_EEPROM_E24C1024) && defined(FEATURE_MEMORIES) + #error "In order to use FEATURE_MEMORIES with HARDWARE_ARDUINO_DUE you need FEATURE_EEPROM_E24C1024" +#endif + +#if defined(FEATURE_DISPLAY) || defined(FEATURE_COMMAND_LINE_INTERFACE) || defined(FEATURE_CW_COMPUTER_KEYBOARD) + #define FEATURE_PADDLE_ECHO +#endif + + +#if defined(FEATURE_STRAIGHT_KEY) && (defined(FEATURE_STRAIGHT_KEY_ECHO) || defined(FEATURE_MEMORIES) || defined(FEATURE_CW_COMPUTER_KEYBOARD)) + #define FEATURE_STRAIGHT_KEY_DECODE +#endif + +#if defined(FEATURE_WEB_SERVER) || defined(FEATURE_INTERNET_LINK) + #define FEATURE_ETHERNET +#endif + +#if defined(FEATURE_INTERNET_LINK) + #define FEATURE_UDP +#endif + +#if defined(FEATURE_4x4_KEYPAD) && defined(FEATURE_3x4_KEYPAD) + #error "You can't use both FEATURE_4x4_KEYPAD and FEATURE_3x4_KEYPAD simultaneously" +#endif + +#if defined(FEATURE_TRAINING_COMMAND_LINE_INTERFACE) && defined(OPTION_WORDSWORTH_NORSK) && !defined(OPTION_NON_ENGLISH_EXTENSIONS) + #define OPTION_NON_ENGLISH_EXTENSIONS +#endif + +#if defined(FEATURE_BEACON_SETTING) && !defined(FEATURE_MEMORIES) + #error "FEATURE_BEACON_SETTING requires FEATURE_MEMORIES and you may also want FEATURE_MEMORY_MACROS" +#endif + +#if defined(FEATURE_COMMAND_MODE) && !defined(FEATURE_BUTTONS) + #error "FEATURE_COMMAND_MODE requires FEATURE_BUTTONS" +#endif diff --git a/k3ng_keyer/keyer_features_and_options.h b/k3ng_keyer/keyer_features_and_options.h new file mode 100755 index 0000000..99d12fd --- /dev/null +++ b/k3ng_keyer/keyer_features_and_options.h @@ -0,0 +1,134 @@ +// compile time features and options - comment or uncomment to add or delete features +// FEATURES add more bytes to the compiled binary, OPTIONS change code behavior + + +#define FEATURE_BUTTONS +#define FEATURE_COMMAND_MODE +// #define FEATURE_COMMAND_LINE_INTERFACE // Command Line Interface functionality +// #define FEATURE_MEMORIES // on the Arduino Due, you must have FEATURE_EEPROM_E24C1024 and E24C1024 EEPROM hardware in order to compile this +// #define FEATURE_MEMORY_MACROS +// #define FEATURE_WINKEY_EMULATION // disabling Automatic Software Reset is highly recommended (see documentation) +// #define FEATURE_BEACON // Go into beacon mode if paddle_left pin is LOW at boot up +// #define FEATURE_BEACON_SETTING // Go into beacon mode at boot up if EEPROM setting is enabled (\_ CLI Command) +// #define FEATURE_TRAINING_COMMAND_LINE_INTERFACE +#define FEATURE_POTENTIOMETER // do not enable unless you have a potentiometer connected, otherwise noise will falsely trigger wpm changes +// #define FEATURE_SIDETONE_SWITCH // adds switch control for the sidetone output. requires an external toggle switch (assigned to an arduino pin - see keyer_pin_settings.h). +// #define FEATURE_SIDETONE_NEWTONE // Use the NewTone library, ~1k smaller code size than the standard tone library. Uses timer1 (pins 9 or 10) https://bitbucket.org/teckel12/arduino-new-tone/wiki/Home +// #define FEATURE_SERIAL_HELP +// #define FEATURE_HELL +// #define FEATURE_PS2_KEYBOARD // Use a PS2 keyboard to send code - Change keyboard layout (non-US) in K3NG_PS2Keyboard.h. Additional options below. +// #define FEATURE_USB_KEYBOARD // Use a USB keyboard to send code - Uncomment three lines in k3ng_keyer.ino (search for note_usb_uncomment_lines) +// #define FEATURE_CW_COMPUTER_KEYBOARD // Have an Arduino Due or Leonardo act as a USB HID (Human Interface Device) keyboard and use the paddle to "type" characters on the computer -- uncomment this line in ino file: #include +// #define FEATURE_DEAD_OP_WATCHDOG +// #define FEATURE_AUTOSPACE +// #define FEATURE_FARNSWORTH +// #define FEATURE_DL2SBA_BANKSWITCH // Switch memory banks feature as described here: http://dl2sba.com/index.php?option=com_content&view=article&id=131:nanokeyer&catid=15:shack&Itemid=27#english +// #define FEATURE_LCD_4BIT // classic LCD disidefplay using 4 I/O lines +// #define FEATURE_LCD_8BIT // classic LCD display using 8 I/O lines +// #define FEATURE_LCD_ADAFRUIT_I2C // Adafruit I2C LCD display using MCP23017 at addr 0x20 +// #define FEATURE_LCD_ADAFRUIT_BACKPACK // Adafruit I2C LCD Backup using MCP23008 (courtesy Josiah Ritchie, KE0BLL) +// #define FEATURE_LCD_YDv1 // YourDuino I2C LCD display with old LCM 1602 V1 ic +// #define FEATURE_LCD1602_N07DH // http://linksprite.com/wiki/index.php5?title=16_X_2_LCD_Keypad_Shield_for_Arduino +// #define FEATURE_LCD_SAINSMART_I2C +// #define FEATURE_LCD_FABO_PCF8574 // https://github.com/FaBoPlatform/FaBoLCD-PCF8574-Library +// #define FEATURE_LCD_MATHERTEL_PCF8574 // https://github.com/mathertel/LiquidCrystal_PCF8574 +// #define FEATURE_LCD_I2C_FDEBRABANDER //https://github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library +#define FEATURE_LCD_HD44780 +// #define FEATURE_CW_DECODER // https://github.com/k3ng/k3ng_cw_keyer/wiki/385-Feature:-CW-Decoder +// #define FEATURE_SLEEP // go to sleep after x minutes to conserve battery power (not compatible with Arduino DUE, may have mixed results with Mega and Mega ADK) +// #define FEATURE_LCD_BACKLIGHT_AUTO_DIM // turn off LCD backlight and/or dim Power Indicator LED after x minutes (LED requires a PWM pin) +// #define FEATURE_ROTARY_ENCODER // rotary encoder speed control +// #define FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING +// #define FEATURE_USB_MOUSE // Uncomment three lines in k3ng_keyer.ino (search for note_usb_uncomment_lines) +// #define FEATURE_CAPACITIVE_PADDLE_PINS // remove the bypass capacitors on the paddle_left and paddle_right lines when using capactive paddles +// #define FEATURE_LED_RING // Mayhew Labs Led Ring support +// #define FEATURE_ALPHABET_SEND_PRACTICE // enables command mode S command - created by Ryan, KC2ZWM +// #define FEATURE_COMMAND_MODE_PROGRESSIVE_5_CHAR_ECHO_PRACTICE // enables command mode U +// #define FEATURE_PTT_INTERLOCK +// #define FEATURE_QLF +// #define FEATURE_EEPROM_E24C1024 +// #define FEATURE_STRAIGHT_KEY +// #define FEATURE_DYNAMIC_DAH_TO_DIT_RATIO +// #define FEATURE_PADDLE_ECHO // you may also need to comment out line 19 in the file keyer_dependencies.h +// #define FEATURE_STRAIGHT_KEY_ECHO +// #define FEATURE_AMERICAN_MORSE +// #define FEATURE_4x4_KEYPAD // code contributed by Jack, W0XR - documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/380-Feature:-Keypad +// #define FEATURE_3x4_KEYPAD // code contributed by Jack, W0XR - documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/380-Feature:-Keypad +// #define FEATURE_SEQUENCER +// #define FEATURE_WEB_SERVER // Details: https://github.com/k3ng/k3ng_cw_keyer/wiki/390-Feature:-Ethernet,-Web-Server,-and-Internet-Linking +// #define FEATURE_INTERNET_LINK // Details: https://github.com/k3ng/k3ng_cw_keyer/wiki/390-Feature:-Ethernet,-Web-Server,-and-Internet-Linking + +// #define FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT // Activate the Command Line interface on the secondary serial port +#define OPTION_PRIMARY_SERIAL_PORT_DEFAULT_WINKEY_EMULATION // Use when activating both FEATURE_WINKEY_EMULATION and FEATURE_COMMAND_LINE_INTERFACE + // simultaneously. This will make Winkey emulation be the default at boot up; + // hold command button down at boot up to activate CLI mode + +// #define OPTION_SUPPRESS_SERIAL_BOOT_MSG +#define OPTION_INCLUDE_PTT_TAIL_FOR_MANUAL_SENDING +#define OPTION_EXCLUDE_PTT_HANG_TIME_FOR_MANUAL_SENDING +// #define OPTION_WINKEY_DISCARD_BYTES_AT_STARTUP // if ASR is not disabled, you may need this to discard errant serial port bytes at startup +// #define OPTION_WINKEY_STRICT_EEPROM_WRITES_MAY_WEAR_OUT_EEPROM // with this activated the unit will write non-volatile settings to EEPROM when set by Winkey commands +// #define OPTION_WINKEY_SEND_WORDSPACE_AT_END_OF_BUFFER +#define OPTION_WINKEY_STRICT_HOST_OPEN // require an admin host open Winkey command before doing any other commands +#define OPTION_WINKEY_2_SUPPORT // comment out to revert to Winkey version 1 emulation +#define OPTION_WINKEY_INTERRUPTS_MEMORY_REPEAT +//#define OPTION_WINKEY_UCXLOG_9600_BAUD // use this only with UCXLog configured for Winkey 9600 baud mode +#define OPTION_WINKEY_2_HOST_CLOSE_NO_SERIAL_PORT_RESET // (Required for Win-Test to function) +// #define OPTION_WINKEY_FREQUENT_STATUS_REPORT // activate this to make Winkey emulation play better with RUMlog and RUMped +#define OPTION_WINKEY_IGNORE_LOWERCASE // Enable for typical K1EL Winkeyer behavior (use for SkookumLogger version 1.10.14 and prior to workaround "r" bug) +// #define OPTION_WINKEY_BLINK_PTT_ON_HOST_OPEN +// #define OPTION_WINKEY_SEND_VERSION_ON_HOST_CLOSE +// #define OPTION_WINKEY_PINCONFIG_PTT_CONTROLS_PTT_LINE // Have Winkeyer PTT setting activate/deactivate PTT line rather than control buffered character PTT hold +// #define OPTION_REVERSE_BUTTON_ORDER // This is mainly for the DJ0MY NanoKeyer http://nanokeyer.wordpress.com/ +#define OPTION_PROG_MEM_TRIM_TRAILING_SPACES // trim trailing spaces from memory when programming in command mode +#define OPTION_DIT_PADDLE_NO_SEND_ON_MEM_RPT // this makes dit paddle memory interruption a little smoother +// #define OPTION_MORE_DISPLAY_MSGS // additional optional display messages - comment out to save memory +// #define OPTION_WATCHDOG_TIMER // this enables a four second ATmega48/88/168/328 watchdog timer; use for unattended/remote operation only +// #define OPTION_MOUSE_MOVEMENT_PADDLE // experimental (just fooling around) - mouse movement will act like a paddle +// #define OPTION_NON_ENGLISH_EXTENSIONS // add support for additional CW characters (i.e. À, Å, Þ, etc.) +// #define OPTION_DISPLAY_NON_ENGLISH_EXTENSIONS // LCD display suport for non-English (NO/DK/DE) characters - Courtesy of OZ1JHM +// #define OPTION_UNKNOWN_CHARACTER_ERROR_TONE +// #define OPTION_DO_NOT_SAY_HI +// #define OPTION_PS2_NON_ENGLISH_CHAR_LCD_DISPLAY_SUPPORT // makes some non-English characters from the PS2 keyboard display correctly in the LCD display (donated by Marcin sp5iou) +// #define OPTION_PS2_KEYBOARD_RESET // reset the PS2 keyboard upon startup with 0xFF (contributed by Bill, W9BEL) +// #define OPTION_SAVE_MEMORY_NANOKEYER +#define OPTION_CW_KEYBOARD_CAPSLOCK_BEEP +// #define OPTION_CW_KEYBOARD_ITALIAN +// #define OPTION_CW_KEYBOARD_GERMAN +// #define OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR // https://github.com/k3ng/k3ng_cw_keyer/wiki/385-Feature:-CW-Decoder +// #define OPTION_INVERT_PADDLE_PIN_LOGIC +// #define OPTION_ADVANCED_SPEED_DISPLAY //enables "nerd" speed visualization on display: wpm, cpm (char per min), duration of dit and dah in milliseconds and ratio (contributed by Giorgio, IZ2XBZ) +// #define OPTION_PROSIGN_SUPPORT // additional prosign support for paddle and straight key echo on display, CLI, and in memory storage +// #define OPTION_RUSSIAN_LANGUAGE_SEND_CLI // Russian language CLI sending support (contributed by Павел Бирюков, UA1AQC) +#define OPTION_DO_NOT_SEND_UNKNOWN_CHAR_QUESTION +// #define OPTION_CMOS_SUPER_KEYER_IAMBIC_B_TIMING_ON_BY_DEFAULT +// #define OPTION_SIDETONE_DIGITAL_OUTPUT_NO_SQUARE_WAVE +// #define FEATURE_SD_CARD_SUPPORT +// #define FEATURE_SO2R_BASE // SO2R Box base protocol extensions +// #define FEATURE_SO2R_SWITCHES // SO2R Box TX and RX switches +// #define FEATURE_SO2R_ANTENNA // SO2R Box antenna selection (not fully implemented) +// #define FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT + +// #define OPTION_DIRECT_PADDLE_PIN_READS_MEGA // only works with Mega and pins 2 and 5 - minor performance increase +// #define OPTION_DIRECT_PADDLE_PIN_READS_UNO // Unos or Nanos pins 2 and 5 - do not enable on a nanoKeyer, it uses different pins + +// #define OPTION_WORDSWORTH_CZECH +// #define OPTION_WORDSWORTH_DEUTSCH +// #define OPTION_WORDSWORTH_NORSK + +#define OPTION_EXCLUDE_EXTENDED_CLI_COMMANDS + +// #define OPTION_DFROBOT_LCD_COMMAND_BUTTONS + +// #define OPTION_EXCLUDE_MILL_MODE +// #define OPTION_NO_ULTIMATIC // reduce memory usage by removing ultimatic code. + +// #define OPTION_DISABLE_SERIAL_PORT_CHECKING_WHILE_SENDING_CW + +// #define OPTION_PERSONALIZED_STARTUP_SCREEN // displays a user defined string of characters on the second or fourth row of the screen during startup. 1602 display requires OPTION_DO_NOT_SAY_HI +// #define OPTION_SWAP_PADDLE_PARAMETER_CHANGE_DIRECTION // reverses the up/down direction when using the paddles to change the wpm or sidetone frequency +#define OPTION_DISPLAY_MEMORY_CONTENTS_COMMAND_MODE +// #define OPTION_BEACON_MODE_MEMORY_REPEAT_TIME // to space out the repeated playing of memory 1 when in beacon mode +// #define OPTION_BEACON_MODE_PTT_TAIL_TIME // adds the ptt tail time to each playing of memory 1 in beacon mode + +// #define OPTION_WINKEY_PROSIGN_COMPATIBILITY // Additional character mappings to support K1EL Winkey emulation prosigns diff --git a/k3ng_keyer/keyer_features_and_options_fk_10.h b/k3ng_keyer/keyer_features_and_options_fk_10.h new file mode 100755 index 0000000..798846b --- /dev/null +++ b/k3ng_keyer/keyer_features_and_options_fk_10.h @@ -0,0 +1,148 @@ +// compile time features and options - comment or uncomment to add or delete features +// FEATURES add more bytes to the compiled binary, OPTIONS change code behavior + +// ######## ## ## ## ## ## ## ######## ## ## ######## ######## +// ## ## ## ### ## ## ## ## ## ## ## ## ## +// ## ## ## #### ## ## ## ## #### ## ## ## +// ###### ## ## ## ## ## ##### ###### ## ###### ######## +// ## ## ## ## #### ## ## ## ## ## ## ## +// ## ## ## ## ### ## ## ## ## ## ## ## +// ## ####### ## ## ## ## ######## ## ######## ## ## + + +// ######## ## ## ## ##### +// ## ## ## #### ## ## +// ## ## ## ## ## ## +// ###### ##### ## ## ## +// ## ## ## ## ## ## +// ## ## ## ## ## ## +// ## ## ## ###### ##### + +// Contributed by Disneysw + +#define FEATURE_BUTTONS +#define FEATURE_COMMAND_MODE +#define FEATURE_COMMAND_LINE_INTERFACE // Command Line Interface functionality +#define FEATURE_MEMORIES // on the Arduino Due, you must have FEATURE_EEPROM_E24C1024 and E24C1024 EEPROM hardware in order to compile this +#define FEATURE_MEMORY_MACROS +#define FEATURE_WINKEY_EMULATION // disabling Automatic Software Reset is highly recommended (see documentation) +#define FEATURE_BEACON // Go into beacon mode if paddle_left pin is LOW at boot up +// #define FEATURE_BEACON_SETTING // Go into beacon mode at boot up if EEPROM setting is enabled (\_ CLI Command) +#define FEATURE_TRAINING_COMMAND_LINE_INTERFACE +// #define FEATURE_POTENTIOMETER // do not enable unless you have a potentiometer connected, otherwise noise will falsely trigger wpm changes +// #define FEATURE_SIDETONE_SWITCH // adds switch control for the sidetone output. requires an external toggle switch (assigned to an arduino pin - see keyer_pin_settings.h). +// #define FEATURE_SIDETONE_NEWTONE // Use the NewTone library, ~1k smaller code size than the standard tone library. Uses timer1 (pins 9 or 10) https://bitbucket.org/teckel12/arduino-new-tone/wiki/Home +#define FEATURE_SERIAL_HELP +#define FEATURE_HELL +// #define FEATURE_PS2_KEYBOARD // Use a PS2 keyboard to send code - Change keyboard layout (non-US) in K3NG_PS2Keyboard.h. Additional options below. +#define FEATURE_USB_KEYBOARD // Use a USB keyboard to send code - Uncomment three lines in k3ng_keyer.ino (search for note_usb_uncomment_lines) +// #define FEATURE_CW_COMPUTER_KEYBOARD // Have an Arduino Due or Leonardo act as a USB HID (Human Interface Device) keyboard and use the paddle to "type" characters on the computer +#define FEATURE_DEAD_OP_WATCHDOG +#define FEATURE_AUTOSPACE +#define FEATURE_FARNSWORTH +// #define FEATURE_DL2SBA_BANKSWITCH // Switch memory banks feature as described here: http://dl2sba.com/index.php?option=com_content&view=article&id=131:nanokeyer&catid=15:shack&Itemid=27#english +#define FEATURE_LCD_4BIT // classic LCD disidefplay using 4 I/O lines +// #define FEATURE_LCD_8BIT // classic LCD display using 8 I/O lines +// #define FEATURE_LCD_ADAFRUIT_I2C // Adafruit I2C LCD display using MCP23017 at addr 0x20 +// #define FEATURE_LCD_ADAFRUIT_BACKPACK // Adafruit I2C LCD Backup using MCP23008 (courtesy Josiah Ritchie, KE0BLL) +// #define FEATURE_LCD_YDv1 // YourDuino I2C LCD display with old LCM 1602 V1 ic +// #define FEATURE_LCD1602_N07DH // http://linksprite.com/wiki/index.php5?title=16_X_2_LCD_Keypad_Shield_for_Arduino +// #define FEATURE_LCD_SAINSMART_I2C +// #define FEATURE_LCD_FABO_PCF8574 // https://github.com/FaBoPlatform/FaBoLCD-PCF8574-Library +// #define FEATURE_LCD_I2C_FDEBRABANDER //https://github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library +#define FEATURE_CW_DECODER // https://github.com/k3ng/k3ng_cw_keyer/wiki/385-Feature:-CW-Decoder +// #define FEATURE_SLEEP // go to sleep after x minutes to conserve battery power (not compatible with Arduino DUE, may have mixed results with Mega and Mega ADK) +// #define FEATURE_LCD_BACKLIGHT_AUTO_DIM // turn off LCD backlight and/or dim Power Indicator LED after x minutes (LED requires a PWM pin) +#define FEATURE_ROTARY_ENCODER // rotary encoder speed control +#define FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING +#define FEATURE_USB_MOUSE // Uncomment three lines in k3ng_keyer.ino (search for note_usb_uncomment_lines) +// #define FEATURE_CAPACITIVE_PADDLE_PINS // remove the bypass capacitors on the paddle_left and paddle_right lines when using capactive paddles +// #define FEATURE_LED_RING // Mayhew Labs Led Ring support +#define FEATURE_ALPHABET_SEND_PRACTICE // enables command mode S command - created by Ryan, KC2ZWM +//#define FEATURE_COMMAND_MODE_PROGRESSIVE_5_CHAR_ECHO_PRACTICE // enables command mode U +// #define FEATURE_PTT_INTERLOCK +// #define FEATURE_QLF +// #define FEATURE_EEPROM_E24C1024 +// #define FEATURE_STRAIGHT_KEY +// #define FEATURE_DYNAMIC_DAH_TO_DIT_RATIO +// #define FEATURE_PADDLE_ECHO +// #define FEATURE_STRAIGHT_KEY_ECHO +// #define FEATURE_AMERICAN_MORSE +// #define FEATURE_4x4_KEYPAD // code contributed by Jack, W0XR - documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/380-Feature:-Keypad +// #define FEATURE_3x4_KEYPAD // code contributed by Jack, W0XR - documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/380-Feature:-Keypad +// #define FEATURE_SEQUENCER +// #define FEATURE_SD_CARD_SUPPORT +// #define FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT +// #define FEATURE_WEB_SERVER // Details: https://github.com/k3ng/k3ng_cw_keyer/wiki/390-Feature:-Ethernet,-Web-Server,-and-Internet-Linking +// #define FEATURE_INTERNET_LINK // Details: https://github.com/k3ng/k3ng_cw_keyer/wiki/390-Feature:-Ethernet,-Web-Server,-and-Internet-Linking + +// #define FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT // Activate the Command Line interface on the secondary serial port +#define OPTION_PRIMARY_SERIAL_PORT_DEFAULT_WINKEY_EMULATION // Use when activating both FEATURE_WINKEY_EMULATION and FEATURE_COMMAND_LINE_INTERFACE + // simultaneously. This will make Winkey emulation be the default at boot up; + // hold command button down at boot up to activate CLI mode + +// #define OPTION_SUPPRESS_SERIAL_BOOT_MSG +#define OPTION_INCLUDE_PTT_TAIL_FOR_MANUAL_SENDING +#define OPTION_EXCLUDE_PTT_HANG_TIME_FOR_MANUAL_SENDING +// #define OPTION_WINKEY_DISCARD_BYTES_AT_STARTUP // if ASR is not disabled, you may need this to discard errant serial port bytes at startup +// #define OPTION_WINKEY_STRICT_EEPROM_WRITES_MAY_WEAR_OUT_EEPROM // with this activated the unit will write non-volatile settings to EEPROM when set by Winkey commands +// #define OPTION_WINKEY_SEND_WORDSPACE_AT_END_OF_BUFFER +#define OPTION_WINKEY_STRICT_HOST_OPEN // require an admin host open Winkey command before doing any other commands +#define OPTION_WINKEY_2_SUPPORT // comment out to revert to Winkey version 1 emulation +#define OPTION_WINKEY_INTERRUPTS_MEMORY_REPEAT +//#define OPTION_WINKEY_UCXLOG_9600_BAUD // use this only with UCXLog configured for Winkey 9600 baud mode +#define OPTION_WINKEY_2_HOST_CLOSE_NO_SERIAL_PORT_RESET // (Required for Win-Test to function) +// #define OPTION_WINKEY_FREQUENT_STATUS_REPORT // activate this to make Winkey emulation play better with RUMlog and RUMped +#define OPTION_WINKEY_IGNORE_LOWERCASE // Enable for typical K1EL Winkeyer behavior (use for SkookumLogger version 1.10.14 and prior to workaround "r" bug) +// #define OPTION_WINKEY_BLINK_PTT_ON_HOST_OPEN +// #define OPTION_WINKEY_PINCONFIG_PTT_CONTROLS_PTT_LINE // Have Winkeyer PTT setting activate/deactivate PTT line rather than control buffered character PTT hold +// #define OPTION_REVERSE_BUTTON_ORDER // This is mainly for the DJ0MY NanoKeyer http://nanokeyer.wordpress.com/ +// #define OPTION_WINKEY_SEND_VERSION_ON_HOST_CLOSE +#define OPTION_PROG_MEM_TRIM_TRAILING_SPACES // trim trailing spaces from memory when programming in command mode +#define OPTION_DIT_PADDLE_NO_SEND_ON_MEM_RPT // this makes dit paddle memory interruption a little smoother +#define OPTION_MORE_DISPLAY_MSGS // additional optional display messages - comment out to save memory +#define OPTION_N1MM_WINKEY_TAB_BUG_WORKAROUND // enable this to ignore the TAB key in the Send CW window (this breaks SO2R functionality in N1MM) +#define OPTION_WATCHDOG_TIMER // this enables a four second ATmega48/88/168/328 watchdog timer; use for unattended/remote operation only +#define OPTION_MOUSE_MOVEMENT_PADDLE // experimental (just fooling around) - mouse movement will act like a paddle +// #define OPTION_NON_ENGLISH_EXTENSIONS // add support for additional CW characters (i.e. À, Å, Þ, etc.) +// #define OPTION_DISPLAY_NON_ENGLISH_EXTENSIONS // LCD display suport for non-English (NO/DK/DE) characters - Courtesy of OZ1JHM +// #define OPTION_UNKNOWN_CHARACTER_ERROR_TONE +// #define OPTION_DO_NOT_SAY_HI +// #define OPTION_PS2_NON_ENGLISH_CHAR_LCD_DISPLAY_SUPPORT // makes some non-English characters from the PS2 keyboard display correctly in the LCD display (donated by Marcin sp5iou) +// #define OPTION_PS2_KEYBOARD_RESET // reset the PS2 keyboard upon startup with 0xFF (contributed by Bill, W9BEL) +// #define OPTION_SAVE_MEMORY_NANOKEYER +// #define OPTION_CW_KEYBOARD_CAPSLOCK_BEEP +// #define OPTION_CW_KEYBOARD_ITALIAN +// #define OPTION_CW_KEYBOARD_GERMAN +#define OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR +// #define OPTION_INVERT_PADDLE_PIN_LOGIC +#define OPTION_ADVANCED_SPEED_DISPLAY //enables "nerd" speed visualization on display: wpm, cpm (char per min), duration of dit and dah in milliseconds and ratio (contributed by Giorgio, IZ2XBZ) +#define OPTION_PROSIGN_SUPPORT // additional prosign support for paddle and straight key echo on display, CLI, and in memory storage +// #define OPTION_RUSSIAN_LANGUAGE_SEND_CLI // Russian language CLI sending support (contributed by Павел Бирюков, UA1AQC) +#define OPTION_DO_NOT_SEND_UNKNOWN_CHAR_QUESTION +// #define OPTION_CMOS_SUPER_KEYER_IAMBIC_B_TIMING_ON_BY_DEFAULT +// #define OPTION_SIDETONE_DIGITAL_OUTPUT_NO_SQUARE_WAVE + +// #define OPTION_DIRECT_PADDLE_PIN_READS_MEGA // only works with Mega and pins 2 and 5 - minor performance increase +// #define OPTION_DIRECT_PADDLE_PIN_READS_UNO // Unos or Nanos pins 2 and 5 - do not enable on a nanoKeyer, it uses different pins + +// #define OPTION_WORDSWORTH_CZECH +// #define OPTION_WORDSWORTH_DEUTSCH +// #define OPTION_WORDSWORTH_NORSK + +#define OPTION_EXCLUDE_EXTENDED_CLI_COMMANDS + +// #define OPTION_DFROBOT_LCD_COMMAND_BUTTONS + +// #define OPTION_EXCLUDE_MILL_MODE +// #define OPTION_NO_ULTIMATIC // reduce memory usage by removing ultimatic code. + +// #define OPTION_DISABLE_SERIAL_PORT_CHECKING_WHILE_SENDING_CW +// #define OPTION_PERSONALIZED_STARTUP_SCREEN // displays a user defined string of characters on the second or fourth row of the screen during startup. 1602 display requires OPTION_DO_NOT_SAY_HI +// #define OPTION_SWAP_PADDLE_PARAMETER_CHANGE_DIRECTION // reverses the up/down direction when using the paddles to change the wpm or sidetone frequency +// #define OPTION_DISPLAY_MEMORY_CONTENTS_COMMAND_MODE + +// #define OPTION_BEACON_MODE_MEMORY_REPEAT_TIME // to space out the repeated playing of memory 1 when in beacon mode +// #define OPTION_BEACON_MODE_PTT_TAIL_TIME // adds the ptt tail time to each playing of memory 1 in beacon mode + +// #define OPTION_WINKEY_PROSIGN_COMPATIBILITY // Additional character mappings to support K1EL Winkey emulation prosigns \ No newline at end of file diff --git a/k3ng_keyer/keyer_features_and_options_fk_11.h b/k3ng_keyer/keyer_features_and_options_fk_11.h new file mode 100755 index 0000000..add5def --- /dev/null +++ b/k3ng_keyer/keyer_features_and_options_fk_11.h @@ -0,0 +1,135 @@ +// compile time features and options - comment or uncomment to add or delete features +// FEATURES add more bytes to the compiled binary, OPTIONS change code behavior + +// Funtronics FK-11 + +#define FEATURE_BUTTONS +#define FEATURE_COMMAND_MODE +#define FEATURE_COMMAND_LINE_INTERFACE // Command Line Interface functionality +#define FEATURE_MEMORIES // on the Arduino Due, you must have FEATURE_EEPROM_E24C1024 and E24C1024 EEPROM hardware in order to compile this +#define FEATURE_MEMORY_MACROS +#define FEATURE_WINKEY_EMULATION // disabling Automatic Software Reset is highly recommended (see documentation) +#define FEATURE_BEACON // Go into beacon mode if paddle_left pin is LOW at boot up +#define FEATURE_BEACON_SETTING // Go into beacon mode at boot up if EEPROM setting is enabled (\_ CLI Command) +#define FEATURE_TRAINING_COMMAND_LINE_INTERFACE +// #define FEATURE_POTENTIOMETER // do not enable unless you have a potentiometer connected, otherwise noise will falsely trigger wpm changes +// #define FEATURE_SIDETONE_SWITCH // adds switch control for the sidetone output. requires an external toggle switch (assigned to an arduino pin - see keyer_pin_settings.h). +// #define FEATURE_SIDETONE_NEWTONE // Use the NewTone library, ~1k smaller code size than the standard tone library. Uses timer1 (pins 9 or 10) https://bitbucket.org/teckel12/arduino-new-tone/wiki/Home +#define FEATURE_SERIAL_HELP +#define FEATURE_HELL +// #define FEATURE_PS2_KEYBOARD // Use a PS2 keyboard to send code - Change keyboard layout (non-US) in K3NG_PS2Keyboard.h. Additional options below. +#define FEATURE_USB_KEYBOARD // Use a USB keyboard to send code - Uncomment three lines in k3ng_keyer.ino (search for note_usb_uncomment_lines) +// #define FEATURE_CW_COMPUTER_KEYBOARD // Have an Arduino Due or Leonardo act as a USB HID (Human Interface Device) keyboard and use the paddle to "type" characters on the computer -- uncomment this line in ino file: #include +#define FEATURE_DEAD_OP_WATCHDOG +#define FEATURE_AUTOSPACE +#define FEATURE_FARNSWORTH +// #define FEATURE_DL2SBA_BANKSWITCH // Switch memory banks feature as described here: http://dl2sba.com/index.php?option=com_content&view=article&id=131:nanokeyer&catid=15:shack&Itemid=27#english +// #define FEATURE_LCD_4BIT // classic LCD disidefplay using 4 I/O lines +// #define FEATURE_LCD_8BIT // classic LCD display using 8 I/O lines +// #define FEATURE_LCD_ADAFRUIT_I2C // Adafruit I2C LCD display using MCP23017 at addr 0x20 +// #define FEATURE_LCD_ADAFRUIT_BACKPACK // Adafruit I2C LCD Backup using MCP23008 (courtesy Josiah Ritchie, KE0BLL) +// #define FEATURE_LCD_YDv1 // YourDuino I2C LCD display with old LCM 1602 V1 ic +// #define FEATURE_LCD1602_N07DH // http://linksprite.com/wiki/index.php5?title=16_X_2_LCD_Keypad_Shield_for_Arduino +// #define FEATURE_LCD_SAINSMART_I2C +#define FEATURE_LCD_FABO_PCF8574 // https://github.com/FaBoPlatform/FaBoLCD-PCF8574-Library +// #define FEATURE_LCD_MATHERTEL_PCF8574 // https://github.com/mathertel/LiquidCrystal_PCF8574 +// #define FEATURE_LCD_I2C_FDEBRABANDER //https://github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library +// #define FEATURE_LCD_HD44780 +#define FEATURE_CW_DECODER // https://github.com/k3ng/k3ng_cw_keyer/wiki/385-Feature:-CW-Decoder +// #define FEATURE_SLEEP // go to sleep after x minutes to conserve battery power (not compatible with Arduino DUE, may have mixed results with Mega and Mega ADK) +// #define FEATURE_LCD_BACKLIGHT_AUTO_DIM // turn off LCD backlight and/or dim Power Indicator LED after x minutes (LED requires a PWM pin) +#define FEATURE_ROTARY_ENCODER // rotary encoder speed control +#define FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING +#define FEATURE_USB_MOUSE // Uncomment three lines in k3ng_keyer.ino (search for note_usb_uncomment_lines) +// #define FEATURE_CAPACITIVE_PADDLE_PINS // remove the bypass capacitors on the paddle_left and paddle_right lines when using capactive paddles +// #define FEATURE_LED_RING // Mayhew Labs Led Ring support +#define FEATURE_ALPHABET_SEND_PRACTICE // enables command mode S command - created by Ryan, KC2ZWM +// #define FEATURE_COMMAND_MODE_PROGRESSIVE_5_CHAR_ECHO_PRACTICE // enables command mode U +// #define FEATURE_PTT_INTERLOCK +// #define FEATURE_QLF +// #define FEATURE_EEPROM_E24C1024 +// #define FEATURE_STRAIGHT_KEY +// #define FEATURE_DYNAMIC_DAH_TO_DIT_RATIO +// #define FEATURE_PADDLE_ECHO +// #define FEATURE_STRAIGHT_KEY_ECHO +// #define FEATURE_AMERICAN_MORSE +// #define FEATURE_4x4_KEYPAD // code contributed by Jack, W0XR - documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/380-Feature:-Keypad +// #define FEATURE_3x4_KEYPAD // code contributed by Jack, W0XR - documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/380-Feature:-Keypad +// #define FEATURE_SEQUENCER +// #define FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT +// #define FEATURE_WEB_SERVER // Details: https://github.com/k3ng/k3ng_cw_keyer/wiki/390-Feature:-Ethernet,-Web-Server,-and-Internet-Linking +// #define FEATURE_INTERNET_LINK // Details: https://github.com/k3ng/k3ng_cw_keyer/wiki/390-Feature:-Ethernet,-Web-Server,-and-Internet-Linking + +#define FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT // Activate the Command Line interface on the secondary serial port +#define OPTION_PRIMARY_SERIAL_PORT_DEFAULT_WINKEY_EMULATION // Use when activating both FEATURE_WINKEY_EMULATION and FEATURE_COMMAND_LINE_INTERFACE + // simultaneously. This will make Winkey emulation be the default at boot up; + // hold command button down at boot up to activate CLI mode + +// #define OPTION_SUPPRESS_SERIAL_BOOT_MSG +#define OPTION_INCLUDE_PTT_TAIL_FOR_MANUAL_SENDING +#define OPTION_EXCLUDE_PTT_HANG_TIME_FOR_MANUAL_SENDING +// #define OPTION_WINKEY_DISCARD_BYTES_AT_STARTUP // if ASR is not disabled, you may need this to discard errant serial port bytes at startup +// #define OPTION_WINKEY_STRICT_EEPROM_WRITES_MAY_WEAR_OUT_EEPROM // with this activated the unit will write non-volatile settings to EEPROM when set by Winkey commands +// #define OPTION_WINKEY_SEND_WORDSPACE_AT_END_OF_BUFFER +#define OPTION_WINKEY_STRICT_HOST_OPEN // require an admin host open Winkey command before doing any other commands +#define OPTION_WINKEY_2_SUPPORT // comment out to revert to Winkey version 1 emulation +#define OPTION_WINKEY_INTERRUPTS_MEMORY_REPEAT +//#define OPTION_WINKEY_UCXLOG_9600_BAUD // use this only with UCXLog configured for Winkey 9600 baud mode +#define OPTION_WINKEY_2_HOST_CLOSE_NO_SERIAL_PORT_RESET // (Required for Win-Test to function) +// #define OPTION_WINKEY_FREQUENT_STATUS_REPORT // activate this to make Winkey emulation play better with RUMlog and RUMped +#define OPTION_WINKEY_IGNORE_LOWERCASE // Enable for typical K1EL Winkeyer behavior (use for SkookumLogger version 1.10.14 and prior to workaround "r" bug) +// #define OPTION_WINKEY_BLINK_PTT_ON_HOST_OPEN +// #define OPTION_WINKEY_SEND_VERSION_ON_HOST_CLOSE +// #define OPTION_WINKEY_PINCONFIG_PTT_CONTROLS_PTT_LINE // Have Winkeyer PTT setting activate/deactivate PTT line rather than control buffered character PTT hold +// #define OPTION_REVERSE_BUTTON_ORDER // This is mainly for the DJ0MY NanoKeyer http://nanokeyer.wordpress.com/ +#define OPTION_PROG_MEM_TRIM_TRAILING_SPACES // trim trailing spaces from memory when programming in command mode +#define OPTION_DIT_PADDLE_NO_SEND_ON_MEM_RPT // this makes dit paddle memory interruption a little smoother +#define OPTION_MORE_DISPLAY_MSGS // additional optional display messages - comment out to save memory +#define OPTION_WATCHDOG_TIMER // this enables a four second ATmega48/88/168/328 watchdog timer; use for unattended/remote operation only +#define OPTION_MOUSE_MOVEMENT_PADDLE // experimental (just fooling around) - mouse movement will act like a paddle +// #define OPTION_NON_ENGLISH_EXTENSIONS // add support for additional CW characters (i.e. À, Å, Þ, etc.) +// #define OPTION_DISPLAY_NON_ENGLISH_EXTENSIONS // LCD display suport for non-English (NO/DK/DE) characters - Courtesy of OZ1JHM +// #define OPTION_UNKNOWN_CHARACTER_ERROR_TONE +// #define OPTION_DO_NOT_SAY_HI +// #define OPTION_PS2_NON_ENGLISH_CHAR_LCD_DISPLAY_SUPPORT // makes some non-English characters from the PS2 keyboard display correctly in the LCD display (donated by Marcin sp5iou) +// #define OPTION_PS2_KEYBOARD_RESET // reset the PS2 keyboard upon startup with 0xFF (contributed by Bill, W9BEL) +// #define OPTION_SAVE_MEMORY_NANOKEYER +// #define OPTION_CW_KEYBOARD_CAPSLOCK_BEEP +// #define OPTION_CW_KEYBOARD_ITALIAN +// #define OPTION_CW_KEYBOARD_GERMAN +#define OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR +// #define OPTION_INVERT_PADDLE_PIN_LOGIC +#define OPTION_ADVANCED_SPEED_DISPLAY //enables "nerd" speed visualization on display: wpm, cpm (char per min), duration of dit and dah in milliseconds and ratio (contributed by Giorgio, IZ2XBZ) +#define OPTION_PROSIGN_SUPPORT // additional prosign support for paddle and straight key echo on display, CLI, and in memory storage +// #define OPTION_RUSSIAN_LANGUAGE_SEND_CLI // Russian language CLI sending support (contributed by Павел Бирюков, UA1AQC) +#define OPTION_DO_NOT_SEND_UNKNOWN_CHAR_QUESTION +// #define OPTION_CMOS_SUPER_KEYER_IAMBIC_B_TIMING_ON_BY_DEFAULT +// #define OPTION_SIDETONE_DIGITAL_OUTPUT_NO_SQUARE_WAVE +// #define FEATURE_SD_CARD_SUPPORT +// #define FEATURE_SO2R_BASE // SO2R Box base protocol extensions +// #define FEATURE_SO2R_SWITCHES // SO2R Box TX and RX switches +// #define FEATURE_SO2R_ANTENNA // SO2R Box antenna selection (not fully implemented) + +#define OPTION_DIRECT_PADDLE_PIN_READS_MEGA // only works with Mega and pins 2 and 5 - minor performance increase +// #define OPTION_DIRECT_PADDLE_PIN_READS_UNO // Unos or Nanos pins 2 and 5 - do not enable on a nanoKeyer, it uses different pins + +// #define OPTION_WORDSWORTH_CZECH +// #define OPTION_WORDSWORTH_DEUTSCH +// #define OPTION_WORDSWORTH_NORSK + +#define OPTION_EXCLUDE_EXTENDED_CLI_COMMANDS + +// #define OPTION_DFROBOT_LCD_COMMAND_BUTTONS + +// #define OPTION_EXCLUDE_MILL_MODE +// #define OPTION_NO_ULTIMATIC // reduce memory usage by removing ultimatic code. + +// #define OPTION_DISABLE_SERIAL_PORT_CHECKING_WHILE_SENDING_CW +// #define OPTION_PERSONALIZED_STARTUP_SCREEN // displays a user defined string of characters on the second or fourth row of the screen during startup. 1602 display requires OPTION_DO_NOT_SAY_HI +// #define OPTION_SWAP_PADDLE_PARAMETER_CHANGE_DIRECTION // reverses the up/down direction when using the paddles to change the wpm or sidetone frequency +// #define OPTION_DISPLAY_MEMORY_CONTENTS_COMMAND_MODE + +// #define OPTION_BEACON_MODE_MEMORY_REPEAT_TIME // to space out the repeated playing of memory 1 when in beacon mode +// #define OPTION_BEACON_MODE_PTT_TAIL_TIME // adds the ptt tail time to each playing of memory 1 in beacon mode + +// #define OPTION_WINKEY_PROSIGN_COMPATIBILITY // Additional character mappings to support K1EL Winkey emulation prosigns \ No newline at end of file diff --git a/k3ng_keyer/keyer_features_and_options_generic_STM32F103C.h b/k3ng_keyer/keyer_features_and_options_generic_STM32F103C.h new file mode 100755 index 0000000..d8e8c12 --- /dev/null +++ b/k3ng_keyer/keyer_features_and_options_generic_STM32F103C.h @@ -0,0 +1,135 @@ + /* +Generic STM32F103C "Blue Pill" + + */ + + + +// compile time features and options - comment or uncomment to add or delete features +// FEATURES add more bytes to the compiled binary, OPTIONS change code behavior + + +#define FEATURE_BUTTONS +#define FEATURE_COMMAND_MODE +#define FEATURE_COMMAND_LINE_INTERFACE // Command Line Interface functionality +#define FEATURE_MEMORIES // on the Arduino Due, you must have FEATURE_EEPROM_E24C1024 and E24C1024 EEPROM hardware in order to compile this +#define FEATURE_MEMORY_MACROS +#define FEATURE_WINKEY_EMULATION // disabling Automatic Software Reset is highly recommended (see documentation) +#define FEATURE_BEACON // Go into beacon mode if paddle_left pin is LOW at boot up +#define FEATURE_BEACON_SETTING // Go into beacon mode at boot up if EEPROM setting is enabled (\_ CLI Command) +// #define FEATURE_TRAINING_COMMAND_LINE_INTERFACE +// #define FEATURE_POTENTIOMETER // do not enable unless you have a potentiometer connected, otherwise noise will falsely trigger wpm changes +// #define FEATURE_SIDETONE_SWITCH // adds switch control for the sidetone output. requires an external toggle switch (assigned to an arduino pin - see keyer_pin_settings.h). +// #define FEATURE_SIDETONE_NEWTONE // Use the NewTone library, ~1k smaller code size than the standard tone library. Uses timer1 (pins 9 or 10) https://bitbucket.org/teckel12/arduino-new-tone/wiki/Home +#define FEATURE_SERIAL_HELP +#define FEATURE_HELL +// #define FEATURE_PS2_KEYBOARD // Use a PS2 keyboard to send code - Change keyboard layout (non-US) in K3NG_PS2Keyboard.h. Additional options below. +// #define FEATURE_USB_KEYBOARD // Use a USB keyboard to send code - Uncomment three lines in k3ng_keyer.ino (search for note_usb_uncomment_lines) +// #define FEATURE_CW_COMPUTER_KEYBOARD // Have an Arduino Due or Leonardo act as a USB HID (Human Interface Device) keyboard and use the paddle to "type" characters on the computer -- uncomment this line in ino file: #include +#define FEATURE_DEAD_OP_WATCHDOG +#define FEATURE_AUTOSPACE +#define FEATURE_FARNSWORTH +// #define FEATURE_DL2SBA_BANKSWITCH // Switch memory banks feature as described here: http://dl2sba.com/index.php?option=com_content&view=article&id=131:nanokeyer&catid=15:shack&Itemid=27#english +#define FEATURE_LCD_4BIT // classic LCD disidefplay using 4 I/O lines +// #define FEATURE_LCD_8BIT // classic LCD display using 8 I/O lines +// #define FEATURE_LCD_ADAFRUIT_I2C // Adafruit I2C LCD display using MCP23017 at addr 0x20 +// #define FEATURE_LCD_ADAFRUIT_BACKPACK // Adafruit I2C LCD Backup using MCP23008 (courtesy Josiah Ritchie, KE0BLL) +// #define FEATURE_LCD_YDv1 // YourDuino I2C LCD display with old LCM 1602 V1 ic +// #define FEATURE_LCD1602_N07DH // http://linksprite.com/wiki/index.php5?title=16_X_2_LCD_Keypad_Shield_for_Arduino +// #define FEATURE_LCD_SAINSMART_I2C +// #define FEATURE_LCD_FABO_PCF8574 // https://github.com/FaBoPlatform/FaBoLCD-PCF8574-Library +// #define FEATURE_LCD_I2C_FDEBRABANDER //https://github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library +// #define FEATURE_CW_DECODER // https://github.com/k3ng/k3ng_cw_keyer/wiki/385-Feature:-CW-Decoder +// #define FEATURE_SLEEP // go to sleep after x minutes to conserve battery power (not compatible with Arduino DUE, may have mixed results with Mega and Mega ADK) +// #define FEATURE_LCD_BACKLIGHT_AUTO_DIM // turn off LCD backlight and/or dim Power Indicator LED after x minutes (LED requires a PWM pin) +#define FEATURE_ROTARY_ENCODER // rotary encoder speed control +// #define FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING +// #define FEATURE_USB_MOUSE // Uncomment three lines in k3ng_keyer.ino (search for note_usb_uncomment_lines) +// #define FEATURE_CAPACITIVE_PADDLE_PINS // remove the bypass capacitors on the paddle_left and paddle_right lines when using capactive paddles +// #define FEATURE_LED_RING // Mayhew Labs Led Ring support +#define FEATURE_ALPHABET_SEND_PRACTICE // enables command mode S command - created by Ryan, KC2ZWM +// #define FEATURE_COMMAND_MODE_PROGRESSIVE_5_CHAR_ECHO_PRACTICE // enables command mode U +// #define FEATURE_PTT_INTERLOCK +#define FEATURE_QLF +// #define FEATURE_EEPROM_E24C1024 +// #define FEATURE_STRAIGHT_KEY +// #define FEATURE_DYNAMIC_DAH_TO_DIT_RATIO +// #define FEATURE_PADDLE_ECHO +// #define FEATURE_STRAIGHT_KEY_ECHO +// #define FEATURE_AMERICAN_MORSE +// #define FEATURE_4x4_KEYPAD // code contributed by Jack, W0XR - documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/380-Feature:-Keypad +// #define FEATURE_3x4_KEYPAD // code contributed by Jack, W0XR - documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/380-Feature:-Keypad +// #define FEATURE_SEQUENCER +// #define FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT +// #define FEATURE_WEB_SERVER // Details: https://github.com/k3ng/k3ng_cw_keyer/wiki/390-Feature:-Ethernet,-Web-Server,-and-Internet-Linking +// #define FEATURE_INTERNET_LINK // Details: https://github.com/k3ng/k3ng_cw_keyer/wiki/390-Feature:-Ethernet,-Web-Server,-and-Internet-Linking + +// #define FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT // Activate the Command Line interface on the secondary serial port +#define OPTION_PRIMARY_SERIAL_PORT_DEFAULT_WINKEY_EMULATION // Use when activating both FEATURE_WINKEY_EMULATION and FEATURE_COMMAND_LINE_INTERFACE + // simultaneously. This will make Winkey emulation be the default at boot up; + // hold command button down at boot up to activate CLI mode + +//#define OPTION_SUPPRESS_SERIAL_BOOT_MSG +#define OPTION_INCLUDE_PTT_TAIL_FOR_MANUAL_SENDING +#define OPTION_EXCLUDE_PTT_HANG_TIME_FOR_MANUAL_SENDING +// #define OPTION_WINKEY_DISCARD_BYTES_AT_STARTUP // if ASR is not disabled, you may need this to discard errant serial port bytes at startup +// #define OPTION_WINKEY_STRICT_EEPROM_WRITES_MAY_WEAR_OUT_EEPROM // with this activated the unit will write non-volatile settings to EEPROM when set by Winkey commands +// #define OPTION_WINKEY_SEND_WORDSPACE_AT_END_OF_BUFFER +//#define OPTION_WINKEY_STRICT_HOST_OPEN // require an admin host open Winkey command before doing any other commands +//#define OPTION_WINKEY_2_SUPPORT // comment out to revert to Winkey version 1 emulation +//#define OPTION_WINKEY_INTERRUPTS_MEMORY_REPEAT +//#define OPTION_WINKEY_UCXLOG_9600_BAUD // use this only with UCXLog configured for Winkey 9600 baud mode +#define OPTION_WINKEY_2_HOST_CLOSE_NO_SERIAL_PORT_RESET // (Required for Win-Test to function) +// #define OPTION_WINKEY_FREQUENT_STATUS_REPORT // activate this to make Winkey emulation play better with RUMlog and RUMped +// #define OPTION_WINKEY_IGNORE_LOWERCASE // Enable for typical K1EL Winkeyer behavior (use for SkookumLogger version 1.10.14 and prior to workaround "r" bug) +// #define OPTION_WINKEY_BLINK_PTT_ON_HOST_OPEN +// #define OPTION_WINKEY_SEND_VERSION_ON_HOST_CLOSE +// #define OPTION_WINKEY_PINCONFIG_PTT_CONTROLS_PTT_LINE // Have Winkeyer PTT setting activate/deactivate PTT line rather than control buffered character PTT hold +// #define OPTION_REVERSE_BUTTON_ORDER // This is mainly for the DJ0MY NanoKeyer http://nanokeyer.wordpress.com/ +#define OPTION_PROG_MEM_TRIM_TRAILING_SPACES // trim trailing spaces from memory when programming in command mode +#define OPTION_DIT_PADDLE_NO_SEND_ON_MEM_RPT // this makes dit paddle memory interruption a little smoother +#define OPTION_MORE_DISPLAY_MSGS // additional optional display messages - comment out to save memory +// #define OPTION_WATCHDOG_TIMER // this enables a four second ATmega48/88/168/328 watchdog timer; use for unattended/remote operation only +// #define OPTION_MOUSE_MOVEMENT_PADDLE // experimental (just fooling around) - mouse movement will act like a paddle +// #define OPTION_NON_ENGLISH_EXTENSIONS // add support for additional CW characters (i.e. À, Å, Þ, etc.) +// #define OPTION_DISPLAY_NON_ENGLISH_EXTENSIONS // LCD display suport for non-English (NO/DK/DE) characters - Courtesy of OZ1JHM +// #define OPTION_UNKNOWN_CHARACTER_ERROR_TONE +// #define OPTION_DO_NOT_SAY_HI +// #define OPTION_PS2_NON_ENGLISH_CHAR_LCD_DISPLAY_SUPPORT // makes some non-English characters from the PS2 keyboard display correctly in the LCD display (donated by Marcin sp5iou) +// #define OPTION_PS2_KEYBOARD_RESET // reset the PS2 keyboard upon startup with 0xFF (contributed by Bill, W9BEL) +// #define OPTION_SAVE_MEMORY_NANOKEYER +// #define OPTION_CW_KEYBOARD_CAPSLOCK_BEEP +// #define OPTION_CW_KEYBOARD_ITALIAN +// #define OPTION_CW_KEYBOARD_GERMAN +// #define OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR +// #define OPTION_INVERT_PADDLE_PIN_LOGIC +// #define OPTION_ADVANCED_SPEED_DISPLAY //enables "nerd" speed visualization on display: wpm, cpm (char per min), duration of dit and dah in milliseconds and ratio (contributed by Giorgio, IZ2XBZ) +#define OPTION_PROSIGN_SUPPORT // additional prosign support for paddle and straight key echo on display, CLI, and in memory storage +// #define OPTION_RUSSIAN_LANGUAGE_SEND_CLI // Russian language CLI sending support (contributed by Павел Бирюков, UA1AQC) +#define OPTION_DO_NOT_SEND_UNKNOWN_CHAR_QUESTION +#define OPTION_CMOS_SUPER_KEYER_IAMBIC_B_TIMING_ON_BY_DEFAULT +// #define OPTION_SIDETONE_DIGITAL_OUTPUT_NO_SQUARE_WAVE + +// #define OPTION_DIRECT_PADDLE_PIN_READS_MEGA // only works with Mega and pins 2 and 5 - minor performance increase +// #define OPTION_DIRECT_PADDLE_PIN_READS_UNO // Unos or Nanos pins 2 and 5 - do not enable on a nanoKeyer, it uses different pins + +// #define OPTION_WORDSWORTH_CZECH +// #define OPTION_WORDSWORTH_DEUTSCH +// #define OPTION_WORDSWORTH_NORSK + +// #define OPTION_EXCLUDE_EXTENDED_CLI_COMMANDS + +// #define OPTION_DFROBOT_LCD_COMMAND_BUTTONS + +// #define OPTION_EXCLUDE_MILL_MODE +// #define OPTION_NO_ULTIMATIC // reduce memory usage by removing ultimatic code. + +// #define OPTION_DISABLE_SERIAL_PORT_CHECKING_WHILE_SENDING_CW +// #define OPTION_PERSONALIZED_STARTUP_SCREEN // displays a user defined string of characters on the second or fourth row of the screen during startup. 1602 display requires OPTION_DO_NOT_SAY_HI +// #define OPTION_SWAP_PADDLE_PARAMETER_CHANGE_DIRECTION // reverses the up/down direction when using the paddles to change the wpm or sidetone frequency +// #define OPTION_DISPLAY_MEMORY_CONTENTS_COMMAND_MODE + +// #define OPTION_BEACON_MODE_MEMORY_REPEAT_TIME // to space out the repeated playing of memory 1 when in beacon mode +// #define OPTION_BEACON_MODE_PTT_TAIL_TIME // adds the ptt tail time to each playing of memory 1 in beacon mode + +// #define OPTION_WINKEY_PROSIGN_COMPATIBILITY // Additional character mappings to support K1EL Winkey emulation prosigns diff --git a/k3ng_keyer/keyer_features_and_options_iz3gme.h b/k3ng_keyer/keyer_features_and_options_iz3gme.h new file mode 100755 index 0000000..eba9760 --- /dev/null +++ b/k3ng_keyer/keyer_features_and_options_iz3gme.h @@ -0,0 +1,131 @@ +// compile time features and options - comment or uncomment to add or delete features +// FEATURES add more bytes to the compiled binary, OPTIONS change code behavior + + +#define FEATURE_BUTTONS +#define FEATURE_COMMAND_MODE +#define FEATURE_COMMAND_LINE_INTERFACE // Command Line Interface functionality +#define FEATURE_MEMORIES // on the Arduino Due, you must have FEATURE_EEPROM_E24C1024 and E24C1024 EEPROM hardware in order to compile this +#define FEATURE_MEMORY_MACROS +#define FEATURE_WINKEY_EMULATION // disabling Automatic Software Reset is highly recommended (see documentation) +// #define FEATURE_BEACON // Go into beacon mode if paddle_left pin is LOW at boot up +// #define FEATURE_BEACON_SETTING // Go into beacon mode at boot up if EEPROM setting is enabled (\_ CLI Command) +// #define FEATURE_TRAINING_COMMAND_LINE_INTERFACE +#define FEATURE_POTENTIOMETER // do not enable unless you have a potentiometer connected, otherwise noise will falsely trigger wpm changes +// #define FEATURE_SIDETONE_SWITCH // adds switch control for the sidetone output. requires an external toggle switch (assigned to an arduino pin - see keyer_pin_settings.h). +// #define FEATURE_SIDETONE_NEWTONE // Use the NewTone library, ~1k smaller code size than the standard tone library. Uses timer1 (pins 9 or 10) https://bitbucket.org/teckel12/arduino-new-tone/wiki/Home +#define FEATURE_SERIAL_HELP +// #define FEATURE_HELL +// #define FEATURE_PS2_KEYBOARD // Use a PS2 keyboard to send code - Change keyboard layout (non-US) in K3NG_PS2Keyboard.h. Additional options below. +// #define FEATURE_USB_KEYBOARD // Use a USB keyboard to send code - Uncomment three lines in k3ng_keyer.ino (search for note_usb_uncomment_lines) +// #define FEATURE_CW_COMPUTER_KEYBOARD // Have an Arduino Due or Leonardo act as a USB HID (Human Interface Device) keyboard and use the paddle to "type" characters on the computer -- uncomment this line in ino file: #include +// #define FEATURE_DEAD_OP_WATCHDOG +// #define FEATURE_AUTOSPACE +// #define FEATURE_FARNSWORTH +// #define FEATURE_DL2SBA_BANKSWITCH // Switch memory banks feature as described here: http://dl2sba.com/index.php?option=com_content&view=article&id=131:nanokeyer&catid=15:shack&Itemid=27#english +#define FEATURE_LCD_4BIT // classic LCD disidefplay using 4 I/O lines +// #define FEATURE_LCD_8BIT // classic LCD display using 8 I/O lines +// #define FEATURE_LCD_ADAFRUIT_I2C // Adafruit I2C LCD display using MCP23017 at addr 0x20 +// #define FEATURE_LCD_ADAFRUIT_BACKPACK // Adafruit I2C LCD Backup using MCP23008 (courtesy Josiah Ritchie, KE0BLL) +// #define FEATURE_LCD_YDv1 // YourDuino I2C LCD display with old LCM 1602 V1 ic +// #define FEATURE_LCD1602_N07DH // http://linksprite.com/wiki/index.php5?title=16_X_2_LCD_Keypad_Shield_for_Arduino +// #define FEATURE_LCD_SAINSMART_I2C +// #define FEATURE_LCD_FABO_PCF8574 // https://github.com/FaBoPlatform/FaBoLCD-PCF8574-Library +// #define FEATURE_LCD_MATHERTEL_PCF8574 // https://github.com/mathertel/LiquidCrystal_PCF8574 +// #define FEATURE_LCD_I2C_FDEBRABANDER //https://github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library +// #define FEATURE_LCD_HD44780 +// #define FEATURE_CW_DECODER // https://github.com/k3ng/k3ng_cw_keyer/wiki/385-Feature:-CW-Decoder +// #define FEATURE_SLEEP // go to sleep after x minutes to conserve battery power (not compatible with Arduino DUE, may have mixed results with Mega and Mega ADK) +// #define FEATURE_LCD_BACKLIGHT_AUTO_DIM // turn off LCD backlight and/or dim Power Indicator LED after x minutes (LED requires a PWM pin) +// #define FEATURE_ROTARY_ENCODER // rotary encoder speed control +// #define FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING +// #define FEATURE_USB_MOUSE // Uncomment three lines in k3ng_keyer.ino (search for note_usb_uncomment_lines) +// #define FEATURE_CAPACITIVE_PADDLE_PINS // remove the bypass capacitors on the paddle_left and paddle_right lines when using capactive paddles +// #define FEATURE_LED_RING // Mayhew Labs Led Ring support +// #define FEATURE_ALPHABET_SEND_PRACTICE // enables command mode S command - created by Ryan, KC2ZWM +// #define FEATURE_COMMAND_MODE_PROGRESSIVE_5_CHAR_ECHO_PRACTICE // enables command mode U +// #define FEATURE_PTT_INTERLOCK +// #define FEATURE_QLF +// #define FEATURE_EEPROM_E24C1024 +// #define FEATURE_STRAIGHT_KEY +// #define FEATURE_DYNAMIC_DAH_TO_DIT_RATIO +// #define FEATURE_PADDLE_ECHO +// #define FEATURE_STRAIGHT_KEY_ECHO +// #define FEATURE_AMERICAN_MORSE +// #define FEATURE_4x4_KEYPAD // code contributed by Jack, W0XR - documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/380-Feature:-Keypad +// #define FEATURE_3x4_KEYPAD // code contributed by Jack, W0XR - documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/380-Feature:-Keypad +// #define FEATURE_SEQUENCER +// #define FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT +// #define FEATURE_WEB_SERVER // Details: https://github.com/k3ng/k3ng_cw_keyer/wiki/390-Feature:-Ethernet,-Web-Server,-and-Internet-Linking +// #define FEATURE_INTERNET_LINK // Details: https://github.com/k3ng/k3ng_cw_keyer/wiki/390-Feature:-Ethernet,-Web-Server,-and-Internet-Linking + +// #define FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT // Activate the Command Line interface on the secondary serial port +#define OPTION_PRIMARY_SERIAL_PORT_DEFAULT_WINKEY_EMULATION // Use when activating both FEATURE_WINKEY_EMULATION and FEATURE_COMMAND_LINE_INTERFACE + // simultaneously. This will make Winkey emulation be the default at boot up; + // hold command button down at boot up to activate CLI mode + +// #define OPTION_SUPPRESS_SERIAL_BOOT_MSG +#define OPTION_INCLUDE_PTT_TAIL_FOR_MANUAL_SENDING +#define OPTION_EXCLUDE_PTT_HANG_TIME_FOR_MANUAL_SENDING +// #define OPTION_WINKEY_DISCARD_BYTES_AT_STARTUP // if ASR is not disabled, you may need this to discard errant serial port bytes at startup +// #define OPTION_WINKEY_STRICT_EEPROM_WRITES_MAY_WEAR_OUT_EEPROM // with this activated the unit will write non-volatile settings to EEPROM when set by Winkey commands +// #define OPTION_WINKEY_SEND_WORDSPACE_AT_END_OF_BUFFER +#define OPTION_WINKEY_STRICT_HOST_OPEN // require an admin host open Winkey command before doing any other commands +#define OPTION_WINKEY_2_SUPPORT // comment out to revert to Winkey version 1 emulation +#define OPTION_WINKEY_INTERRUPTS_MEMORY_REPEAT +//#define OPTION_WINKEY_UCXLOG_9600_BAUD // use this only with UCXLog configured for Winkey 9600 baud mode +#define OPTION_WINKEY_2_HOST_CLOSE_NO_SERIAL_PORT_RESET // (Required for Win-Test to function) +// #define OPTION_WINKEY_FREQUENT_STATUS_REPORT // activate this to make Winkey emulation play better with RUMlog and RUMped +#define OPTION_WINKEY_IGNORE_LOWERCASE // Enable for typical K1EL Winkeyer behavior (use for SkookumLogger version 1.10.14 and prior to workaround "r" bug) +// #define OPTION_WINKEY_BLINK_PTT_ON_HOST_OPEN +// #define OPTION_WINKEY_SEND_VERSION_ON_HOST_CLOSE +// #define OPTION_WINKEY_PINCONFIG_PTT_CONTROLS_PTT_LINE // Have Winkeyer PTT setting activate/deactivate PTT line rather than control buffered character PTT hold +// #define OPTION_REVERSE_BUTTON_ORDER // This is mainly for the DJ0MY NanoKeyer http://nanokeyer.wordpress.com/ +#define OPTION_PROG_MEM_TRIM_TRAILING_SPACES // trim trailing spaces from memory when programming in command mode +#define OPTION_DIT_PADDLE_NO_SEND_ON_MEM_RPT // this makes dit paddle memory interruption a little smoother +#define OPTION_MORE_DISPLAY_MSGS // additional optional display messages - comment out to save memory +// #define OPTION_WATCHDOG_TIMER // this enables a four second ATmega48/88/168/328 watchdog timer; use for unattended/remote operation only +// #define OPTION_MOUSE_MOVEMENT_PADDLE // experimental (just fooling around) - mouse movement will act like a paddle +// #define OPTION_NON_ENGLISH_EXTENSIONS // add support for additional CW characters (i.e. À, Å, Þ, etc.) +// #define OPTION_DISPLAY_NON_ENGLISH_EXTENSIONS // LCD display suport for non-English (NO/DK/DE) characters - Courtesy of OZ1JHM +// #define OPTION_UNKNOWN_CHARACTER_ERROR_TONE +// #define OPTION_DO_NOT_SAY_HI +// #define OPTION_PS2_NON_ENGLISH_CHAR_LCD_DISPLAY_SUPPORT // makes some non-English characters from the PS2 keyboard display correctly in the LCD display (donated by Marcin sp5iou) +// #define OPTION_PS2_KEYBOARD_RESET // reset the PS2 keyboard upon startup with 0xFF (contributed by Bill, W9BEL) +// #define OPTION_SAVE_MEMORY_NANOKEYER +#define OPTION_CW_KEYBOARD_CAPSLOCK_BEEP +// #define OPTION_CW_KEYBOARD_ITALIAN +// #define OPTION_CW_KEYBOARD_GERMAN +// #define OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR +// #define OPTION_INVERT_PADDLE_PIN_LOGIC +// #define OPTION_ADVANCED_SPEED_DISPLAY //enables "nerd" speed visualization on display: wpm, cpm (char per min), duration of dit and dah in milliseconds and ratio (contributed by Giorgio, IZ2XBZ) +#define OPTION_PROSIGN_SUPPORT // additional prosign support for paddle and straight key echo on display, CLI, and in memory storage +// #define OPTION_RUSSIAN_LANGUAGE_SEND_CLI // Russian language CLI sending support (contributed by Павел Бирюков, UA1AQC) +#define OPTION_DO_NOT_SEND_UNKNOWN_CHAR_QUESTION +// #define OPTION_CMOS_SUPER_KEYER_IAMBIC_B_TIMING_ON_BY_DEFAULT +// #define OPTION_SIDETONE_DIGITAL_OUTPUT_NO_SQUARE_WAVE +// #define FEATURE_SD_CARD_SUPPORT + +// #define OPTION_DIRECT_PADDLE_PIN_READS_MEGA // only works with Mega and pins 2 and 5 - minor performance increase +// #define OPTION_DIRECT_PADDLE_PIN_READS_UNO // Unos or Nanos pins 2 and 5 - do not enable on a nanoKeyer, it uses different pins + +// #define OPTION_WORDSWORTH_CZECH +// #define OPTION_WORDSWORTH_DEUTSCH +// #define OPTION_WORDSWORTH_NORSK + +#define OPTION_EXCLUDE_EXTENDED_CLI_COMMANDS + +// #define OPTION_DFROBOT_LCD_COMMAND_BUTTONS + +// #define OPTION_EXCLUDE_MILL_MODE +// #define OPTION_NO_ULTIMATIC // reduce memory usage by removing ultimatic code. + +// #define OPTION_DISABLE_SERIAL_PORT_CHECKING_WHILE_SENDING_CW +// #define OPTION_PERSONALIZED_STARTUP_SCREEN // displays a user defined string of characters on the second or fourth row of the screen during startup. 1602 display requires OPTION_DO_NOT_SAY_HI +// #define OPTION_SWAP_PADDLE_PARAMETER_CHANGE_DIRECTION // reverses the up/down direction when using the paddles to change the wpm or sidetone frequency +// #define OPTION_DISPLAY_MEMORY_CONTENTS_COMMAND_MODE + +// #define OPTION_BEACON_MODE_MEMORY_REPEAT_TIME // to space out the repeated playing of memory 1 when in beacon mode +// #define OPTION_BEACON_MODE_PTT_TAIL_TIME // adds the ptt tail time to each playing of memory 1 in beacon mode + +// #define OPTION_WINKEY_PROSIGN_COMPATIBILITY // Additional character mappings to support K1EL Winkey emulation prosigns diff --git a/k3ng_keyer/keyer_features_and_options_k5bcq.h b/k3ng_keyer/keyer_features_and_options_k5bcq.h new file mode 100755 index 0000000..a73166c --- /dev/null +++ b/k3ng_keyer/keyer_features_and_options_k5bcq.h @@ -0,0 +1,130 @@ +// compile time features and options - comment or uncomment to add or delete features +// FEATURES add more bytes to the compiled binary, OPTIONS change code behavior + + +#define FEATURE_BUTTONS +#define FEATURE_COMMAND_MODE +#define FEATURE_COMMAND_LINE_INTERFACE // Command Line Interface functionality +#define FEATURE_MEMORIES // on the Arduino Due, you must have FEATURE_EEPROM_E24C1024 and E24C1024 EEPROM hardware in order to compile this +#define FEATURE_MEMORY_MACROS +#define FEATURE_WINKEY_EMULATION // disabling Automatic Software Reset is highly recommended (see documentation) +#define FEATURE_BEACON // Go into beacon mode if paddle_left pin is LOW at boot up +#define FEATURE_BEACON_SETTING // Go into beacon mode at boot up if EEPROM setting is enabled (\_ CLI Command) +// #define FEATURE_TRAINING_COMMAND_LINE_INTERFACE +// #define FEATURE_POTENTIOMETER // do not enable unless you have a potentiometer connected, otherwise noise will falsely trigger wpm changes +// #define FEATURE_SIDETONE_SWITCH // adds switch control for the sidetone output. requires an external toggle switch (assigned to an arduino pin - see keyer_pin_settings.h). +// #define FEATURE_SIDETONE_NEWTONE // Use the NewTone library, ~1k smaller code size than the standard tone library. Uses timer1 (pins 9 or 10) https://bitbucket.org/teckel12/arduino-new-tone/wiki/Home +#define FEATURE_SERIAL_HELP +#define FEATURE_HELL +#define FEATURE_PS2_KEYBOARD // Use a PS2 keyboard to send code - Change keyboard layout (non-US) in K3NG_PS2Keyboard.h. Additional options below. +// #define FEATURE_USB_KEYBOARD // Use a USB keyboard to send code - Uncomment three lines in k3ng_keyer.ino (search for note_usb_uncomment_lines) +// #define FEATURE_CW_COMPUTER_KEYBOARD // Have an Arduino Due or Leonardo act as a USB HID (Human Interface Device) keyboard and use the paddle to "type" characters on the computer -- uncomment this line in ino file: #include +#define FEATURE_DEAD_OP_WATCHDOG +#define FEATURE_AUTOSPACE +#define FEATURE_FARNSWORTH +// #define FEATURE_DL2SBA_BANKSWITCH // Switch memory banks feature as described here: http://dl2sba.com/index.php?option=com_content&view=article&id=131:nanokeyer&catid=15:shack&Itemid=27#english +#define FEATURE_LCD_4BIT // classic LCD disidefplay using 4 I/O lines +// #define FEATURE_LCD_8BIT // classic LCD display using 8 I/O lines +// #define FEATURE_LCD_ADAFRUIT_I2C // Adafruit I2C LCD display using MCP23017 at addr 0x20 +// #define FEATURE_LCD_ADAFRUIT_BACKPACK // Adafruit I2C LCD Backup using MCP23008 (courtesy Josiah Ritchie, KE0BLL) +// #define FEATURE_LCD_YDv1 // YourDuino I2C LCD display with old LCM 1602 V1 ic +// #define FEATURE_LCD1602_N07DH // http://linksprite.com/wiki/index.php5?title=16_X_2_LCD_Keypad_Shield_for_Arduino +// #define FEATURE_LCD_SAINSMART_I2C +// #define FEATURE_LCD_FABO_PCF8574 // https://github.com/FaBoPlatform/FaBoLCD-PCF8574-Library +// #define FEATURE_LCD_I2C_FDEBRABANDER //https://github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library +#define FEATURE_CW_DECODER // https://github.com/k3ng/k3ng_cw_keyer/wiki/385-Feature:-CW-Decoder +// #define FEATURE_SLEEP // go to sleep after x minutes to conserve battery power (not compatible with Arduino DUE, may have mixed results with Mega and Mega ADK) +// #define FEATURE_LCD_BACKLIGHT_AUTO_DIM // turn off LCD backlight and/or dim Power Indicator LED after x minutes (LED requires a PWM pin) +#define FEATURE_ROTARY_ENCODER // rotary encoder speed control +// #define FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING +// #define FEATURE_USB_MOUSE // Uncomment three lines in k3ng_keyer.ino (search for note_usb_uncomment_lines) +// #define FEATURE_CAPACITIVE_PADDLE_PINS // remove the bypass capacitors on the paddle_left and paddle_right lines when using capactive paddles +// #define FEATURE_LED_RING // Mayhew Labs Led Ring support +#define FEATURE_ALPHABET_SEND_PRACTICE // enables command mode S command - created by Ryan, KC2ZWM +#define FEATURE_COMMAND_MODE_PROGRESSIVE_5_CHAR_ECHO_PRACTICE // enables command mode U +// #define FEATURE_PTT_INTERLOCK +// #define FEATURE_QLF +// #define FEATURE_EEPROM_E24C1024 +// #define FEATURE_STRAIGHT_KEY //Highly recommended to leave disabled. Booting with straight key plugged in works better (timing issues w/ this feature w/paddles) +// #define FEATURE_DYNAMIC_DAH_TO_DIT_RATIO +// #define FEATURE_PADDLE_ECHO +// #define FEATURE_STRAIGHT_KEY_ECHO +// #define FEATURE_AMERICAN_MORSE +// #define FEATURE_4x4_KEYPAD // code contributed by Jack, W0XR - documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/380-Feature:-Keypad +// #define FEATURE_3x4_KEYPAD // code contributed by Jack, W0XR - documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/380-Feature:-Keypad +// #define FEATURE_SEQUENCER +// #define FEATURE_SD_CARD_SUPPORT +// #define FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT +// #define FEATURE_WEB_SERVER // Details: https://github.com/k3ng/k3ng_cw_keyer/wiki/390-Feature:-Ethernet,-Web-Server,-and-Internet-Linking +// #define FEATURE_INTERNET_LINK // Details: https://github.com/k3ng/k3ng_cw_keyer/wiki/390-Feature:-Ethernet,-Web-Server,-and-Internet-Linking + +// #define FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT // Activate the Command Line interface on the secondary serial port +#define OPTION_PRIMARY_SERIAL_PORT_DEFAULT_WINKEY_EMULATION // Use when activating both FEATURE_WINKEY_EMULATION and FEATURE_COMMAND_LINE_INTERFACE + // simultaneously. This will make Winkey emulation be the default at boot up; + // hold command button down at boot up to activate CLI mode + +// #define OPTION_SUPPRESS_SERIAL_BOOT_MSG +#define OPTION_INCLUDE_PTT_TAIL_FOR_MANUAL_SENDING +#define OPTION_EXCLUDE_PTT_HANG_TIME_FOR_MANUAL_SENDING +// #define OPTION_WINKEY_DISCARD_BYTES_AT_STARTUP // if ASR is not disabled, you may need this to discard errant serial port bytes at startup +// #define OPTION_WINKEY_STRICT_EEPROM_WRITES_MAY_WEAR_OUT_EEPROM // with this activated the unit will write non-volatile settings to EEPROM when set by Winkey commands +// #define OPTION_WINKEY_SEND_WORDSPACE_AT_END_OF_BUFFER +#define OPTION_WINKEY_STRICT_HOST_OPEN // require an admin host open Winkey command before doing any other commands +#define OPTION_WINKEY_2_SUPPORT // comment out to revert to Winkey version 1 emulation +#define OPTION_WINKEY_INTERRUPTS_MEMORY_REPEAT +//#define OPTION_WINKEY_UCXLOG_9600_BAUD // use this only with UCXLog configured for Winkey 9600 baud mode +#define OPTION_WINKEY_2_HOST_CLOSE_NO_SERIAL_PORT_RESET // (Required for Win-Test to function) +// #define OPTION_WINKEY_FREQUENT_STATUS_REPORT // activate this to make Winkey emulation play better with RUMlog and RUMped +#define OPTION_WINKEY_IGNORE_LOWERCASE // Enable for typical K1EL Winkeyer behavior (use for SkookumLogger version 1.10.14 and prior to workaround "r" bug) +// #define OPTION_WINKEY_BLINK_PTT_ON_HOST_OPEN +// #define OPTION_WINKEY_SEND_VERSION_ON_HOST_CLOSE +// #define OPTION_WINKEY_PINCONFIG_PTT_CONTROLS_PTT_LINE // Have Winkeyer PTT setting activate/deactivate PTT line rather than control buffered character PTT hold +// #define OPTION_REVERSE_BUTTON_ORDER // This is mainly for the DJ0MY NanoKeyer http://nanokeyer.wordpress.com/ +#define OPTION_PROG_MEM_TRIM_TRAILING_SPACES // trim trailing spaces from memory when programming in command mode +#define OPTION_DIT_PADDLE_NO_SEND_ON_MEM_RPT // this makes dit paddle memory interruption a little smoother +#define OPTION_MORE_DISPLAY_MSGS // additional optional display messages - comment out to save memory +// #define OPTION_WATCHDOG_TIMER // this enables a four second ATmega48/88/168/328 watchdog timer; use for unattended/remote operation only +// #define OPTION_MOUSE_MOVEMENT_PADDLE // experimental (just fooling around) - mouse movement will act like a paddle +// #define OPTION_NON_ENGLISH_EXTENSIONS // add support for additional CW characters (i.e. À, Å, Þ, etc.) +// #define OPTION_DISPLAY_NON_ENGLISH_EXTENSIONS // LCD display suport for non-English (NO/DK/DE) characters - Courtesy of OZ1JHM +// #define OPTION_UNKNOWN_CHARACTER_ERROR_TONE +// #define OPTION_DO_NOT_SAY_HI +// #define OPTION_PS2_NON_ENGLISH_CHAR_LCD_DISPLAY_SUPPORT // makes some non-English characters from the PS2 keyboard display correctly in the LCD display (donated by Marcin sp5iou) +// #define OPTION_PS2_KEYBOARD_RESET // reset the PS2 keyboard upon startup with 0xFF (contributed by Bill, W9BEL) +// #define OPTION_SAVE_MEMORY_NANOKEYER +#define OPTION_CW_KEYBOARD_CAPSLOCK_BEEP +// #define OPTION_CW_KEYBOARD_ITALIAN +// #define OPTION_CW_KEYBOARD_GERMAN +#define OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR //Goertzel default is 558Hz, with 186Hz bandwidth. Edit goertzel.h in libraries as desired. +// #define OPTION_INVERT_PADDLE_PIN_LOGIC +#define OPTION_ADVANCED_SPEED_DISPLAY //enables "nerd" speed visualization on display: wpm, cpm (char per min), duration of dit and dah in milliseconds and ratio (contributed by Giorgio, IZ2XBZ) +#define OPTION_PROSIGN_SUPPORT // additional prosign support for paddle and straight key echo on display, CLI, and in memory storage +// #define OPTION_RUSSIAN_LANGUAGE_SEND_CLI // Russian language CLI sending support (contributed by Павел Бирюков, UA1AQC) +#define OPTION_DO_NOT_SEND_UNKNOWN_CHAR_QUESTION +// #define OPTION_CMOS_SUPER_KEYER_IAMBIC_B_TIMING_ON_BY_DEFAULT +#define OPTION_SIDETONE_DIGITAL_OUTPUT_NO_SQUARE_WAVE //comment out if not using the twin T oscillator. Remember to change audio out pin + +// #define OPTION_DIRECT_PADDLE_PIN_READS_MEGA // only works with Mega and pins 2 and 5 - minor performance increase +// #define OPTION_DIRECT_PADDLE_PIN_READS_UNO // Unos or Nanos pins 2 and 5 - do not enable on a nanoKeyer, it uses different pins + +// #define OPTION_WORDSWORTH_CZECH +// #define OPTION_WORDSWORTH_DEUTSCH +// #define OPTION_WORDSWORTH_NORSK + +#define OPTION_EXCLUDE_EXTENDED_CLI_COMMANDS + +// #define OPTION_DFROBOT_LCD_COMMAND_BUTTONS + +// #define OPTION_EXCLUDE_MILL_MODE +// #define OPTION_NO_ULTIMATIC // reduce memory usage by removing ultimatic code. + +// #define OPTION_DISABLE_SERIAL_PORT_CHECKING_WHILE_SENDING_CW + +// #define OPTION_PERSONALIZED_STARTUP_SCREEN // displays a user defined string of characters on the second or fourth row of the screen during startup. 1602 display requires OPTION_DO_NOT_SAY_HI +// #define OPTION_SWAP_PADDLE_PARAMETER_CHANGE_DIRECTION // reverses the up/down direction when using the paddles to change the wpm or sidetone frequency +// #define OPTION_DISPLAY_MEMORY_CONTENTS_COMMAND_MODE + +// #define OPTION_BEACON_MODE_MEMORY_REPEAT_TIME // to space out the repeated playing of memory 1 when in beacon mode +// #define OPTION_BEACON_MODE_PTT_TAIL_TIME // adds the ptt tail time to each playing of memory 1 in beacon mode + +// #define OPTION_WINKEY_PROSIGN_COMPATIBILITY // Additional character mappings to support K1EL Winkey emulation prosigns \ No newline at end of file diff --git a/k3ng_keyer/keyer_features_and_options_maple_mini.h b/k3ng_keyer/keyer_features_and_options_maple_mini.h new file mode 100755 index 0000000..d38e5ad --- /dev/null +++ b/k3ng_keyer/keyer_features_and_options_maple_mini.h @@ -0,0 +1,143 @@ +/* + +# # ## ##### # ###### # # # # # # +## ## # # # # # # ## ## # ## # # +# ## # # # # # # ##### # ## # # # # # # +# # ###### ##### # # # # # # # # # +# # # # # # # # # # # ## # +# # # # # ###### ###### # # # # # # + + + */ + + + +// compile time features and options - comment or uncomment to add or delete features +// FEATURES add more bytes to the compiled binary, OPTIONS change code behavior + + +#define FEATURE_BUTTONS +#define FEATURE_COMMAND_MODE +#define FEATURE_COMMAND_LINE_INTERFACE // Command Line Interface functionality +#define FEATURE_MEMORIES // on the Arduino Due, you must have FEATURE_EEPROM_E24C1024 and E24C1024 EEPROM hardware in order to compile this +#define FEATURE_MEMORY_MACROS +#define FEATURE_WINKEY_EMULATION // disabling Automatic Software Reset is highly recommended (see documentation) +#define FEATURE_BEACON // Go into beacon mode if paddle_left pin is LOW at boot up +#define FEATURE_BEACON_SETTING // Go into beacon mode at boot up if EEPROM setting is enabled (\_ CLI Command) +// #define FEATURE_TRAINING_COMMAND_LINE_INTERFACE +#define FEATURE_POTENTIOMETER // do not enable unless you have a potentiometer connected, otherwise noise will falsely trigger wpm changes +// #define FEATURE_SIDETONE_SWITCH // adds switch control for the sidetone output. requires an external toggle switch (assigned to an arduino pin - see keyer_pin_settings.h). +// #define FEATURE_SIDETONE_NEWTONE // Use the NewTone library, ~1k smaller code size than the standard tone library. Uses timer1 (pins 9 or 10) https://bitbucket.org/teckel12/arduino-new-tone/wiki/Home +#define FEATURE_SERIAL_HELP +#define FEATURE_HELL +// #define FEATURE_PS2_KEYBOARD // Use a PS2 keyboard to send code - Change keyboard layout (non-US) in K3NG_PS2Keyboard.h. Additional options below. +// #define FEATURE_USB_KEYBOARD // Use a USB keyboard to send code - Uncomment three lines in k3ng_keyer.ino (search for note_usb_uncomment_lines) +// #define FEATURE_CW_COMPUTER_KEYBOARD // Have an Arduino Due or Leonardo act as a USB HID (Human Interface Device) keyboard and use the paddle to "type" characters on the computer -- uncomment this line in ino file: #include +#define FEATURE_DEAD_OP_WATCHDOG +#define FEATURE_AUTOSPACE +#define FEATURE_FARNSWORTH +// #define FEATURE_DL2SBA_BANKSWITCH // Switch memory banks feature as described here: http://dl2sba.com/index.php?option=com_content&view=article&id=131:nanokeyer&catid=15:shack&Itemid=27#english +#define FEATURE_LCD_4BIT // classic LCD disidefplay using 4 I/O lines +// #define FEATURE_LCD_8BIT // classic LCD display using 8 I/O lines +// #define FEATURE_LCD_ADAFRUIT_I2C // Adafruit I2C LCD display using MCP23017 at addr 0x20 +// #define FEATURE_LCD_ADAFRUIT_BACKPACK // Adafruit I2C LCD Backup using MCP23008 (courtesy Josiah Ritchie, KE0BLL) +// #define FEATURE_LCD_YDv1 // YourDuino I2C LCD display with old LCM 1602 V1 ic +// #define FEATURE_LCD1602_N07DH // http://linksprite.com/wiki/index.php5?title=16_X_2_LCD_Keypad_Shield_for_Arduino +// #define FEATURE_LCD_SAINSMART_I2C +// #define FEATURE_LCD_FABO_PCF8574 // https://github.com/FaBoPlatform/FaBoLCD-PCF8574-Library +// #define FEATURE_LCD_I2C_FDEBRABANDER //https://github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library +// #define FEATURE_CW_DECODER // https://github.com/k3ng/k3ng_cw_keyer/wiki/385-Feature:-CW-Decoder +// #define FEATURE_SLEEP // go to sleep after x minutes to conserve battery power (not compatible with Arduino DUE, may have mixed results with Mega and Mega ADK) +// #define FEATURE_LCD_BACKLIGHT_AUTO_DIM // turn off LCD backlight and/or dim Power Indicator LED after x minutes (LED requires a PWM pin) +// #define FEATURE_ROTARY_ENCODER // rotary encoder speed control +// #define FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING +// #define FEATURE_USB_MOUSE // Uncomment three lines in k3ng_keyer.ino (search for note_usb_uncomment_lines) +// #define FEATURE_CAPACITIVE_PADDLE_PINS // remove the bypass capacitors on the paddle_left and paddle_right lines when using capactive paddles +// #define FEATURE_LED_RING // Mayhew Labs Led Ring support +#define FEATURE_ALPHABET_SEND_PRACTICE // enables command mode S command - created by Ryan, KC2ZWM +#define FEATURE_COMMAND_MODE_PROGRESSIVE_5_CHAR_ECHO_PRACTICE // enables command mode U +// #define FEATURE_PTT_INTERLOCK +#define FEATURE_QLF +// #define FEATURE_EEPROM_E24C1024 +// #define FEATURE_STRAIGHT_KEY +// #define FEATURE_DYNAMIC_DAH_TO_DIT_RATIO +// #define FEATURE_PADDLE_ECHO +// #define FEATURE_STRAIGHT_KEY_ECHO +// #define FEATURE_AMERICAN_MORSE +// #define FEATURE_4x4_KEYPAD // code contributed by Jack, W0XR - documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/380-Feature:-Keypad +// #define FEATURE_3x4_KEYPAD // code contributed by Jack, W0XR - documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/380-Feature:-Keypad +// #define FEATURE_SEQUENCER +// #define FEATURE_SD_CARD_SUPPORT +// #define FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT +// #define FEATURE_WEB_SERVER // Details: https://github.com/k3ng/k3ng_cw_keyer/wiki/390-Feature:-Ethernet,-Web-Server,-and-Internet-Linking +// #define FEATURE_INTERNET_LINK // Details: https://github.com/k3ng/k3ng_cw_keyer/wiki/390-Feature:-Ethernet,-Web-Server,-and-Internet-Linking + +// #define FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT // Activate the Command Line interface on the secondary serial port +#define OPTION_PRIMARY_SERIAL_PORT_DEFAULT_WINKEY_EMULATION // Use when activating both FEATURE_WINKEY_EMULATION and FEATURE_COMMAND_LINE_INTERFACE + // simultaneously. This will make Winkey emulation be the default at boot up; + // hold command button down at boot up to activate CLI mode + +//#define OPTION_SUPPRESS_SERIAL_BOOT_MSG +#define OPTION_INCLUDE_PTT_TAIL_FOR_MANUAL_SENDING +#define OPTION_EXCLUDE_PTT_HANG_TIME_FOR_MANUAL_SENDING +// #define OPTION_WINKEY_DISCARD_BYTES_AT_STARTUP // if ASR is not disabled, you may need this to discard errant serial port bytes at startup +// #define OPTION_WINKEY_STRICT_EEPROM_WRITES_MAY_WEAR_OUT_EEPROM // with this activated the unit will write non-volatile settings to EEPROM when set by Winkey commands +// #define OPTION_WINKEY_SEND_WORDSPACE_AT_END_OF_BUFFER +//#define OPTION_WINKEY_STRICT_HOST_OPEN // require an admin host open Winkey command before doing any other commands +#define OPTION_WINKEY_2_SUPPORT // comment out to revert to Winkey version 1 emulation +#define OPTION_WINKEY_INTERRUPTS_MEMORY_REPEAT +//#define OPTION_WINKEY_UCXLOG_9600_BAUD // use this only with UCXLog configured for Winkey 9600 baud mode +#define OPTION_WINKEY_2_HOST_CLOSE_NO_SERIAL_PORT_RESET // (Required for Win-Test to function) +// #define OPTION_WINKEY_FREQUENT_STATUS_REPORT // activate this to make Winkey emulation play better with RUMlog and RUMped +// #define OPTION_WINKEY_IGNORE_LOWERCASE // Enable for typical K1EL Winkeyer behavior (use for SkookumLogger version 1.10.14 and prior to workaround "r" bug) +// #define OPTION_WINKEY_BLINK_PTT_ON_HOST_OPEN +// #define OPTION_WINKEY_SEND_VERSION_ON_HOST_CLOSE +// #define OPTION_WINKEY_PINCONFIG_PTT_CONTROLS_PTT_LINE // Have Winkeyer PTT setting activate/deactivate PTT line rather than control buffered character PTT hold +// #define OPTION_REVERSE_BUTTON_ORDER // This is mainly for the DJ0MY NanoKeyer http://nanokeyer.wordpress.com/ +#define OPTION_PROG_MEM_TRIM_TRAILING_SPACES // trim trailing spaces from memory when programming in command mode +#define OPTION_DIT_PADDLE_NO_SEND_ON_MEM_RPT // this makes dit paddle memory interruption a little smoother +#define OPTION_MORE_DISPLAY_MSGS // additional optional display messages - comment out to save memory +// #define OPTION_WATCHDOG_TIMER // this enables a four second ATmega48/88/168/328 watchdog timer; use for unattended/remote operation only +// #define OPTION_MOUSE_MOVEMENT_PADDLE // experimental (just fooling around) - mouse movement will act like a paddle +// #define OPTION_NON_ENGLISH_EXTENSIONS // add support for additional CW characters (i.e. À, Å, Þ, etc.) +// #define OPTION_DISPLAY_NON_ENGLISH_EXTENSIONS // LCD display suport for non-English (NO/DK/DE) characters - Courtesy of OZ1JHM +// #define OPTION_UNKNOWN_CHARACTER_ERROR_TONE +// #define OPTION_DO_NOT_SAY_HI +// #define OPTION_PS2_NON_ENGLISH_CHAR_LCD_DISPLAY_SUPPORT // makes some non-English characters from the PS2 keyboard display correctly in the LCD display (donated by Marcin sp5iou) +// #define OPTION_PS2_KEYBOARD_RESET // reset the PS2 keyboard upon startup with 0xFF (contributed by Bill, W9BEL) +// #define OPTION_SAVE_MEMORY_NANOKEYER +#define OPTION_CW_KEYBOARD_CAPSLOCK_BEEP +// #define OPTION_CW_KEYBOARD_ITALIAN +// #define OPTION_CW_KEYBOARD_GERMAN +// #define OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR +// #define OPTION_INVERT_PADDLE_PIN_LOGIC +// #define OPTION_ADVANCED_SPEED_DISPLAY //enables "nerd" speed visualization on display: wpm, cpm (char per min), duration of dit and dah in milliseconds and ratio (contributed by Giorgio, IZ2XBZ) +#define OPTION_PROSIGN_SUPPORT // additional prosign support for paddle and straight key echo on display, CLI, and in memory storage +// #define OPTION_RUSSIAN_LANGUAGE_SEND_CLI // Russian language CLI sending support (contributed by Павел Бирюков, UA1AQC) +#define OPTION_DO_NOT_SEND_UNKNOWN_CHAR_QUESTION +#define OPTION_CMOS_SUPER_KEYER_IAMBIC_B_TIMING_ON_BY_DEFAULT +// #define OPTION_SIDETONE_DIGITAL_OUTPUT_NO_SQUARE_WAVE + +// #define OPTION_DIRECT_PADDLE_PIN_READS_MEGA // only works with Mega and pins 2 and 5 - minor performance increase +// #define OPTION_DIRECT_PADDLE_PIN_READS_UNO // Unos or Nanos pins 2 and 5 - do not enable on a nanoKeyer, it uses different pins + +// #define OPTION_WORDSWORTH_CZECH +// #define OPTION_WORDSWORTH_DEUTSCH +// #define OPTION_WORDSWORTH_NORSK + +// #define OPTION_EXCLUDE_EXTENDED_CLI_COMMANDS + +// #define OPTION_DFROBOT_LCD_COMMAND_BUTTONS + +// #define OPTION_EXCLUDE_MILL_MODE +// #define OPTION_NO_ULTIMATIC // reduce memory usage by removing ultimatic code. + +// #define OPTION_DISABLE_SERIAL_PORT_CHECKING_WHILE_SENDING_CW +// #define OPTION_PERSONALIZED_STARTUP_SCREEN // displays a user defined string of characters on the second or fourth row of the screen during startup. 1602 display requires OPTION_DO_NOT_SAY_HI +// #define OPTION_SWAP_PADDLE_PARAMETER_CHANGE_DIRECTION // reverses the up/down direction when using the paddles to change the wpm or sidetone frequency +// #define OPTION_DISPLAY_MEMORY_CONTENTS_COMMAND_MODE + +// #define OPTION_BEACON_MODE_MEMORY_REPEAT_TIME // to space out the repeated playing of memory 1 when in beacon mode +// #define OPTION_BEACON_MODE_PTT_TAIL_TIME // adds the ptt tail time to each playing of memory 1 in beacon mode + +// #define OPTION_WINKEY_PROSIGN_COMPATIBILITY // Additional character mappings to support K1EL Winkey emulation prosigns \ No newline at end of file diff --git a/k3ng_keyer/keyer_features_and_options_megakeyer.h b/k3ng_keyer/keyer_features_and_options_megakeyer.h new file mode 100755 index 0000000..dc3b94d --- /dev/null +++ b/k3ng_keyer/keyer_features_and_options_megakeyer.h @@ -0,0 +1,135 @@ +// +// Features for W6IPA megakeyer v1.1 (CC) BY-NC-SA +// Project files are available here https://github.com/w6ipa/megakeyer +// + +// FEATURES add more bytes to the compiled binary, OPTIONS change code behavior + + +#define FEATURE_BUTTONS +#define FEATURE_COMMAND_MODE +#define FEATURE_COMMAND_LINE_INTERFACE // Command Line Interface functionality +#define FEATURE_MEMORIES // on the Arduino Due, you must have FEATURE_EEPROM_E24C1024 and E24C1024 EEPROM hardware in order to compile this +#define FEATURE_MEMORY_MACROS +#define FEATURE_WINKEY_EMULATION // disabling Automatic Software Reset is highly recommended (see documentation) +#define FEATURE_BEACON // Go into beacon mode if paddle_left pin is LOW at boot up +#define FEATURE_BEACON_SETTING // Go into beacon mode at boot up if EEPROM setting is enabled (\_ CLI Command) +#define FEATURE_TRAINING_COMMAND_LINE_INTERFACE +// #define FEATURE_POTENTIOMETER // do not enable unless you have a potentiometer connected, otherwise noise will falsely trigger wpm changes +// #define FEATURE_SIDETONE_SWITCH // adds switch control for the sidetone output. requires an external toggle switch (assigned to an arduino pin - see keyer_pin_settings.h). +// #define FEATURE_SIDETONE_NEWTONE // Use the NewTone library, ~1k smaller code size than the standard tone library. Uses timer1 (pins 9 or 10) https://bitbucket.org/teckel12/arduino-new-tone/wiki/Home +#define FEATURE_SERIAL_HELP +// #define FEATURE_HELL +#define FEATURE_PS2_KEYBOARD // Use a PS2 keyboard to send code - Change keyboard layout (non-US) in K3NG_PS2Keyboard.h. Additional options below. +// #define FEATURE_USB_KEYBOARD // Use a USB keyboard to send code - Uncomment three lines in k3ng_keyer.ino (search for note_usb_uncomment_lines) +// #define FEATURE_CW_COMPUTER_KEYBOARD // Have an Arduino Due or Leonardo act as a USB HID (Human Interface Device) keyboard and use the paddle to "type" characters on the computer -- uncomment this line in ino file: #include +// #define FEATURE_DEAD_OP_WATCHDOG +// #define FEATURE_AUTOSPACE +// #define FEATURE_FARNSWORTH +// #define FEATURE_DL2SBA_BANKSWITCH // Switch memory banks feature as described here: http://dl2sba.com/index.php?option=com_content&view=article&id=131:nanokeyer&catid=15:shack&Itemid=27#english +// #define FEATURE_LCD_4BIT // classic LCD disidefplay using 4 I/O lines +// #define FEATURE_LCD_8BIT // classic LCD display using 8 I/O lines +// #define FEATURE_LCD_ADAFRUIT_I2C // Adafruit I2C LCD display using MCP23017 at addr 0x20 +// #define FEATURE_LCD_ADAFRUIT_BACKPACK // Adafruit I2C LCD Backup using MCP23008 (courtesy Josiah Ritchie, KE0BLL) +// #define FEATURE_LCD_YDv1 // YourDuino I2C LCD display with old LCM 1602 V1 ic +// #define FEATURE_LCD1602_N07DH // http://linksprite.com/wiki/index.php5?title=16_X_2_LCD_Keypad_Shield_for_Arduino +// #define FEATURE_LCD_SAINSMART_I2C +// #define FEATURE_LCD_FABO_PCF8574 // https://github.com/FaBoPlatform/FaBoLCD-PCF8574-Library +#define FEATURE_LCD_HD44780 +// #define FEATURE_LCD_MATHERTEL_PCF8574 // https://github.com/mathertel/LiquidCrystal_PCF8574 +// #define FEATURE_LCD_I2C_FDEBRABANDER //https://github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library +// #define FEATURE_CW_DECODER // https://github.com/k3ng/k3ng_cw_keyer/wiki/385-Feature:-CW-Decoder +// #define FEATURE_SLEEP // go to sleep after x minutes to conserve battery power (not compatible with Arduino DUE, may have mixed results with Mega and Mega ADK) +// #define FEATURE_LCD_BACKLIGHT_AUTO_DIM // turn off LCD backlight and/or dim Power Indicator LED after x minutes (LED requires a PWM pin) +#define FEATURE_ROTARY_ENCODER // rotary encoder speed control +#define FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING +// #define FEATURE_USB_MOUSE // Uncomment three lines in k3ng_keyer.ino (search for note_usb_uncomment_lines) +// #define FEATURE_CAPACITIVE_PADDLE_PINS // remove the bypass capacitors on the paddle_left and paddle_right lines when using capactive paddles +// #define FEATURE_LED_RING // Mayhew Labs Led Ring support +#define FEATURE_ALPHABET_SEND_PRACTICE // enables command mode S command - created by Ryan, KC2ZWM +#define FEATURE_COMMAND_MODE_PROGRESSIVE_5_CHAR_ECHO_PRACTICE // enables command mode U +// #define FEATURE_PTT_INTERLOCK +// #define FEATURE_QLF +// #define FEATURE_EEPROM_E24C1024 +// #define FEATURE_STRAIGHT_KEY +// #define FEATURE_DYNAMIC_DAH_TO_DIT_RATIO +// #define FEATURE_PADDLE_ECHO +// #define FEATURE_STRAIGHT_KEY_ECHO +// #define FEATURE_AMERICAN_MORSE +// #define FEATURE_4x4_KEYPAD // code contributed by Jack, W0XR - documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/380-Feature:-Keypad +// #define FEATURE_3x4_KEYPAD // code contributed by Jack, W0XR - documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/380-Feature:-Keypad +// #define FEATURE_SEQUENCER +// #define FEATURE_SD_CARD_SUPPORT +// #define FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT +// #define FEATURE_WEB_SERVER // Details: https://github.com/k3ng/k3ng_cw_keyer/wiki/390-Feature:-Ethernet,-Web-Server,-and-Internet-Linking +// #define FEATURE_INTERNET_LINK // Details: https://github.com/k3ng/k3ng_cw_keyer/wiki/390-Feature:-Ethernet,-Web-Server,-and-Internet-Linking + +// #define FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT // Activate the Command Line interface on the secondary serial port +#define OPTION_PRIMARY_SERIAL_PORT_DEFAULT_WINKEY_EMULATION // Use when activating both FEATURE_WINKEY_EMULATION and FEATURE_COMMAND_LINE_INTERFACE + // simultaneously. This will make Winkey emulation be the default at boot up; + // hold command button down at boot up to activate CLI mode + +// #define OPTION_SUPPRESS_SERIAL_BOOT_MSG +#define OPTION_INCLUDE_PTT_TAIL_FOR_MANUAL_SENDING +#define OPTION_EXCLUDE_PTT_HANG_TIME_FOR_MANUAL_SENDING +// #define OPTION_WINKEY_DISCARD_BYTES_AT_STARTUP // if ASR is not disabled, you may need this to discard errant serial port bytes at startup +// #define OPTION_WINKEY_STRICT_EEPROM_WRITES_MAY_WEAR_OUT_EEPROM // with this activated the unit will write non-volatile settings to EEPROM when set by Winkey commands +// #define OPTION_WINKEY_SEND_WORDSPACE_AT_END_OF_BUFFER +#define OPTION_WINKEY_STRICT_HOST_OPEN // require an admin host open Winkey command before doing any other commands +#define OPTION_WINKEY_2_SUPPORT // comment out to revert to Winkey version 1 emulation +#define OPTION_WINKEY_INTERRUPTS_MEMORY_REPEAT +//#define OPTION_WINKEY_UCXLOG_9600_BAUD // use this only with UCXLog configured for Winkey 9600 baud mode +#define OPTION_WINKEY_2_HOST_CLOSE_NO_SERIAL_PORT_RESET // (Required for Win-Test to function) +// #define OPTION_WINKEY_FREQUENT_STATUS_REPORT // activate this to make Winkey emulation play better with RUMlog and RUMped +#define OPTION_WINKEY_IGNORE_LOWERCASE // Enable for typical K1EL Winkeyer behavior (use for SkookumLogger version 1.10.14 and prior to workaround "r" bug) +#define OPTION_WINKEY_BLINK_PTT_ON_HOST_OPEN +// #define OPTION_WINKEY_SEND_VERSION_ON_HOST_CLOSE +// #define OPTION_WINKEY_PINCONFIG_PTT_CONTROLS_PTT_LINE // Have Winkeyer PTT setting activate/deactivate PTT line rather than control buffered character PTT hold +// #define OPTION_REVERSE_BUTTON_ORDER // This is mainly for the DJ0MY NanoKeyer http://nanokeyer.wordpress.com/ +#define OPTION_PROG_MEM_TRIM_TRAILING_SPACES // trim trailing spaces from memory when programming in command mode +#define OPTION_DIT_PADDLE_NO_SEND_ON_MEM_RPT // this makes dit paddle memory interruption a little smoother +#define OPTION_MORE_DISPLAY_MSGS // additional optional display messages - comment out to save memory +// #define OPTION_WATCHDOG_TIMER // this enables a four second ATmega48/88/168/328 watchdog timer; use for unattended/remote operation only +// #define OPTION_MOUSE_MOVEMENT_PADDLE // experimental (just fooling around) - mouse movement will act like a paddle +// #define OPTION_NON_ENGLISH_EXTENSIONS // add support for additional CW characters (i.e. À, Å, Þ, etc.) +// #define OPTION_DISPLAY_NON_ENGLISH_EXTENSIONS // LCD display suport for non-English (NO/DK/DE) characters - Courtesy of OZ1JHM +// #define OPTION_UNKNOWN_CHARACTER_ERROR_TONE +// #define OPTION_DO_NOT_SAY_HI +// #define OPTION_PS2_NON_ENGLISH_CHAR_LCD_DISPLAY_SUPPORT // makes some non-English characters from the PS2 keyboard display correctly in the LCD display (donated by Marcin sp5iou) +// #define OPTION_PS2_KEYBOARD_RESET // reset the PS2 keyboard upon startup with 0xFF (contributed by Bill, W9BEL) +// #define OPTION_SAVE_MEMORY_NANOKEYER +#define OPTION_CW_KEYBOARD_CAPSLOCK_BEEP +// #define OPTION_CW_KEYBOARD_ITALIAN +// #define OPTION_CW_KEYBOARD_GERMAN +// #define OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR +// #define OPTION_INVERT_PADDLE_PIN_LOGIC +#define OPTION_ADVANCED_SPEED_DISPLAY //enables "nerd" speed visualization on display: wpm, cpm (char per min), duration of dit and dah in milliseconds and ratio (contributed by Giorgio, IZ2XBZ) +#define OPTION_PROSIGN_SUPPORT // additional prosign support for paddle and straight key echo on display, CLI, and in memory storage +// #define OPTION_RUSSIAN_LANGUAGE_SEND_CLI // Russian language CLI sending support (contributed by Павел Бирюков, UA1AQC) +#define OPTION_DO_NOT_SEND_UNKNOWN_CHAR_QUESTION +#define OPTION_CMOS_SUPER_KEYER_IAMBIC_B_TIMING_ON_BY_DEFAULT +// #define OPTION_SIDETONE_DIGITAL_OUTPUT_NO_SQUARE_WAVE + +// #define OPTION_DIRECT_PADDLE_PIN_READS_MEGA // only works with Mega and pins 2 and 5 - minor performance increase +// #define OPTION_DIRECT_PADDLE_PIN_READS_UNO // Unos or Nanos pins 2 and 5 - do not enable on a nanoKeyer, it uses different pins + +// #define OPTION_WORDSWORTH_CZECH +// #define OPTION_WORDSWORTH_DEUTSCH +// #define OPTION_WORDSWORTH_NORSK + +#define OPTION_EXCLUDE_EXTENDED_CLI_COMMANDS + +// #define OPTION_DFROBOT_LCD_COMMAND_BUTTONS + +// #define OPTION_EXCLUDE_MILL_MODE +// #define OPTION_NO_ULTIMATIC // reduce memory usage by removing ultimatic code. + +// #define OPTION_DISABLE_SERIAL_PORT_CHECKING_WHILE_SENDING_CW +// #define OPTION_PERSONALIZED_STARTUP_SCREEN // displays a user defined string of characters on the second or fourth row of the screen during startup. 1602 display requires OPTION_DO_NOT_SAY_HI +// #define OPTION_SWAP_PADDLE_PARAMETER_CHANGE_DIRECTION // reverses the up/down direction when using the paddles to change the wpm or sidetone frequency +// #define OPTION_DISPLAY_MEMORY_CONTENTS_COMMAND_MODE + +// #define OPTION_BEACON_MODE_MEMORY_REPEAT_TIME // to space out the repeated playing of memory 1 when in beacon mode +// #define OPTION_BEACON_MODE_PTT_TAIL_TIME // adds the ptt tail time to each playing of memory 1 in beacon mode + +// #define OPTION_WINKEY_PROSIGN_COMPATIBILITY // Additional character mappings to support K1EL Winkey emulation prosigns diff --git a/k3ng_keyer/keyer_features_and_options_mortty.h b/k3ng_keyer/keyer_features_and_options_mortty.h new file mode 100755 index 0000000..920fca2 --- /dev/null +++ b/k3ng_keyer/keyer_features_and_options_mortty.h @@ -0,0 +1,122 @@ +// compile time features and options - comment or uncomment to add or delete features +// FEATURES add more bytes to the compiled binary, OPTIONS change code behavior + + +// #define FEATURE_BUTTONS +// #define FEATURE_COMMAND_MODE +// #define FEATURE_COMMAND_LINE_INTERFACE // Command Line Interface functionality +// #define FEATURE_MEMORIES // on the Arduino Due, you must have FEATURE_EEPROM_E24C1024 and E24C1024 EEPROM hardware in order to compile this +// #define FEATURE_MEMORY_MACROS +#define FEATURE_WINKEY_EMULATION // disabling Automatic Software Reset is highly recommended (see documentation) +// #define FEATURE_BEACON // Go into beacon mode if paddle_left pin is LOW at boot up +// #define FEATURE_BEACON_SETTING // Go into beacon mode at boot up if EEPROM setting is enabled (\_ CLI Command) +// #define FEATURE_TRAINING_COMMAND_LINE_INTERFACE +// #define FEATURE_POTENTIOMETER // do not enable unless you have a potentiometer connected, otherwise noise will falsely trigger wpm changes +// #define FEATURE_SIDETONE_SWITCH // adds switch control for the sidetone output. requires an external toggle switch (assigned to an arduino pin - see keyer_pin_settings.h). +// #define FEATURE_SIDETONE_NEWTONE // Use the NewTone library, ~1k smaller code size than the standard tone library. Uses timer1 (pins 9 or 10) https://bitbucket.org/teckel12/arduino-new-tone/wiki/Home +// #define FEATURE_SERIAL_HELP +// #define FEATURE_HELL +// #define FEATURE_PS2_KEYBOARD // Use a PS2 keyboard to send code - Change keyboard layout (non-US) in K3NG_PS2Keyboard.h. Additional options below. +// #define FEATURE_USB_KEYBOARD // Use a USB keyboard to send code - Uncomment three lines in k3ng_keyer.ino (search for note_usb_uncomment_lines) +// #define FEATURE_CW_COMPUTER_KEYBOARD // Have an Arduino Due or Leonardo act as a USB HID (Human Interface Device) keyboard and use the paddle to "type" characters on the computer -- uncomment this line in ino file: #include +// #define FEATURE_DEAD_OP_WATCHDOG +// #define FEATURE_AUTOSPACE +// #define FEATURE_FARNSWORTH +// #define FEATURE_DL2SBA_BANKSWITCH // Switch memory banks feature as described here: http://dl2sba.com/index.php?option=com_content&view=article&id=131:nanokeyer&catid=15:shack&Itemid=27#english +// #define FEATURE_LCD_4BIT // classic LCD disidefplay using 4 I/O lines +// #define FEATURE_LCD_ADAFRUIT_I2C // Adafruit I2C LCD display using MCP23017 at addr 0x20 +// #define FEATURE_LCD_ADAFRUIT_BACKPACK // Adafruit I2C LCD Backup using MCP23008 (courtesy Josiah Ritchie, KE0BLL) +// #define FEATURE_LCD_YDv1 // YourDuino I2C LCD display with old LCM 1602 V1 ic +// #define FEATURE_LCD1602_N07DH // http://linksprite.com/wiki/index.php5?title=16_X_2_LCD_Keypad_Shield_for_Arduino +// #define FEATURE_LCD_SAINSMART_I2C +// #define FEATURE_LCD_I2C_FDEBRABANDER //https://github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library +// #define FEATURE_CW_DECODER // https://github.com/k3ng/k3ng_cw_keyer/wiki/385-Feature:-CW-Decoder +// #define FEATURE_SLEEP // go to sleep after x minutes to conserve battery power (not compatible with Arduino DUE, may have mixed results with Mega and Mega ADK) +// #define FEATURE_LCD_BACKLIGHT_AUTO_DIM // turn off LCD backlight and/or dim Power Indicator LED after x minutes (LED requires a PWM pin) +// #define FEATURE_ROTARY_ENCODER // rotary encoder speed control +// #define FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING +// #define FEATURE_USB_MOUSE // Uncomment three lines in k3ng_keyer.ino (search for note_usb_uncomment_lines) +// #define FEATURE_CAPACITIVE_PADDLE_PINS // remove the bypass capacitors on the paddle_left and paddle_right lines when using capactive paddles +// #define FEATURE_LED_RING // Mayhew Labs Led Ring support +// #define FEATURE_ALPHABET_SEND_PRACTICE // enables command mode S command - created by Ryan, KC2ZWM +// #define FEATURE_COMMAND_MODE_PROGRESSIVE_5_CHAR_ECHO_PRACTICE // enables command mode U +// #define FEATURE_PTT_INTERLOCK +// #define FEATURE_QLF +// #define FEATURE_EEPROM_E24C1024 +// #define FEATURE_STRAIGHT_KEY +// #define FEATURE_DYNAMIC_DAH_TO_DIT_RATIO +// #define FEATURE_PADDLE_ECHO +// #define FEATURE_STRAIGHT_KEY_ECHO +// #define FEATURE_AMERICAN_MORSE +// #define FEATURE_4x4_KEYPAD // code contributed by Jack, W0XR - documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/380-Feature:-Keypad +// #define FEATURE_3x4_KEYPAD // code contributed by Jack, W0XR - documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/380-Feature:-Keypad +// #define FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT +// #define FEATURE_WEB_SERVER // Details: https://github.com/k3ng/k3ng_cw_keyer/wiki/390-Feature:-Ethernet,-Web-Server,-and-Internet-Linking +// #define FEATURE_INTERNET_LINK // Details: https://github.com/k3ng/k3ng_cw_keyer/wiki/390-Feature:-Ethernet,-Web-Server,-and-Internet-Linking + +// #define FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT // Activate the Command Line interface on the secondary serial port +#define OPTION_PRIMARY_SERIAL_PORT_DEFAULT_WINKEY_EMULATION // Use when activating both FEATURE_WINKEY_EMULATION and FEATURE_COMMAND_LINE_INTERFACE + // simultaneously. This will make Winkey emulation be the default at boot up; + // hold command button down at boot up to activate CLI mode + +// #define OPTION_SUPPRESS_SERIAL_BOOT_MSG +#define OPTION_INCLUDE_PTT_TAIL_FOR_MANUAL_SENDING +#define OPTION_EXCLUDE_PTT_HANG_TIME_FOR_MANUAL_SENDING +//#define OPTION_WINKEY_DISCARD_BYTES_AT_STARTUP // if ASR is not disabled, you may need this to discard errant serial port bytes at startup +// #define OPTION_WINKEY_STRICT_EEPROM_WRITES_MAY_WEAR_OUT_EEPROM // with this activated the unit will write non-volatile settings to EEPROM when set by Winkey commands +// #define OPTION_WINKEY_SEND_WORDSPACE_AT_END_OF_BUFFER +// #define OPTION_WINKEY_STRICT_HOST_OPEN // require an admin host open Winkey command before doing any other commands +#define OPTION_WINKEY_2_SUPPORT // comment out to revert to Winkey version 1 emulation +#define OPTION_WINKEY_INTERRUPTS_MEMORY_REPEAT +//#define OPTION_WINKEY_UCXLOG_9600_BAUD // use this only with UCXLog configured for Winkey 9600 baud mode +#define OPTION_WINKEY_2_HOST_CLOSE_NO_SERIAL_PORT_RESET // activate this when using Winkey 2 emulation and Win-Test +// #define OPTION_WINKEY_FREQUENT_STATUS_REPORT // activate this to make Winkey emulation play better with RUMlog and RUMped +#define OPTION_WINKEY_IGNORE_LOWERCASE // Enable for typical K1EL Winkeyer behavior (use for SkookumLogger version 1.10.14 and prior to workaround "r" bug) +#define OPTION_WINKEY_BLINK_PTT_ON_HOST_OPEN +// #define OPTION_WINKEY_SEND_VERSION_ON_HOST_CLOSE +// #define OPTION_WINKEY_PINCONFIG_PTT_CONTROLS_PTT_LINE // Have Winkeyer PTT setting activate/deactivate PTT line rather than control buffered character PTT hold +// #define OPTION_REVERSE_BUTTON_ORDER // This is mainly for the DJ0MY NanoKeyer http://nanokeyer.wordpress.com/ +#define OPTION_PROG_MEM_TRIM_TRAILING_SPACES // trim trailing spaces from memory when programming in command mode +#define OPTION_DIT_PADDLE_NO_SEND_ON_MEM_RPT // this makes dit paddle memory interruption a little smoother +// #define OPTION_MORE_DISPLAY_MSGS // additional optional display messages - comment out to save memory +// #define OPTION_WATCHDOG_TIMER // this enables a four second ATmega48/88/168/328 watchdog timer; use for unattended/remote operation only +// #define OPTION_MOUSE_MOVEMENT_PADDLE // experimental (just fooling around) - mouse movement will act like a paddle +// #define OPTION_NON_ENGLISH_EXTENSIONS // add support for additional CW characters (i.e. À, Å, Þ, etc.) +// #define OPTION_DISPLAY_NON_ENGLISH_EXTENSIONS // LCD display suport for non-English (NO/DK/DE) characters - Courtesy of OZ1JHM +// #define OPTION_UNKNOWN_CHARACTER_ERROR_TONE +#define OPTION_DO_NOT_SAY_HI +#define OPTION_BLINK_HI_ON_PTT +// #define OPTION_PS2_NON_ENGLISH_CHAR_LCD_DISPLAY_SUPPORT // makes some non-English characters from the PS2 keyboard display correctly in the LCD display (donated by Marcin sp5iou) +// #define OPTION_PS2_KEYBOARD_RESET // reset the PS2 keyboard upon startup with 0xFF (contributed by Bill, W9BEL) +// #define OPTION_SAVE_MEMORY_NANOKEYER +// #define OPTION_CW_KEYBOARD_CAPSLOCK_BEEP +// #define OPTION_CW_KEYBOARD_ITALIAN +// #define OPTION_CW_KEYBOARD_GERMAN +// #define OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR +// #define OPTION_INVERT_PADDLE_PIN_LOGIC +// #define OPTION_ADVANCED_SPEED_DISPLAY //enables "nerd" speed visualization on display: wpm, cpm (char per min), duration of dit and dah in milliseconds and ratio (contributed by Giorgio, IZ2XBZ) +// #define OPTION_PROSIGN_SUPPORT // additional prosign support for paddle and straight key echo on display, CLI, and in memory storage +// #define OPTION_RUSSIAN_LANGUAGE_SEND_CLI // Russian language CLI sending support (contributed by Павел Бирюков, UA1AQC) +#define OPTION_DO_NOT_SEND_UNKNOWN_CHAR_QUESTION +// #define OPTION_CMOS_SUPER_KEYER_IAMBIC_B_TIMING_ON_BY_DEFAULT +#define OPTION_SIDETONE_DIGITAL_OUTPUT_NO_SQUARE_WAVE + +// #define OPTION_DIRECT_PADDLE_PIN_READS_MEGA // only works with Mega and pins 2 and 5 - minor performance increase +// #define OPTION_DIRECT_PADDLE_PIN_READS_UNO // Unos or Nanos pins 2 and 5 - do not enable on a nanoKeyer, it uses different pins + +// #define OPTION_WORDSWORTH_CZECH +// #define OPTION_WORDSWORTH_DEUTSCH +// #define OPTION_WORDSWORTH_NORSK + +#define OPTION_EXCLUDE_EXTENDED_CLI_COMMANDS +// #define OPTION_NO_ULTIMATIC // reduce memory usage by removing ultimatic code. + +// #define OPTION_DISABLE_SERIAL_PORT_CHECKING_WHILE_SENDING_CW +// #define OPTION_PERSONALIZED_STARTUP_SCREEN // displays a user defined string of characters on the second or fourth row of the screen during startup. 1602 display requires OPTION_DO_NOT_SAY_HI +// #define OPTION_SWAP_PADDLE_PARAMETER_CHANGE_DIRECTION // reverses the up/down direction when using the paddles to change the wpm or sidetone frequency +// #define OPTION_DISPLAY_MEMORY_CONTENTS_COMMAND_MODE + +// #define OPTION_BEACON_MODE_MEMORY_REPEAT_TIME // to space out the repeated playing of memory 1 when in beacon mode +// #define OPTION_BEACON_MODE_PTT_TAIL_TIME // adds the ptt tail time to each playing of memory 1 in beacon mode + +// #define OPTION_WINKEY_PROSIGN_COMPATIBILITY // Additional character mappings to support K1EL Winkey emulation prosigns \ No newline at end of file diff --git a/k3ng_keyer/keyer_features_and_options_mortty_regular.h b/k3ng_keyer/keyer_features_and_options_mortty_regular.h new file mode 100755 index 0000000..692c72e --- /dev/null +++ b/k3ng_keyer/keyer_features_and_options_mortty_regular.h @@ -0,0 +1,119 @@ +// compile time features and options - comment or uncomment to add or delete features +// FEATURES add more bytes to the compiled binary, OPTIONS change code behavior + + +// #define FEATURE_BUTTONS +// #define FEATURE_COMMAND_MODE +// #define FEATURE_COMMAND_LINE_INTERFACE // Command Line Interface functionality +// #define FEATURE_MEMORIES // on the Arduino Due, you must have FEATURE_EEPROM_E24C1024 and E24C1024 EEPROM hardware in order to compile this +// #define FEATURE_MEMORY_MACROS +#define FEATURE_WINKEY_EMULATION // disabling Automatic Software Reset is highly recommended (see documentation) +// #define FEATURE_BEACON // Go into beacon mode if paddle_left pin is LOW at boot up +// #define FEATURE_BEACON_SETTING // Go into beacon mode at boot up if EEPROM setting is enabled (\_ CLI Command) +// #define FEATURE_TRAINING_COMMAND_LINE_INTERFACE +// #define FEATURE_POTENTIOMETER // do not enable unless you have a potentiometer connected, otherwise noise will falsely trigger wpm changes +// #define FEATURE_SIDETONE_SWITCH // adds switch control for the sidetone output. requires an external toggle switch (assigned to an arduino pin - see keyer_pin_settings.h). +// #define FEATURE_SIDETONE_NEWTONE // Use the NewTone library, ~1k smaller code size than the standard tone library. Uses timer1 (pins 9 or 10) https://bitbucket.org/teckel12/arduino-new-tone/wiki/Home +// #define FEATURE_SERIAL_HELP +// #define FEATURE_HELL +// #define FEATURE_PS2_KEYBOARD // Use a PS2 keyboard to send code - Change keyboard layout (non-US) in K3NG_PS2Keyboard.h. Additional options below. +// #define FEATURE_USB_KEYBOARD // Use a USB keyboard to send code - Uncomment three lines in k3ng_keyer.ino (search for note_usb_uncomment_lines) +// #define FEATURE_CW_COMPUTER_KEYBOARD // Have an Arduino Due or Leonardo act as a USB HID (Human Interface Device) keyboard and use the paddle to "type" characters on the computer -- uncomment this line in ino file: #include +// #define FEATURE_DEAD_OP_WATCHDOG +// #define FEATURE_AUTOSPACE +// #define FEATURE_FARNSWORTH +// #define FEATURE_DL2SBA_BANKSWITCH // Switch memory banks feature as described here: http://dl2sba.com/index.php?option=com_content&view=article&id=131:nanokeyer&catid=15:shack&Itemid=27#english +// #define FEATURE_LCD_4BIT // classic LCD disidefplay using 4 I/O lines +// #define FEATURE_LCD_ADAFRUIT_I2C // Adafruit I2C LCD display using MCP23017 at addr 0x20 +// #define FEATURE_LCD_ADAFRUIT_BACKPACK // Adafruit I2C LCD Backup using MCP23008 (courtesy Josiah Ritchie, KE0BLL) +// #define FEATURE_LCD_YDv1 // YourDuino I2C LCD display with old LCM 1602 V1 ic +// #define FEATURE_LCD1602_N07DH // http://linksprite.com/wiki/index.php5?title=16_X_2_LCD_Keypad_Shield_for_Arduino +// #define FEATURE_LCD_SAINSMART_I2C +// #define FEATURE_LCD_I2C_FDEBRABANDER //https://github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library +// #define FEATURE_CW_DECODER // https://github.com/k3ng/k3ng_cw_keyer/wiki/385-Feature:-CW-Decoder +// #define FEATURE_SLEEP // go to sleep after x minutes to conserve battery power (not compatible with Arduino DUE, may have mixed results with Mega and Mega ADK) +// #define FEATURE_LCD_BACKLIGHT_AUTO_DIM // turn off LCD backlight and/or dim Power Indicator LED after x minutes (LED requires a PWM pin) +// #define FEATURE_ROTARY_ENCODER // rotary encoder speed control +// #define FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING +// #define FEATURE_USB_MOUSE // Uncomment three lines in k3ng_keyer.ino (search for note_usb_uncomment_lines) +// #define FEATURE_CAPACITIVE_PADDLE_PINS // remove the bypass capacitors on the paddle_left and paddle_right lines when using capactive paddles +// #define FEATURE_LED_RING // Mayhew Labs Led Ring support +// #define FEATURE_ALPHABET_SEND_PRACTICE // enables command mode S command - created by Ryan, KC2ZWM +// #define FEATURE_COMMAND_MODE_PROGRESSIVE_5_CHAR_ECHO_PRACTICE // enables command mode U +// #define FEATURE_PTT_INTERLOCK +// #define FEATURE_QLF +// #define FEATURE_EEPROM_E24C1024 +// #define FEATURE_STRAIGHT_KEY +// #define FEATURE_DYNAMIC_DAH_TO_DIT_RATIO +// #define FEATURE_PADDLE_ECHO +// #define FEATURE_STRAIGHT_KEY_ECHO +// #define FEATURE_AMERICAN_MORSE +// #define FEATURE_4x4_KEYPAD // code contributed by Jack, W0XR - documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/380-Feature:-Keypad +// #define FEATURE_3x4_KEYPAD // code contributed by Jack, W0XR - documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/380-Feature:-Keypad +// #define FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT +// #define FEATURE_WEB_SERVER // Details: https://github.com/k3ng/k3ng_cw_keyer/wiki/390-Feature:-Ethernet,-Web-Server,-and-Internet-Linking +// #define FEATURE_INTERNET_LINK // Details: https://github.com/k3ng/k3ng_cw_keyer/wiki/390-Feature:-Ethernet,-Web-Server,-and-Internet-Linking + +// #define FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT // Activate the Command Line interface on the secondary serial port +#define OPTION_PRIMARY_SERIAL_PORT_DEFAULT_WINKEY_EMULATION // Use when activating both FEATURE_WINKEY_EMULATION and FEATURE_COMMAND_LINE_INTERFACE + // simultaneously. This will make Winkey emulation be the default at boot up; + // hold command button down at boot up to activate CLI mode + +// #define OPTION_SUPPRESS_SERIAL_BOOT_MSG +#define OPTION_INCLUDE_PTT_TAIL_FOR_MANUAL_SENDING +#define OPTION_EXCLUDE_PTT_HANG_TIME_FOR_MANUAL_SENDING +//#define OPTION_WINKEY_DISCARD_BYTES_AT_STARTUP // if ASR is not disabled, you may need this to discard errant serial port bytes at startup +// #define OPTION_WINKEY_STRICT_EEPROM_WRITES_MAY_WEAR_OUT_EEPROM // with this activated the unit will write non-volatile settings to EEPROM when set by Winkey commands +// #define OPTION_WINKEY_SEND_WORDSPACE_AT_END_OF_BUFFER +// #define OPTION_WINKEY_STRICT_HOST_OPEN // require an admin host open Winkey command before doing any other commands +#define OPTION_WINKEY_2_SUPPORT // comment out to revert to Winkey version 1 emulation +#define OPTION_WINKEY_INTERRUPTS_MEMORY_REPEAT +//#define OPTION_WINKEY_UCXLOG_9600_BAUD // use this only with UCXLog configured for Winkey 9600 baud mode +#define OPTION_WINKEY_2_HOST_CLOSE_NO_SERIAL_PORT_RESET // activate this when using Winkey 2 emulation and Win-Test +// #define OPTION_WINKEY_FREQUENT_STATUS_REPORT // activate this to make Winkey emulation play better with RUMlog and RUMped +#define OPTION_WINKEY_IGNORE_LOWERCASE // Enable for typical K1EL Winkeyer behavior (use for SkookumLogger version 1.10.14 and prior to workaround "r" bug) +#define OPTION_WINKEY_BLINK_PTT_ON_HOST_OPEN +// #define OPTION_WINKEY_SEND_VERSION_ON_HOST_CLOSE +// #define OPTION_WINKEY_PINCONFIG_PTT_CONTROLS_PTT_LINE // Have Winkeyer PTT setting activate/deactivate PTT line rather than control buffered character PTT hold +// #define OPTION_REVERSE_BUTTON_ORDER // This is mainly for the DJ0MY NanoKeyer http://nanokeyer.wordpress.com/ +#define OPTION_PROG_MEM_TRIM_TRAILING_SPACES // trim trailing spaces from memory when programming in command mode +#define OPTION_DIT_PADDLE_NO_SEND_ON_MEM_RPT // this makes dit paddle memory interruption a little smoother +// #define OPTION_MORE_DISPLAY_MSGS // additional optional display messages - comment out to save memory +// #define OPTION_N1MM_WINKEY_TAB_BUG_WORKAROUND // enable this to ignore the TAB key in the Send CW window (this breaks SO2R functionality in N1MM) +// #define OPTION_WATCHDOG_TIMER // this enables a four second ATmega48/88/168/328 watchdog timer; use for unattended/remote operation only +// #define OPTION_MOUSE_MOVEMENT_PADDLE // experimental (just fooling around) - mouse movement will act like a paddle +// #define OPTION_NON_ENGLISH_EXTENSIONS // add support for additional CW characters (i.e. À, Å, Þ, etc.) +// #define OPTION_DISPLAY_NON_ENGLISH_EXTENSIONS // LCD display suport for non-English (NO/DK/DE) characters - Courtesy of OZ1JHM +// #define OPTION_UNKNOWN_CHARACTER_ERROR_TONE +#define OPTION_DO_NOT_SAY_HI +#define OPTION_BLINK_HI_ON_PTT +// #define OPTION_PS2_NON_ENGLISH_CHAR_LCD_DISPLAY_SUPPORT // makes some non-English characters from the PS2 keyboard display correctly in the LCD display (donated by Marcin sp5iou) +// #define OPTION_PS2_KEYBOARD_RESET // reset the PS2 keyboard upon startup with 0xFF (contributed by Bill, W9BEL) +// #define OPTION_SAVE_MEMORY_NANOKEYER +// #define OPTION_CW_KEYBOARD_CAPSLOCK_BEEP +// #define OPTION_CW_KEYBOARD_ITALIAN +// #define OPTION_CW_KEYBOARD_GERMAN +// #define OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR +// #define OPTION_INVERT_PADDLE_PIN_LOGIC +// #define OPTION_ADVANCED_SPEED_DISPLAY //enables "nerd" speed visualization on display: wpm, cpm (char per min), duration of dit and dah in milliseconds and ratio (contributed by Giorgio, IZ2XBZ) +// #define OPTION_PROSIGN_SUPPORT // additional prosign support for paddle and straight key echo on display, CLI, and in memory storage +// #define OPTION_RUSSIAN_LANGUAGE_SEND_CLI // Russian language CLI sending support (contributed by Павел Бирюков, UA1AQC) +#define OPTION_DO_NOT_SEND_UNKNOWN_CHAR_QUESTION +// #define OPTION_CMOS_SUPER_KEYER_IAMBIC_B_TIMING_ON_BY_DEFAULT +#define OPTION_SIDETONE_DIGITAL_OUTPUT_NO_SQUARE_WAVE + +// #define OPTION_WORDSWORTH_CZECH +// #define OPTION_WORDSWORTH_DEUTSCH +// #define OPTION_WORDSWORTH_NORSK + +#define OPTION_EXCLUDE_EXTENDED_CLI_COMMANDS +// #define OPTION_NO_ULTIMATIC // reduce memory usage by removing ultimatic code. + +// #define OPTION_PERSONALIZED_STARTUP_SCREEN // displays a user defined string of characters on the second or fourth row of the screen during startup. 1602 display requires OPTION_DO_NOT_SAY_HI +// #define OPTION_SWAP_PADDLE_PARAMETER_CHANGE_DIRECTION // reverses the up/down direction when using the paddles to change the wpm or sidetone frequency +// #define OPTION_DISPLAY_MEMORY_CONTENTS_COMMAND_MODE + +// #define OPTION_BEACON_MODE_MEMORY_REPEAT_TIME // to space out the repeated playing of memory 1 when in beacon mode +// #define OPTION_BEACON_MODE_PTT_TAIL_TIME // adds the ptt tail time to each playing of memory 1 in beacon mode + +// #define OPTION_WINKEY_PROSIGN_COMPATIBILITY // Additional character mappings to support K1EL Winkey emulation prosigns diff --git a/k3ng_keyer/keyer_features_and_options_mortty_regular_with_potentiometer.h b/k3ng_keyer/keyer_features_and_options_mortty_regular_with_potentiometer.h new file mode 100755 index 0000000..e562ee9 --- /dev/null +++ b/k3ng_keyer/keyer_features_and_options_mortty_regular_with_potentiometer.h @@ -0,0 +1,119 @@ +// compile time features and options - comment or uncomment to add or delete features +// FEATURES add more bytes to the compiled binary, OPTIONS change code behavior + + +// #define FEATURE_BUTTONS +// #define FEATURE_COMMAND_MODE +// #define FEATURE_COMMAND_LINE_INTERFACE // Command Line Interface functionality +// #define FEATURE_MEMORIES // on the Arduino Due, you must have FEATURE_EEPROM_E24C1024 and E24C1024 EEPROM hardware in order to compile this +// #define FEATURE_MEMORY_MACROS +#define FEATURE_WINKEY_EMULATION // disabling Automatic Software Reset is highly recommended (see documentation) +// #define FEATURE_BEACON // Go into beacon mode if paddle_left pin is LOW at boot up +// #define FEATURE_BEACON_SETTING // Go into beacon mode at boot up if EEPROM setting is enabled (\_ CLI Command) +// #define FEATURE_TRAINING_COMMAND_LINE_INTERFACE +#define FEATURE_POTENTIOMETER // do not enable unless you have a potentiometer connected, otherwise noise will falsely trigger wpm changes +// #define FEATURE_SIDETONE_SWITCH // adds switch control for the sidetone output. requires an external toggle switch (assigned to an arduino pin - see keyer_pin_settings.h). +// #define FEATURE_SIDETONE_NEWTONE // Use the NewTone library, ~1k smaller code size than the standard tone library. Uses timer1 (pins 9 or 10) https://bitbucket.org/teckel12/arduino-new-tone/wiki/Home +// #define FEATURE_SERIAL_HELP +// #define FEATURE_HELL +// #define FEATURE_PS2_KEYBOARD // Use a PS2 keyboard to send code - Change keyboard layout (non-US) in K3NG_PS2Keyboard.h. Additional options below. +// #define FEATURE_USB_KEYBOARD // Use a USB keyboard to send code - Uncomment three lines in k3ng_keyer.ino (search for note_usb_uncomment_lines) +// #define FEATURE_CW_COMPUTER_KEYBOARD // Have an Arduino Due or Leonardo act as a USB HID (Human Interface Device) keyboard and use the paddle to "type" characters on the computer -- uncomment this line in ino file: #include +// #define FEATURE_DEAD_OP_WATCHDOG +// #define FEATURE_AUTOSPACE +// #define FEATURE_FARNSWORTH +// #define FEATURE_DL2SBA_BANKSWITCH // Switch memory banks feature as described here: http://dl2sba.com/index.php?option=com_content&view=article&id=131:nanokeyer&catid=15:shack&Itemid=27#english +// #define FEATURE_LCD_4BIT // classic LCD disidefplay using 4 I/O lines +// #define FEATURE_LCD_ADAFRUIT_I2C // Adafruit I2C LCD display using MCP23017 at addr 0x20 +// #define FEATURE_LCD_ADAFRUIT_BACKPACK // Adafruit I2C LCD Backup using MCP23008 (courtesy Josiah Ritchie, KE0BLL) +// #define FEATURE_LCD_YDv1 // YourDuino I2C LCD display with old LCM 1602 V1 ic +// #define FEATURE_LCD1602_N07DH // http://linksprite.com/wiki/index.php5?title=16_X_2_LCD_Keypad_Shield_for_Arduino +// #define FEATURE_LCD_SAINSMART_I2C +// #define FEATURE_LCD_I2C_FDEBRABANDER //https://github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library +// #define FEATURE_CW_DECODER // https://github.com/k3ng/k3ng_cw_keyer/wiki/385-Feature:-CW-Decoder +// #define FEATURE_SLEEP // go to sleep after x minutes to conserve battery power (not compatible with Arduino DUE, may have mixed results with Mega and Mega ADK) +// #define FEATURE_LCD_BACKLIGHT_AUTO_DIM // turn off LCD backlight and/or dim Power Indicator LED after x minutes (LED requires a PWM pin) +// #define FEATURE_ROTARY_ENCODER // rotary encoder speed control +// #define FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING +// #define FEATURE_USB_MOUSE // Uncomment three lines in k3ng_keyer.ino (search for note_usb_uncomment_lines) +// #define FEATURE_CAPACITIVE_PADDLE_PINS // remove the bypass capacitors on the paddle_left and paddle_right lines when using capactive paddles +// #define FEATURE_LED_RING // Mayhew Labs Led Ring support +// #define FEATURE_ALPHABET_SEND_PRACTICE // enables command mode S command - created by Ryan, KC2ZWM +// #define FEATURE_COMMAND_MODE_PROGRESSIVE_5_CHAR_ECHO_PRACTICE // enables command mode U +// #define FEATURE_PTT_INTERLOCK +// #define FEATURE_QLF +// #define FEATURE_EEPROM_E24C1024 +// #define FEATURE_STRAIGHT_KEY +// #define FEATURE_DYNAMIC_DAH_TO_DIT_RATIO +// #define FEATURE_PADDLE_ECHO +// #define FEATURE_STRAIGHT_KEY_ECHO +// #define FEATURE_AMERICAN_MORSE +// #define FEATURE_4x4_KEYPAD // code contributed by Jack, W0XR - documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/380-Feature:-Keypad +// #define FEATURE_3x4_KEYPAD // code contributed by Jack, W0XR - documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/380-Feature:-Keypad +// #define FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT +// #define FEATURE_WEB_SERVER // Details: https://github.com/k3ng/k3ng_cw_keyer/wiki/390-Feature:-Ethernet,-Web-Server,-and-Internet-Linking +// #define FEATURE_INTERNET_LINK // Details: https://github.com/k3ng/k3ng_cw_keyer/wiki/390-Feature:-Ethernet,-Web-Server,-and-Internet-Linking + +// #define FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT // Activate the Command Line interface on the secondary serial port +#define OPTION_PRIMARY_SERIAL_PORT_DEFAULT_WINKEY_EMULATION // Use when activating both FEATURE_WINKEY_EMULATION and FEATURE_COMMAND_LINE_INTERFACE + // simultaneously. This will make Winkey emulation be the default at boot up; + // hold command button down at boot up to activate CLI mode + +// #define OPTION_SUPPRESS_SERIAL_BOOT_MSG +#define OPTION_INCLUDE_PTT_TAIL_FOR_MANUAL_SENDING +#define OPTION_EXCLUDE_PTT_HANG_TIME_FOR_MANUAL_SENDING +//#define OPTION_WINKEY_DISCARD_BYTES_AT_STARTUP // if ASR is not disabled, you may need this to discard errant serial port bytes at startup +// #define OPTION_WINKEY_STRICT_EEPROM_WRITES_MAY_WEAR_OUT_EEPROM // with this activated the unit will write non-volatile settings to EEPROM when set by Winkey commands +// #define OPTION_WINKEY_SEND_WORDSPACE_AT_END_OF_BUFFER +// #define OPTION_WINKEY_STRICT_HOST_OPEN // require an admin host open Winkey command before doing any other commands +#define OPTION_WINKEY_2_SUPPORT // comment out to revert to Winkey version 1 emulation +#define OPTION_WINKEY_INTERRUPTS_MEMORY_REPEAT +//#define OPTION_WINKEY_UCXLOG_9600_BAUD // use this only with UCXLog configured for Winkey 9600 baud mode +#define OPTION_WINKEY_2_HOST_CLOSE_NO_SERIAL_PORT_RESET // activate this when using Winkey 2 emulation and Win-Test +// #define OPTION_WINKEY_FREQUENT_STATUS_REPORT // activate this to make Winkey emulation play better with RUMlog and RUMped +#define OPTION_WINKEY_IGNORE_LOWERCASE // Enable for typical K1EL Winkeyer behavior (use for SkookumLogger version 1.10.14 and prior to workaround "r" bug) +#define OPTION_WINKEY_BLINK_PTT_ON_HOST_OPEN +// #define OPTION_WINKEY_SEND_VERSION_ON_HOST_CLOSE +// #define OPTION_WINKEY_PINCONFIG_PTT_CONTROLS_PTT_LINE // Have Winkeyer PTT setting activate/deactivate PTT line rather than control buffered character PTT hold +// #define OPTION_REVERSE_BUTTON_ORDER // This is mainly for the DJ0MY NanoKeyer http://nanokeyer.wordpress.com/ +#define OPTION_PROG_MEM_TRIM_TRAILING_SPACES // trim trailing spaces from memory when programming in command mode +#define OPTION_DIT_PADDLE_NO_SEND_ON_MEM_RPT // this makes dit paddle memory interruption a little smoother +// #define OPTION_MORE_DISPLAY_MSGS // additional optional display messages - comment out to save memory +// #define OPTION_N1MM_WINKEY_TAB_BUG_WORKAROUND // enable this to ignore the TAB key in the Send CW window (this breaks SO2R functionality in N1MM) +// #define OPTION_WATCHDOG_TIMER // this enables a four second ATmega48/88/168/328 watchdog timer; use for unattended/remote operation only +// #define OPTION_MOUSE_MOVEMENT_PADDLE // experimental (just fooling around) - mouse movement will act like a paddle +// #define OPTION_NON_ENGLISH_EXTENSIONS // add support for additional CW characters (i.e. À, Å, Þ, etc.) +// #define OPTION_DISPLAY_NON_ENGLISH_EXTENSIONS // LCD display suport for non-English (NO/DK/DE) characters - Courtesy of OZ1JHM +// #define OPTION_UNKNOWN_CHARACTER_ERROR_TONE +#define OPTION_DO_NOT_SAY_HI +#define OPTION_BLINK_HI_ON_PTT +// #define OPTION_PS2_NON_ENGLISH_CHAR_LCD_DISPLAY_SUPPORT // makes some non-English characters from the PS2 keyboard display correctly in the LCD display (donated by Marcin sp5iou) +// #define OPTION_PS2_KEYBOARD_RESET // reset the PS2 keyboard upon startup with 0xFF (contributed by Bill, W9BEL) +// #define OPTION_SAVE_MEMORY_NANOKEYER +// #define OPTION_CW_KEYBOARD_CAPSLOCK_BEEP +// #define OPTION_CW_KEYBOARD_ITALIAN +// #define OPTION_CW_KEYBOARD_GERMAN +// #define OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR +// #define OPTION_INVERT_PADDLE_PIN_LOGIC +// #define OPTION_ADVANCED_SPEED_DISPLAY //enables "nerd" speed visualization on display: wpm, cpm (char per min), duration of dit and dah in milliseconds and ratio (contributed by Giorgio, IZ2XBZ) +// #define OPTION_PROSIGN_SUPPORT // additional prosign support for paddle and straight key echo on display, CLI, and in memory storage +// #define OPTION_RUSSIAN_LANGUAGE_SEND_CLI // Russian language CLI sending support (contributed by Павел Бирюков, UA1AQC) +#define OPTION_DO_NOT_SEND_UNKNOWN_CHAR_QUESTION +// #define OPTION_CMOS_SUPER_KEYER_IAMBIC_B_TIMING_ON_BY_DEFAULT +#define OPTION_SIDETONE_DIGITAL_OUTPUT_NO_SQUARE_WAVE + +// #define OPTION_WORDSWORTH_CZECH +// #define OPTION_WORDSWORTH_DEUTSCH +// #define OPTION_WORDSWORTH_NORSK + +#define OPTION_EXCLUDE_EXTENDED_CLI_COMMANDS +// #define OPTION_NO_ULTIMATIC // reduce memory usage by removing ultimatic code. + +// #define OPTION_PERSONALIZED_STARTUP_SCREEN // displays a user defined string of characters on the second or fourth row of the screen during startup. 1602 display requires OPTION_DO_NOT_SAY_HI +// #define OPTION_SWAP_PADDLE_PARAMETER_CHANGE_DIRECTION // reverses the up/down direction when using the paddles to change the wpm or sidetone frequency +// #define OPTION_DISPLAY_MEMORY_CONTENTS_COMMAND_MODE + +// #define OPTION_BEACON_MODE_MEMORY_REPEAT_TIME // to space out the repeated playing of memory 1 when in beacon mode +// #define OPTION_BEACON_MODE_PTT_TAIL_TIME // adds the ptt tail time to each playing of memory 1 in beacon mode + +// #define OPTION_WINKEY_PROSIGN_COMPATIBILITY // Additional character mappings to support K1EL Winkey emulation prosigns diff --git a/k3ng_keyer/keyer_features_and_options_mortty_so2r.h b/k3ng_keyer/keyer_features_and_options_mortty_so2r.h new file mode 100755 index 0000000..692c72e --- /dev/null +++ b/k3ng_keyer/keyer_features_and_options_mortty_so2r.h @@ -0,0 +1,119 @@ +// compile time features and options - comment or uncomment to add or delete features +// FEATURES add more bytes to the compiled binary, OPTIONS change code behavior + + +// #define FEATURE_BUTTONS +// #define FEATURE_COMMAND_MODE +// #define FEATURE_COMMAND_LINE_INTERFACE // Command Line Interface functionality +// #define FEATURE_MEMORIES // on the Arduino Due, you must have FEATURE_EEPROM_E24C1024 and E24C1024 EEPROM hardware in order to compile this +// #define FEATURE_MEMORY_MACROS +#define FEATURE_WINKEY_EMULATION // disabling Automatic Software Reset is highly recommended (see documentation) +// #define FEATURE_BEACON // Go into beacon mode if paddle_left pin is LOW at boot up +// #define FEATURE_BEACON_SETTING // Go into beacon mode at boot up if EEPROM setting is enabled (\_ CLI Command) +// #define FEATURE_TRAINING_COMMAND_LINE_INTERFACE +// #define FEATURE_POTENTIOMETER // do not enable unless you have a potentiometer connected, otherwise noise will falsely trigger wpm changes +// #define FEATURE_SIDETONE_SWITCH // adds switch control for the sidetone output. requires an external toggle switch (assigned to an arduino pin - see keyer_pin_settings.h). +// #define FEATURE_SIDETONE_NEWTONE // Use the NewTone library, ~1k smaller code size than the standard tone library. Uses timer1 (pins 9 or 10) https://bitbucket.org/teckel12/arduino-new-tone/wiki/Home +// #define FEATURE_SERIAL_HELP +// #define FEATURE_HELL +// #define FEATURE_PS2_KEYBOARD // Use a PS2 keyboard to send code - Change keyboard layout (non-US) in K3NG_PS2Keyboard.h. Additional options below. +// #define FEATURE_USB_KEYBOARD // Use a USB keyboard to send code - Uncomment three lines in k3ng_keyer.ino (search for note_usb_uncomment_lines) +// #define FEATURE_CW_COMPUTER_KEYBOARD // Have an Arduino Due or Leonardo act as a USB HID (Human Interface Device) keyboard and use the paddle to "type" characters on the computer -- uncomment this line in ino file: #include +// #define FEATURE_DEAD_OP_WATCHDOG +// #define FEATURE_AUTOSPACE +// #define FEATURE_FARNSWORTH +// #define FEATURE_DL2SBA_BANKSWITCH // Switch memory banks feature as described here: http://dl2sba.com/index.php?option=com_content&view=article&id=131:nanokeyer&catid=15:shack&Itemid=27#english +// #define FEATURE_LCD_4BIT // classic LCD disidefplay using 4 I/O lines +// #define FEATURE_LCD_ADAFRUIT_I2C // Adafruit I2C LCD display using MCP23017 at addr 0x20 +// #define FEATURE_LCD_ADAFRUIT_BACKPACK // Adafruit I2C LCD Backup using MCP23008 (courtesy Josiah Ritchie, KE0BLL) +// #define FEATURE_LCD_YDv1 // YourDuino I2C LCD display with old LCM 1602 V1 ic +// #define FEATURE_LCD1602_N07DH // http://linksprite.com/wiki/index.php5?title=16_X_2_LCD_Keypad_Shield_for_Arduino +// #define FEATURE_LCD_SAINSMART_I2C +// #define FEATURE_LCD_I2C_FDEBRABANDER //https://github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library +// #define FEATURE_CW_DECODER // https://github.com/k3ng/k3ng_cw_keyer/wiki/385-Feature:-CW-Decoder +// #define FEATURE_SLEEP // go to sleep after x minutes to conserve battery power (not compatible with Arduino DUE, may have mixed results with Mega and Mega ADK) +// #define FEATURE_LCD_BACKLIGHT_AUTO_DIM // turn off LCD backlight and/or dim Power Indicator LED after x minutes (LED requires a PWM pin) +// #define FEATURE_ROTARY_ENCODER // rotary encoder speed control +// #define FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING +// #define FEATURE_USB_MOUSE // Uncomment three lines in k3ng_keyer.ino (search for note_usb_uncomment_lines) +// #define FEATURE_CAPACITIVE_PADDLE_PINS // remove the bypass capacitors on the paddle_left and paddle_right lines when using capactive paddles +// #define FEATURE_LED_RING // Mayhew Labs Led Ring support +// #define FEATURE_ALPHABET_SEND_PRACTICE // enables command mode S command - created by Ryan, KC2ZWM +// #define FEATURE_COMMAND_MODE_PROGRESSIVE_5_CHAR_ECHO_PRACTICE // enables command mode U +// #define FEATURE_PTT_INTERLOCK +// #define FEATURE_QLF +// #define FEATURE_EEPROM_E24C1024 +// #define FEATURE_STRAIGHT_KEY +// #define FEATURE_DYNAMIC_DAH_TO_DIT_RATIO +// #define FEATURE_PADDLE_ECHO +// #define FEATURE_STRAIGHT_KEY_ECHO +// #define FEATURE_AMERICAN_MORSE +// #define FEATURE_4x4_KEYPAD // code contributed by Jack, W0XR - documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/380-Feature:-Keypad +// #define FEATURE_3x4_KEYPAD // code contributed by Jack, W0XR - documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/380-Feature:-Keypad +// #define FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT +// #define FEATURE_WEB_SERVER // Details: https://github.com/k3ng/k3ng_cw_keyer/wiki/390-Feature:-Ethernet,-Web-Server,-and-Internet-Linking +// #define FEATURE_INTERNET_LINK // Details: https://github.com/k3ng/k3ng_cw_keyer/wiki/390-Feature:-Ethernet,-Web-Server,-and-Internet-Linking + +// #define FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT // Activate the Command Line interface on the secondary serial port +#define OPTION_PRIMARY_SERIAL_PORT_DEFAULT_WINKEY_EMULATION // Use when activating both FEATURE_WINKEY_EMULATION and FEATURE_COMMAND_LINE_INTERFACE + // simultaneously. This will make Winkey emulation be the default at boot up; + // hold command button down at boot up to activate CLI mode + +// #define OPTION_SUPPRESS_SERIAL_BOOT_MSG +#define OPTION_INCLUDE_PTT_TAIL_FOR_MANUAL_SENDING +#define OPTION_EXCLUDE_PTT_HANG_TIME_FOR_MANUAL_SENDING +//#define OPTION_WINKEY_DISCARD_BYTES_AT_STARTUP // if ASR is not disabled, you may need this to discard errant serial port bytes at startup +// #define OPTION_WINKEY_STRICT_EEPROM_WRITES_MAY_WEAR_OUT_EEPROM // with this activated the unit will write non-volatile settings to EEPROM when set by Winkey commands +// #define OPTION_WINKEY_SEND_WORDSPACE_AT_END_OF_BUFFER +// #define OPTION_WINKEY_STRICT_HOST_OPEN // require an admin host open Winkey command before doing any other commands +#define OPTION_WINKEY_2_SUPPORT // comment out to revert to Winkey version 1 emulation +#define OPTION_WINKEY_INTERRUPTS_MEMORY_REPEAT +//#define OPTION_WINKEY_UCXLOG_9600_BAUD // use this only with UCXLog configured for Winkey 9600 baud mode +#define OPTION_WINKEY_2_HOST_CLOSE_NO_SERIAL_PORT_RESET // activate this when using Winkey 2 emulation and Win-Test +// #define OPTION_WINKEY_FREQUENT_STATUS_REPORT // activate this to make Winkey emulation play better with RUMlog and RUMped +#define OPTION_WINKEY_IGNORE_LOWERCASE // Enable for typical K1EL Winkeyer behavior (use for SkookumLogger version 1.10.14 and prior to workaround "r" bug) +#define OPTION_WINKEY_BLINK_PTT_ON_HOST_OPEN +// #define OPTION_WINKEY_SEND_VERSION_ON_HOST_CLOSE +// #define OPTION_WINKEY_PINCONFIG_PTT_CONTROLS_PTT_LINE // Have Winkeyer PTT setting activate/deactivate PTT line rather than control buffered character PTT hold +// #define OPTION_REVERSE_BUTTON_ORDER // This is mainly for the DJ0MY NanoKeyer http://nanokeyer.wordpress.com/ +#define OPTION_PROG_MEM_TRIM_TRAILING_SPACES // trim trailing spaces from memory when programming in command mode +#define OPTION_DIT_PADDLE_NO_SEND_ON_MEM_RPT // this makes dit paddle memory interruption a little smoother +// #define OPTION_MORE_DISPLAY_MSGS // additional optional display messages - comment out to save memory +// #define OPTION_N1MM_WINKEY_TAB_BUG_WORKAROUND // enable this to ignore the TAB key in the Send CW window (this breaks SO2R functionality in N1MM) +// #define OPTION_WATCHDOG_TIMER // this enables a four second ATmega48/88/168/328 watchdog timer; use for unattended/remote operation only +// #define OPTION_MOUSE_MOVEMENT_PADDLE // experimental (just fooling around) - mouse movement will act like a paddle +// #define OPTION_NON_ENGLISH_EXTENSIONS // add support for additional CW characters (i.e. À, Å, Þ, etc.) +// #define OPTION_DISPLAY_NON_ENGLISH_EXTENSIONS // LCD display suport for non-English (NO/DK/DE) characters - Courtesy of OZ1JHM +// #define OPTION_UNKNOWN_CHARACTER_ERROR_TONE +#define OPTION_DO_NOT_SAY_HI +#define OPTION_BLINK_HI_ON_PTT +// #define OPTION_PS2_NON_ENGLISH_CHAR_LCD_DISPLAY_SUPPORT // makes some non-English characters from the PS2 keyboard display correctly in the LCD display (donated by Marcin sp5iou) +// #define OPTION_PS2_KEYBOARD_RESET // reset the PS2 keyboard upon startup with 0xFF (contributed by Bill, W9BEL) +// #define OPTION_SAVE_MEMORY_NANOKEYER +// #define OPTION_CW_KEYBOARD_CAPSLOCK_BEEP +// #define OPTION_CW_KEYBOARD_ITALIAN +// #define OPTION_CW_KEYBOARD_GERMAN +// #define OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR +// #define OPTION_INVERT_PADDLE_PIN_LOGIC +// #define OPTION_ADVANCED_SPEED_DISPLAY //enables "nerd" speed visualization on display: wpm, cpm (char per min), duration of dit and dah in milliseconds and ratio (contributed by Giorgio, IZ2XBZ) +// #define OPTION_PROSIGN_SUPPORT // additional prosign support for paddle and straight key echo on display, CLI, and in memory storage +// #define OPTION_RUSSIAN_LANGUAGE_SEND_CLI // Russian language CLI sending support (contributed by Павел Бирюков, UA1AQC) +#define OPTION_DO_NOT_SEND_UNKNOWN_CHAR_QUESTION +// #define OPTION_CMOS_SUPER_KEYER_IAMBIC_B_TIMING_ON_BY_DEFAULT +#define OPTION_SIDETONE_DIGITAL_OUTPUT_NO_SQUARE_WAVE + +// #define OPTION_WORDSWORTH_CZECH +// #define OPTION_WORDSWORTH_DEUTSCH +// #define OPTION_WORDSWORTH_NORSK + +#define OPTION_EXCLUDE_EXTENDED_CLI_COMMANDS +// #define OPTION_NO_ULTIMATIC // reduce memory usage by removing ultimatic code. + +// #define OPTION_PERSONALIZED_STARTUP_SCREEN // displays a user defined string of characters on the second or fourth row of the screen during startup. 1602 display requires OPTION_DO_NOT_SAY_HI +// #define OPTION_SWAP_PADDLE_PARAMETER_CHANGE_DIRECTION // reverses the up/down direction when using the paddles to change the wpm or sidetone frequency +// #define OPTION_DISPLAY_MEMORY_CONTENTS_COMMAND_MODE + +// #define OPTION_BEACON_MODE_MEMORY_REPEAT_TIME // to space out the repeated playing of memory 1 when in beacon mode +// #define OPTION_BEACON_MODE_PTT_TAIL_TIME // adds the ptt tail time to each playing of memory 1 in beacon mode + +// #define OPTION_WINKEY_PROSIGN_COMPATIBILITY // Additional character mappings to support K1EL Winkey emulation prosigns diff --git a/k3ng_keyer/keyer_features_and_options_mortty_so2r_with_potentiometer.h b/k3ng_keyer/keyer_features_and_options_mortty_so2r_with_potentiometer.h new file mode 100755 index 0000000..3aca96c --- /dev/null +++ b/k3ng_keyer/keyer_features_and_options_mortty_so2r_with_potentiometer.h @@ -0,0 +1,120 @@ +// compile time features and options - comment or uncomment to add or delete features +// FEATURES add more bytes to the compiled binary, OPTIONS change code behavior + + +// #define FEATURE_BUTTONS +// #define FEATURE_COMMAND_MODE +// #define FEATURE_COMMAND_LINE_INTERFACE // Command Line Interface functionality +// #define FEATURE_MEMORIES // on the Arduino Due, you must have FEATURE_EEPROM_E24C1024 and E24C1024 EEPROM hardware in order to compile this +// #define FEATURE_MEMORY_MACROS +#define FEATURE_WINKEY_EMULATION // disabling Automatic Software Reset is highly recommended (see documentation) +// #define FEATURE_BEACON // Go into beacon mode if paddle_left pin is LOW at boot up +// #define FEATURE_BEACON_SETTING // Go into beacon mode at boot up if EEPROM setting is enabled (\_ CLI Command) +// #define FEATURE_TRAINING_COMMAND_LINE_INTERFACE +#define FEATURE_POTENTIOMETER // do not enable unless you have a potentiometer connected, otherwise noise will falsely trigger wpm changes +// #define FEATURE_SIDETONE_SWITCH // adds switch control for the sidetone output. requires an external toggle switch (assigned to an arduino pin - see keyer_pin_settings.h). +// #define FEATURE_SERIAL_HELP +// #define FEATURE_HELL +// #define FEATURE_PS2_KEYBOARD // Use a PS2 keyboard to send code - Change keyboard layout (non-US) in K3NG_PS2Keyboard.h. Additional options below. +// #define FEATURE_USB_KEYBOARD // Use a USB keyboard to send code - Uncomment three lines in k3ng_keyer.ino (search for note_usb_uncomment_lines) +// #define FEATURE_CW_COMPUTER_KEYBOARD // Have an Arduino Due or Leonardo act as a USB HID (Human Interface Device) keyboard and use the paddle to "type" characters on the computer -- uncomment this line in ino file: #include +// #define FEATURE_DEAD_OP_WATCHDOG +// #define FEATURE_AUTOSPACE +// #define FEATURE_FARNSWORTH +// #define FEATURE_DL2SBA_BANKSWITCH // Switch memory banks feature as described here: http://dl2sba.com/index.php?option=com_content&view=article&id=131:nanokeyer&catid=15:shack&Itemid=27#english +// #define FEATURE_LCD_4BIT // classic LCD disidefplay using 4 I/O lines +// #define FEATURE_LCD_ADAFRUIT_I2C // Adafruit I2C LCD display using MCP23017 at addr 0x20 +// #define FEATURE_LCD_ADAFRUIT_BACKPACK // Adafruit I2C LCD Backup using MCP23008 (courtesy Josiah Ritchie, KE0BLL) +// #define FEATURE_LCD_YDv1 // YourDuino I2C LCD display with old LCM 1602 V1 ic +// #define FEATURE_LCD1602_N07DH // http://linksprite.com/wiki/index.php5?title=16_X_2_LCD_Keypad_Shield_for_Arduino +// #define FEATURE_LCD_SAINSMART_I2C +// #define FEATURE_LCD_I2C_FDEBRABANDER //https://github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library +// #define FEATURE_CW_DECODER // https://github.com/k3ng/k3ng_cw_keyer/wiki/385-Feature:-CW-Decoder +// #define FEATURE_SLEEP // go to sleep after x minutes to conserve battery power (not compatible with Arduino DUE, may have mixed results with Mega and Mega ADK) +// #define FEATURE_LCD_BACKLIGHT_AUTO_DIM // turn off LCD backlight and/or dim Power Indicator LED after x minutes (LED requires a PWM pin) +// #define FEATURE_ROTARY_ENCODER // rotary encoder speed control +// #define FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING +// #define FEATURE_USB_MOUSE // Uncomment three lines in k3ng_keyer.ino (search for note_usb_uncomment_lines) +// #define FEATURE_CAPACITIVE_PADDLE_PINS // remove the bypass capacitors on the paddle_left and paddle_right lines when using capactive paddles +// #define FEATURE_LED_RING // Mayhew Labs Led Ring support +// #define FEATURE_ALPHABET_SEND_PRACTICE // enables command mode S command - created by Ryan, KC2ZWM +// #define FEATURE_COMMAND_MODE_PROGRESSIVE_5_CHAR_ECHO_PRACTICE // enables command mode U +// #define FEATURE_PTT_INTERLOCK +// #define FEATURE_QLF +// #define FEATURE_EEPROM_E24C1024 +// #define FEATURE_STRAIGHT_KEY +// #define FEATURE_DYNAMIC_DAH_TO_DIT_RATIO +// #define FEATURE_PADDLE_ECHO +// #define FEATURE_STRAIGHT_KEY_ECHO +// #define FEATURE_WEB_SERVER // Details: https://github.com/k3ng/k3ng_cw_keyer/wiki/390-Feature:-Ethernet,-Web-Server,-and-Internet-Linking +// #define FEATURE_INTERNET_LINK // Details: https://github.com/k3ng/k3ng_cw_keyer/wiki/390-Feature:-Ethernet,-Web-Server,-and-Internet-Linking +// #define FEATURE_AMERICAN_MORSE +// #define FEATURE_4x4_KEYPAD // code contributed by Jack, W0XR - documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/380-Feature:-Keypad +// #define FEATURE_3x4_KEYPAD // code contributed by Jack, W0XR - documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/380-Feature:-Keypad +// #define FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT +// #define FEATURE_WEB_SERVER // Details: https://github.com/k3ng/k3ng_cw_keyer/wiki/390-Feature:-Ethernet,-Web-Server,-and-Internet-Linking +// #define FEATURE_INTERNET_LINK // Details: https://github.com/k3ng/k3ng_cw_keyer/wiki/390-Feature:-Ethernet,-Web-Server,-and-Internet-Linking + +// #define FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT // Activate the Command Line interface on the secondary serial port +#define OPTION_PRIMARY_SERIAL_PORT_DEFAULT_WINKEY_EMULATION // Use when activating both FEATURE_WINKEY_EMULATION and FEATURE_COMMAND_LINE_INTERFACE + // simultaneously. This will make Winkey emulation be the default at boot up; + // hold command button down at boot up to activate CLI mode + +// #define OPTION_SUPPRESS_SERIAL_BOOT_MSG +#define OPTION_INCLUDE_PTT_TAIL_FOR_MANUAL_SENDING +#define OPTION_EXCLUDE_PTT_HANG_TIME_FOR_MANUAL_SENDING +//#define OPTION_WINKEY_DISCARD_BYTES_AT_STARTUP // if ASR is not disabled, you may need this to discard errant serial port bytes at startup +// #define OPTION_WINKEY_STRICT_EEPROM_WRITES_MAY_WEAR_OUT_EEPROM // with this activated the unit will write non-volatile settings to EEPROM when set by Winkey commands +// #define OPTION_WINKEY_SEND_WORDSPACE_AT_END_OF_BUFFER +// #define OPTION_WINKEY_STRICT_HOST_OPEN // require an admin host open Winkey command before doing any other commands +#define OPTION_WINKEY_2_SUPPORT // comment out to revert to Winkey version 1 emulation +#define OPTION_WINKEY_INTERRUPTS_MEMORY_REPEAT +//#define OPTION_WINKEY_UCXLOG_9600_BAUD // use this only with UCXLog configured for Winkey 9600 baud mode +#define OPTION_WINKEY_2_HOST_CLOSE_NO_SERIAL_PORT_RESET // activate this when using Winkey 2 emulation and Win-Test +// #define OPTION_WINKEY_FREQUENT_STATUS_REPORT // activate this to make Winkey emulation play better with RUMlog and RUMped +#define OPTION_WINKEY_IGNORE_LOWERCASE // Enable for typical K1EL Winkeyer behavior (use for SkookumLogger version 1.10.14 and prior to workaround "r" bug) +#define OPTION_WINKEY_BLINK_PTT_ON_HOST_OPEN +// #define OPTION_WINKEY_SEND_VERSION_ON_HOST_CLOSE +// #define OPTION_WINKEY_PINCONFIG_PTT_CONTROLS_PTT_LINE // Have Winkeyer PTT setting activate/deactivate PTT line rather than control buffered character PTT hold +// #define OPTION_REVERSE_BUTTON_ORDER // This is mainly for the DJ0MY NanoKeyer http://nanokeyer.wordpress.com/ +#define OPTION_PROG_MEM_TRIM_TRAILING_SPACES // trim trailing spaces from memory when programming in command mode +#define OPTION_DIT_PADDLE_NO_SEND_ON_MEM_RPT // this makes dit paddle memory interruption a little smoother +// #define OPTION_MORE_DISPLAY_MSGS // additional optional display messages - comment out to save memory +// #define OPTION_N1MM_WINKEY_TAB_BUG_WORKAROUND // enable this to ignore the TAB key in the Send CW window (this breaks SO2R functionality in N1MM) +// #define OPTION_WATCHDOG_TIMER // this enables a four second ATmega48/88/168/328 watchdog timer; use for unattended/remote operation only +// #define OPTION_MOUSE_MOVEMENT_PADDLE // experimental (just fooling around) - mouse movement will act like a paddle +// #define OPTION_NON_ENGLISH_EXTENSIONS // add support for additional CW characters (i.e. À, Å, Þ, etc.) +// #define OPTION_DISPLAY_NON_ENGLISH_EXTENSIONS // LCD display suport for non-English (NO/DK/DE) characters - Courtesy of OZ1JHM +// #define OPTION_UNKNOWN_CHARACTER_ERROR_TONE +#define OPTION_DO_NOT_SAY_HI +#define OPTION_BLINK_HI_ON_PTT +// #define OPTION_PS2_NON_ENGLISH_CHAR_LCD_DISPLAY_SUPPORT // makes some non-English characters from the PS2 keyboard display correctly in the LCD display (donated by Marcin sp5iou) +// #define OPTION_PS2_KEYBOARD_RESET // reset the PS2 keyboard upon startup with 0xFF (contributed by Bill, W9BEL) +// #define OPTION_SAVE_MEMORY_NANOKEYER +// #define OPTION_CW_KEYBOARD_CAPSLOCK_BEEP +// #define OPTION_CW_KEYBOARD_ITALIAN +// #define OPTION_CW_KEYBOARD_GERMAN +// #define OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR +// #define OPTION_INVERT_PADDLE_PIN_LOGIC +// #define OPTION_ADVANCED_SPEED_DISPLAY //enables "nerd" speed visualization on display: wpm, cpm (char per min), duration of dit and dah in milliseconds and ratio (contributed by Giorgio, IZ2XBZ) +// #define OPTION_PROSIGN_SUPPORT // additional prosign support for paddle and straight key echo on display, CLI, and in memory storage +// #define OPTION_RUSSIAN_LANGUAGE_SEND_CLI // Russian language CLI sending support (contributed by Павел Бирюков, UA1AQC) +#define OPTION_DO_NOT_SEND_UNKNOWN_CHAR_QUESTION +// #define OPTION_CMOS_SUPER_KEYER_IAMBIC_B_TIMING_ON_BY_DEFAULT +#define OPTION_SIDETONE_DIGITAL_OUTPUT_NO_SQUARE_WAVE + +// #define OPTION_WORDSWORTH_CZECH +// #define OPTION_WORDSWORTH_DEUTSCH +// #define OPTION_WORDSWORTH_NORSK + +#define OPTION_EXCLUDE_EXTENDED_CLI_COMMANDS +// #define OPTION_NO_ULTIMATIC // reduce memory usage by removing ultimatic code. + +// #define OPTION_PERSONALIZED_STARTUP_SCREEN // displays a user defined string of characters on the second or fourth row of the screen during startup. 1602 display requires OPTION_DO_NOT_SAY_HI +// #define OPTION_SWAP_PADDLE_PARAMETER_CHANGE_DIRECTION // reverses the up/down direction when using the paddles to change the wpm or sidetone frequency +// #define OPTION_DISPLAY_MEMORY_CONTENTS_COMMAND_MODE + +// #define OPTION_BEACON_MODE_MEMORY_REPEAT_TIME // to space out the repeated playing of memory 1 when in beacon mode +// #define OPTION_BEACON_MODE_PTT_TAIL_TIME // adds the ptt tail time to each playing of memory 1 in beacon mode + +// #define OPTION_WINKEY_PROSIGN_COMPATIBILITY // Additional character mappings to support K1EL Winkey emulation prosigns \ No newline at end of file diff --git a/k3ng_keyer/keyer_features_and_options_nanokeyer_rev_b.h b/k3ng_keyer/keyer_features_and_options_nanokeyer_rev_b.h new file mode 100755 index 0000000..9d9ca30 --- /dev/null +++ b/k3ng_keyer/keyer_features_and_options_nanokeyer_rev_b.h @@ -0,0 +1,113 @@ +// compile time features and options - comment or uncomment to add or delete features +// FEATURES add more bytes to the compiled binary, OPTIONS change code behavior + +#define FEATURE_BUTTONS +#define FEATURE_COMMAND_MODE +//#define FEATURE_COMMAND_LINE_INTERFACE // (this no longer requires FEATURE_SERIAL) +#define FEATURE_MEMORIES +//#define FEATURE_MEMORY_MACROS +#define FEATURE_WINKEY_EMULATION // disabling Automatic Software Reset is highly recommended (see documentation) +//#define FEATURE_BEACON // Go into beacon mode if paddle_left pin is LOW at boot up +//#define FEATURE_BEACON_SETTING // Go into beacon mode at boot up if EEPROM setting is enabled (\_ CLI Command) +//#define FEATURE_TRAINING_COMMAND_LINE_INTERFACE +#define FEATURE_POTENTIOMETER // do not enable unless you have a potentiometer connected, otherwise noise will falsely trigger wpm changes +//#define FEATURE_SIDETONE_SWITCH // adds switch control for the sidetone output. requires an external toggle switch (assigned to an arduino pin - see keyer_pin_settings.h). +//#define FEATURE_SIDETONE_NEWTONE // Use the NewTone library, ~1k smaller code size than the standard tone library. Uses timer1 (pins 9 or 10) https://bitbucket.org/teckel12/arduino-new-tone/wiki/Home +//#define FEATURE_SERIAL_HELP +//#define FEATURE_HELL +//#define FEATURE_PS2_KEYBOARD // Change keyboard layout (non-US in K3NG_PS2Keyboard.h). Additional options below. +//#define FEATURE_USB_KEYBOARD +//#define FEATURE_DEAD_OP_WATCHDOG +//#define FEATURE_AUTOSPACE +//#define FEATURE_FARNSWORTH +//#define FEATURE_DL2SBA_BANKSWITCH // Switch memory banks feature as described here: http://dl2sba.com/index.php?option=com_content&view=article&id=131:nanokeyer&catid=15:shack&Itemid=27#english +//#define FEATURE_LCD_4BIT // classic LCD disidefplay using 4 I/O lines +//#define FEATURE_LCD_ADAFRUIT_I2C // Adafruit I2C LCD display using MCP23017 at addr 0x20 +//#define FEATURE_LCD_YDv1 // YourDuino I2C LCD display with old LCM 1602 V1 ic +//#define FEATURE_LCD_I2C_FDEBRABANDER //https://github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library +// #define FEATURE_CW_DECODER // https://github.com/k3ng/k3ng_cw_keyer/wiki/385-Feature:-CW-Decoder +//#define FEATURE_SLEEP // go to sleep after x minutes to conserve battery power (not compatible with Arduino DUE, may have mixed results with Mega and Mega ADK) +//#define FEATURE_LCD_BACKLIGHT_AUTO_DIM // turn off LCD backlight and/or dim Power Indicator LED after x minutes (LED requires a PWM pin) +//#define FEATURE_ROTARY_ENCODER // rotary encoder speed control +//#define FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING +//#define FEATURE_USB_MOUSE // Uncomment three lines in k3ng_keyer.ino (search for note_usb_uncomment_lines) +//#define FEATURE_CAPACITIVE_PADDLE_PINS // remove the bypass capacitors on the paddle_left and paddle_right lines when using capactive paddles +//#define FEATURE_LED_RING // Mayhew Labs Led Ring support +//#define FEATURE_ALPHABET_SEND_PRACTICE // enables command mode S command - created by Ryan, KC2ZWM +//#define FEATURE_COMMAND_MODE_PROGRESSIVE_5_CHAR_ECHO_PRACTICE // enables command mode U +//#define FEATURE_PTT_INTERLOCK +//#define FEATURE_QLF +//#define FEATURE_EEPROM_E24C1024 +//#define FEATURE_STRAIGHT_KEY +//#define FEATURE_DYNAMIC_DAH_TO_DIT_RATIO +//#define FEATURE_PADDLE_ECHO +//#define FEATURE_STRAIGHT_KEY_ECHO +//#define FEATURE_AMERICAN_MORSE +//#define FEATURE_4x4_KEYPAD // code contributed by Jack, W0XR - documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/380-Feature:-Keypad +//#define FEATURE_3x4_KEYPAD // code contributed by Jack, W0XR - documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/380-Feature:-Keypad +//#define FEATURE_SEQUENCER +//#define FEATURE_SD_CARD_SUPPORT +//#define FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT +// #define FEATURE_WEB_SERVER // Details: https://github.com/k3ng/k3ng_cw_keyer/wiki/390-Feature:-Ethernet,-Web-Server,-and-Internet-Linking +// #define FEATURE_INTERNET_LINK // Details: https://github.com/k3ng/k3ng_cw_keyer/wiki/390-Feature:-Ethernet,-Web-Server,-and-Internet-Linking + +//#define OPTION_SUPPRESS_SERIAL_BOOT_MSG +#define OPTION_INCLUDE_PTT_TAIL_FOR_MANUAL_SENDING +#define OPTION_EXCLUDE_PTT_HANG_TIME_FOR_MANUAL_SENDING +//#define OPTION_SERIAL_PORT_DEFAULT_WINKEY_EMULATION // Use when activating both FEATURE_WINKEY_EMULATION and FEATURE_COMMAND_LINE_INTERFACE simultaneously. This will make Winkey emulation be the default at boot up; hold command button down at boot up to activate CLI mode +//#define OPTION_WINKEY_DISCARD_BYTES_AT_STARTUP // if ASR is not disabled, you may need this to discard errant serial port bytes at startup +//#define OPTION_WINKEY_STRICT_EEPROM_WRITES_MAY_WEAR_OUT_EEPROM // with this activated the unit will write non-volatile settings to EEPROM when set by Winkey commands +//#define OPTION_WINKEY_SEND_WORDSPACE_AT_END_OF_BUFFER +#define OPTION_WINKEY_STRICT_HOST_OPEN // require an admin host open Winkey command before doing any other commands +#define OPTION_WINKEY_2_SUPPORT // comment out to revert to Winkey version 1 emulation +#define OPTION_WINKEY_INTERRUPTS_MEMORY_REPEAT +//#define OPTION_WINKEY_UCXLOG_9600_BAUD // use this only with UCXLog configured for Winkey 9600 baud mode +#define OPTION_WINKEY_2_HOST_CLOSE_NO_SERIAL_PORT_RESET // (Required for Win-Test to function) +//#define OPTION_WINKEY_FREQUENT_STATUS_REPORT // activate this to make Winkey emulation play better with RUMlog and RUMped +#define OPTION_WINKEY_IGNORE_LOWERCASE // Enable for typical K1EL Winkeyer behavior (use for SkookumLogger version 1.10.14 and prior to workaround bug) +//#define OPTION_WINKEY_BLINK_PTT_ON_HOST_OPEN +//#define OPTION_WINKEY_SEND_VERSION_ON_HOST_CLOSE +//#define OPTION_WINKEY_PINCONFIG_PTT_CONTROLS_PTT_LINE // Have Winkeyer PTT setting activate/deactivate PTT line rather than control buffered character PTT hold +//#define OPTION_REVERSE_BUTTON_ORDER // This is mainly for the DJ0MY NanoKeyer http://nanokeyer.wordpress.com/ +#define OPTION_PROG_MEM_TRIM_TRAILING_SPACES // trim trailing spaces from memory when programming in command mode +#define OPTION_DIT_PADDLE_NO_SEND_ON_MEM_RPT // this makes dit paddle memory interruption a little smoother +//#define OPTION_MORE_DISPLAY_MSGS // additional optional display messages - comment out to save memory +//#define OPTION_WATCHDOG_TIMER // this enables a four second ATmega48/88/168/328 watchdog timer; use for unattended/remote operation only +//#define OPTION_MOUSE_MOVEMENT_PADDLE // experimental (just fooling around) - mouse movement will act like a paddle +//#define OPTION_NON_ENGLISH_EXTENSIONS // add support for additional CW characters (i.e. À, Å, Þ, etc.) +//#define OPTION_DISPLAY_NON_ENGLISH_EXTENSIONS // LCD display suport for non-English (NO/DK/DE) characters - Courtesy of OZ1JHM +//#define OPTION_UNKNOWN_CHARACTER_ERROR_TONE +//#define OPTION_DO_NOT_SAY_HI +//#define OPTION_PS2_NON_ENGLISH_CHAR_LCD_DISPLAY_SUPPORT // makes some non-English characters from the PS2 keyboard display correctly in the LCD display (donated by Marcin sp5iou) +//#define OPTION_PS2_KEYBOARD_RESET // reset the PS2 keyboard upon startup with 0xFF (contributed by Bill, W9BEL) +#define OPTION_SAVE_MEMORY_NANOKEYER +//#define OPTION_INVERT_PADDLE_PIN_LOGIC +//#define OPTION_ADVANCED_SPEED_DISPLAY //enables "nerd" speed visualization on display: wpm, cpm (char per min), duration of dit and dah in milliseconds and ratio (contributed by Giorgio, IZ2XBZ) +//#define OPTION_RUSSIAN_LANGUAGE_SEND_CLI // Russian language CLI sending support (contributed by Павел Бирюков, UA1AQC) +#define OPTION_DO_NOT_SEND_UNKNOWN_CHAR_QUESTION +//#define OPTION_CMOS_SUPER_KEYER_IAMBIC_B_TIMING_ON_BY_DEFAULT +//#define OPTION_SIDETONE_DIGITAL_OUTPUT_NO_SQUARE_WAVE + +// #define OPTION_DIRECT_PADDLE_PIN_READS_MEGA // only works with Mega and pins 2 and 5 - minor performance increase +// #define OPTION_DIRECT_PADDLE_PIN_READS_UNO // Unos or Nanos pins 2 and 5 - do not enable on a nanoKeyer, it uses different pins + +// #define OPTION_WORDSWORTH_CZECH +// #define OPTION_WORDSWORTH_DEUTSCH +// #define OPTION_WORDSWORTH_NORSK + +#define OPTION_EXCLUDE_EXTENDED_CLI_COMMANDS + +// #define OPTION_DFROBOT_LCD_COMMAND_BUTTONS + +#define OPTION_EXCLUDE_MILL_MODE +// #define OPTION_NO_ULTIMATIC // reduce memory usage by removing ultimatic code. + +// #define OPTION_DISABLE_SERIAL_PORT_CHECKING_WHILE_SENDING_CW +// #define OPTION_PERSONALIZED_STARTUP_SCREEN // displays a user defined string of characters on the second or fourth row of the screen during startup. 1602 display requires OPTION_DO_NOT_SAY_HI +// #define OPTION_SWAP_PADDLE_PARAMETER_CHANGE_DIRECTION // reverses the up/down direction when using the paddles to change the wpm or sidetone frequency +// #define OPTION_DISPLAY_MEMORY_CONTENTS_COMMAND_MODE + +// #define OPTION_BEACON_MODE_MEMORY_REPEAT_TIME // to space out the repeated playing of memory 1 when in beacon mode +// #define OPTION_BEACON_MODE_PTT_TAIL_TIME // adds the ptt tail time to each playing of memory 1 in beacon mode + +// #define OPTION_WINKEY_PROSIGN_COMPATIBILITY // Additional character mappings to support K1EL Winkey emulation prosigns \ No newline at end of file diff --git a/k3ng_keyer/keyer_features_and_options_nanokeyer_rev_d.h b/k3ng_keyer/keyer_features_and_options_nanokeyer_rev_d.h new file mode 100755 index 0000000..356928e --- /dev/null +++ b/k3ng_keyer/keyer_features_and_options_nanokeyer_rev_d.h @@ -0,0 +1,113 @@ +// compile time features and options - comment or uncomment to add or delete features +// FEATURES add more bytes to the compiled binary, OPTIONS change code behavior + +#define FEATURE_BUTTONS +#define FEATURE_COMMAND_MODE +//#define FEATURE_COMMAND_LINE_INTERFACE // (this no longer requires FEATURE_SERIAL) +#define FEATURE_MEMORIES +//#define FEATURE_MEMORY_MACROS +#define FEATURE_WINKEY_EMULATION // disabling Automatic Software Reset is highly recommended (see documentation) +//#define FEATURE_BEACON // Go into beacon mode if paddle_left pin is LOW at boot up +//#define FEATURE_BEACON_SETTING // Go into beacon mode at boot up if EEPROM setting is enabled (\_ CLI Command) +//#define FEATURE_TRAINING_COMMAND_LINE_INTERFACE +#define FEATURE_POTENTIOMETER // do not enable unless you have a potentiometer connected, otherwise noise will falsely trigger wpm changes +//#define FEATURE_SIDETONE_SWITCH // adds switch control for the sidetone output. requires an external toggle switch (assigned to an arduino pin - see keyer_pin_settings.h). +//#define FEATURE_SERIAL_HELP +//#define FEATURE_HELL +//#define FEATURE_PS2_KEYBOARD // Change keyboard layout (non-US in K3NG_PS2Keyboard.h). Additional options below. +//#define FEATURE_USB_KEYBOARD +//#define FEATURE_DEAD_OP_WATCHDOG +//#define FEATURE_AUTOSPACE +//#define FEATURE_FARNSWORTH +//#define FEATURE_DL2SBA_BANKSWITCH // Switch memory banks feature as described here: http://dl2sba.com/index.php?option=com_content&view=article&id=131:nanokeyer&catid=15:shack&Itemid=27#english +//#define FEATURE_LCD_4BIT // classic LCD disidefplay using 4 I/O lines +//#define FEATURE_LCD_ADAFRUIT_I2C // Adafruit I2C LCD display using MCP23017 at addr 0x20 +//#define FEATURE_LCD_YDv1 // YourDuino I2C LCD display with old LCM 1602 V1 ic +//#define FEATURE_LCD_I2C_FDEBRABANDER //https://github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library +// #define FEATURE_CW_DECODER // https://github.com/k3ng/k3ng_cw_keyer/wiki/385-Feature:-CW-Decoder +//#define FEATURE_SLEEP // go to sleep after x minutes to conserve battery power +//#define FEATURE_LCD_BACKLIGHT_AUTO_DIM // turn off LCD backlight and/or dim Power Indicator LED after x minutes (LED requires a PWM pin) +//#define FEATURE_ROTARY_ENCODER // rotary encoder speed control +//#define FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING +//#define FEATURE_USB_MOUSE +//#define FEATURE_CAPACITIVE_PADDLE_PINS // remove the bypass capacitors on the paddle_left and paddle_right lines when using capactive paddles +//#define FEATURE_LED_RING // Mayhew Labs Led Ring support +//#define FEATURE_ALPHABET_SEND_PRACTICE // enables command mode S command - created by Ryan, KC2ZWM +//#define FEATURE_COMMAND_MODE_PROGRESSIVE_5_CHAR_ECHO_PRACTICE // enables command mode U +//#define FEATURE_PTT_INTERLOCK +//#define FEATURE_QLF +//#define FEATURE_EEPROM_E24C1024 +//#define FEATURE_STRAIGHT_KEY +//#define FEATURE_WEB_SERVER // Details: https://github.com/k3ng/k3ng_cw_keyer/wiki/390-Feature:-Ethernet,-Web-Server,-and-Internet-Linking +//#define FEATURE_INTERNET_LINK // Details: https://github.com/k3ng/k3ng_cw_keyer/wiki/390-Feature:-Ethernet,-Web-Server,-and-Internet-Linking +//#define FEATURE_DYNAMIC_DAH_TO_DIT_RATIO +//#define FEATURE_AMERICAN_MORSE +//#define FEATURE_4x4_KEYPAD // code contributed by Jack, W0XR - documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/380-Feature:-Keypad +//#define FEATURE_3x4_KEYPAD // code contributed by Jack, W0XR - documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/380-Feature:-Keypad +//#define FEATURE_SEQUENCER +//#define FEATURE_SD_CARD_SUPPORT +//#define FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT +// #define FEATURE_WEB_SERVER // Details: https://github.com/k3ng/k3ng_cw_keyer/wiki/390-Feature:-Ethernet,-Web-Server,-and-Internet-Linking +// #define FEATURE_INTERNET_LINK // Details: https://github.com/k3ng/k3ng_cw_keyer/wiki/390-Feature:-Ethernet,-Web-Server,-and-Internet-Linking + +//#define OPTION_SUPPRESS_SERIAL_BOOT_MSG +#define OPTION_INCLUDE_PTT_TAIL_FOR_MANUAL_SENDING +#define OPTION_EXCLUDE_PTT_HANG_TIME_FOR_MANUAL_SENDING +//#define OPTION_SERIAL_PORT_DEFAULT_WINKEY_EMULATION // Use when activating both FEATURE_WINKEY_EMULATION and FEATURE_COMMAND_LINE_INTERFACE simultaneously. This will make Winkey emulation be the default at boot up; hold command button down at boot up to activate CLI mode +//#define OPTION_WINKEY_DISCARD_BYTES_AT_STARTUP // if ASR is not disabled, you may need this to discard errant serial port bytes at startup +//#define OPTION_WINKEY_STRICT_EEPROM_WRITES_MAY_WEAR_OUT_EEPROM // with this activated the unit will write non-volatile settings to EEPROM when set by Winkey commands +//#define OPTION_WINKEY_SEND_WORDSPACE_AT_END_OF_BUFFER +#define OPTION_WINKEY_STRICT_HOST_OPEN // require an admin host open Winkey command before doing any other commands +#define OPTION_WINKEY_2_SUPPORT // comment out to revert to Winkey version 1 emulation +#define OPTION_WINKEY_INTERRUPTS_MEMORY_REPEAT +//#define OPTION_WINKEY_UCXLOG_9600_BAUD // use this only with UCXLog configured for Winkey 9600 baud mode +#define OPTION_WINKEY_2_HOST_CLOSE_NO_SERIAL_PORT_RESET // (Required for Win-Test to function) +//#define OPTION_WINKEY_FREQUENT_STATUS_REPORT // activate this to make Winkey emulation play better with RUMlog and RUMped +#define OPTION_WINKEY_IGNORE_LOWERCASE // Enable for typical K1EL Winkeyer behavior (use for SkookumLogger version 1.10.14 and prior to workaround bug) +//#define OPTION_WINKEY_BLINK_PTT_ON_HOST_OPEN +//#define OPTION_WINKEY_SEND_VERSION_ON_HOST_CLOSE +//#define OPTION_WINKEY_PINCONFIG_PTT_CONTROLS_PTT_LINE // Have Winkeyer PTT setting activate/deactivate PTT line rather than control buffered character PTT hold +#define OPTION_REVERSE_BUTTON_ORDER // This is mainly for the DJ0MY NanoKeyer http://nanokeyer.wordpress.com/ +#define OPTION_PROG_MEM_TRIM_TRAILING_SPACES // trim trailing spaces from memory when programming in command mode +#define OPTION_DIT_PADDLE_NO_SEND_ON_MEM_RPT // this makes dit paddle memory interruption a little smoother +//#define OPTION_MORE_DISPLAY_MSGS // additional optional display messages - comment out to save memory +//#define OPTION_WATCHDOG_TIMER // this enables a four second ATmega48/88/168/328 watchdog timer; use for unattended/remote operation only +//#define OPTION_MOUSE_MOVEMENT_PADDLE // experimental (just fooling around) - mouse movement will act like a paddle +//#define OPTION_NON_ENGLISH_EXTENSIONS // add support for additional CW characters (i.e. À, Å, Þ, etc.) +//#define OPTION_DISPLAY_NON_ENGLISH_EXTENSIONS // LCD display suport for non-English (NO/DK/DE) characters - Courtesy of OZ1JHM +//#define OPTION_UNKNOWN_CHARACTER_ERROR_TONE +//#define OPTION_DO_NOT_SAY_HI +//#define OPTION_PS2_NON_ENGLISH_CHAR_LCD_DISPLAY_SUPPORT // makes some non-English characters from the PS2 keyboard display correctly in the LCD display (donated by Marcin sp5iou) +//#define OPTION_PS2_KEYBOARD_RESET // reset the PS2 keyboard upon startup with 0xFF (contributed by Bill, W9BEL) +#define OPTION_SAVE_MEMORY_NANOKEYER +//#define OPTION_INVERT_PADDLE_PIN_LOGIC +//#define OPTION_ADVANCED_SPEED_DISPLAY //enables "nerd" speed visualization on display: wpm, cpm (char per min), duration of dit and dah in milliseconds and ratio (contributed by Giorgio, IZ2XBZ) +//#define OPTION_RUSSIAN_LANGUAGE_SEND_CLI // Russian language CLI sending support (contributed by Павел Бирюков, UA1AQC) +#define OPTION_DO_NOT_SEND_UNKNOWN_CHAR_QUESTION +//#define OPTION_CMOS_SUPER_KEYER_IAMBIC_B_TIMING_ON_BY_DEFAULT +//#define OPTION_SIDETONE_DIGITAL_OUTPUT_NO_SQUARE_WAVE + +// #define OPTION_DIRECT_PADDLE_PIN_READS_MEGA // only works with Mega and pins 2 and 5 - minor performance increase +// #define OPTION_DIRECT_PADDLE_PIN_READS_UNO // Unos or Nanos pins 2 and 5 - do not enable on a nanoKeyer, it uses different pins + +// #define OPTION_WORDSWORTH_CZECH +// #define OPTION_WORDSWORTH_DEUTSCH +// #define OPTION_WORDSWORTH_NORSK + +#define OPTION_EXCLUDE_EXTENDED_CLI_COMMANDS + +// #define OPTION_DFROBOT_LCD_COMMAND_BUTTONS + +#define OPTION_EXCLUDE_MILL_MODE +// #define OPTION_NO_ULTIMATIC // reduce memory usage by removing ultimatic code. + +// #define OPTION_DISABLE_SERIAL_PORT_CHECKING_WHILE_SENDING_CW +// #define OPTION_PERSONALIZED_STARTUP_SCREEN // displays a user defined string of characters on the second or fourth row of the screen during startup. 1602 display requires OPTION_DO_NOT_SAY_HI +// #define OPTION_SWAP_PADDLE_PARAMETER_CHANGE_DIRECTION // reverses the up/down direction when using the paddles to change the wpm or sidetone frequency +// #define OPTION_DISPLAY_MEMORY_CONTENTS_COMMAND_MODE + +// #define OPTION_BEACON_MODE_MEMORY_REPEAT_TIME // to space out the repeated playing of memory 1 when in beacon mode +// #define OPTION_BEACON_MODE_PTT_TAIL_TIME // adds the ptt tail time to each playing of memory 1 in beacon mode + +// #define OPTION_WINKEY_PROSIGN_COMPATIBILITY // Additional character mappings to support K1EL Winkey emulation prosigns + diff --git a/k3ng_keyer/keyer_features_and_options_open_interface.h b/k3ng_keyer/keyer_features_and_options_open_interface.h new file mode 100755 index 0000000..3d619a3 --- /dev/null +++ b/k3ng_keyer/keyer_features_and_options_open_interface.h @@ -0,0 +1,117 @@ +// This file is for the Open Interface http://remoteqth.com/open-interface.php + +// compile time features and options - comment or uncomment to add or delete features +// FEATURES add more bytes to the compiled binary, OPTIONS change code behavior + +#define FEATURE_BUTTONS +#define FEATURE_COMMAND_MODE +#define FEATURE_COMMAND_LINE_INTERFACE // (this no longer requires FEATURE_SERIAL) +#define FEATURE_MEMORIES +#define FEATURE_MEMORY_MACROS +#define FEATURE_WINKEY_EMULATION // disabling Automatic Software Reset is highly recommended (see documentation) +//#define FEATURE_BEACON // Go into beacon mode if paddle_left pin is LOW at boot up +//#define FEATURE_BEACON_SETTING // Go into beacon mode at boot up if EEPROM setting is enabled (\_ CLI Command) +#define FEATURE_TRAINING_COMMAND_LINE_INTERFACE +#define FEATURE_POTENTIOMETER // do not enable unless you have a potentiometer connected, otherwise noise will falsely trigger wpm changes +// #define FEATURE_SIDETONE_SWITCH // adds switch control for the sidetone output. requires an external toggle switch (assigned to an arduino pin - see keyer_pin_settings.h). +// #define FEATURE_SIDETONE_NEWTONE // Use the NewTone library, ~1k smaller code size than the standard tone library. Uses timer1 (pins 9 or 10) https://bitbucket.org/teckel12/arduino-new-tone/wiki/Home +#define FEATURE_SERIAL_HELP +//#define FEATURE_HELL +//#define FEATURE_PS2_KEYBOARD // Use a PS2 keyboard to send code - Change keyboard layout (non-US) in K3NG_PS2Keyboard.h. Additional options below. +//#define FEATURE_USB_KEYBOARD // Use a USB keyboard to send code - Uncomment three lines in k3ng_keyer.ino (search for note_usb_uncomment_lines) +//#define FEATURE_DEAD_OP_WATCHDOG +//#define FEATURE_AUTOSPACE +#define FEATURE_FARNSWORTH +//#define FEATURE_DL2SBA_BANKSWITCH // Switch memory banks feature as described here: http://dl2sba.com/index.php?option=com_content&view=article&id=131:nanokeyer&catid=15:shack&Itemid=27#english +#define FEATURE_LCD_4BIT // classic LCD disidefplay using 4 I/O lines +//#define FEATURE_LCD_ADAFRUIT_I2C // Adafruit I2C LCD display using MCP23017 at addr 0x20 +//#define FEATURE_LCD_YDv1 // YourDuino I2C LCD display with old LCM 1602 V1 ic +//#define FEATURE_LCD_FABO_PCF8574 // https://github.com/FaBoPlatform/FaBoLCD-PCF8574-Library +// #define FEATURE_LCD_I2C_FDEBRABANDER //https://github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library +// #define FEATURE_CW_DECODER // https://github.com/k3ng/k3ng_cw_keyer/wiki/385-Feature:-CW-Decoder +//#define FEATURE_SLEEP // go to sleep after x minutes to conserve battery power +//#define FEATURE_LCD_BACKLIGHT_AUTO_DIM // turn off LCD backlight and/or dim Power Indicator LED after x minutes (LED requires a PWM pin) +//#define FEATURE_ROTARY_ENCODER // rotary encoder speed control +#define FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING +//#define FEATURE_USB_MOUSE // Uncomment three lines in k3ng_keyer.ino (search for note_usb_uncomment_lines) +//#define FEATURE_CAPACITIVE_PADDLE_PINS // remove the bypass capacitors on the paddle_left and paddle_right lines when using capactive paddles +//#define FEATURE_LED_RING // Mayhew Labs Led Ring support +#define FEATURE_ALPHABET_SEND_PRACTICE // enables command mode S command - created by Ryan, KC2ZWM +#define FEATURE_COMMAND_MODE_PROGRESSIVE_5_CHAR_ECHO_PRACTICE // enables command mode U +//#define FEATURE_PTT_INTERLOCK +//#define FEATURE_QLF +//#define FEATURE_EEPROM_E24C1024 +//#define FEATURE_STRAIGHT_KEY +//#define FEATURE_DYNAMIC_DAH_TO_DIT_RATIO +//#define FEATURE_PADDLE_ECHO +//#define FEATURE_STRAIGHT_KEY_ECHO +//#define FEATURE_AMERICAN_MORSE +//#define FEATURE_4x4_KEYPAD // code contributed by Jack, W0XR - documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/380-Feature:-Keypad +//#define FEATURE_3x4_KEYPAD // code contributed by Jack, W0XR - documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/380-Feature:-Keypad +//#define FEATURE_SEQUENCER +//#define FEATURE_SD_CARD_SUPPORT +//#define FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT +// #define FEATURE_WEB_SERVER // Details: https://github.com/k3ng/k3ng_cw_keyer/wiki/390-Feature:-Ethernet,-Web-Server,-and-Internet-Linking +// #define FEATURE_INTERNET_LINK // Details: https://github.com/k3ng/k3ng_cw_keyer/wiki/390-Feature:-Ethernet,-Web-Server,-and-Internet-Linking + +//#define OPTION_SUPPRESS_SERIAL_BOOT_MSG +#define OPTION_INCLUDE_PTT_TAIL_FOR_MANUAL_SENDING +#define OPTION_EXCLUDE_PTT_HANG_TIME_FOR_MANUAL_SENDING +#define OPTION_SERIAL_PORT_DEFAULT_WINKEY_EMULATION // Use when activating both FEATURE_WINKEY_EMULATION and FEATURE_COMMAND_LINE_INTERFACE simultaneously. This will make Winkey emulation be the default at boot up; hold command button down at boot up to activate CLI mode +//#define OPTION_WINKEY_DISCARD_BYTES_AT_STARTUP // if ASR is not disabled, you may need this to discard errant serial port bytes at startup +//#define OPTION_WINKEY_STRICT_EEPROM_WRITES_MAY_WEAR_OUT_EEPROM // with this activated the unit will write non-volatile settings to EEPROM when set by Winkey commands +//#define OPTION_WINKEY_SEND_WORDSPACE_AT_END_OF_BUFFER +#define OPTION_WINKEY_STRICT_HOST_OPEN // require an admin host open Winkey command before doing any other commands +// #define OPTION_WINKEY_2_SUPPORT // comment out to revert to Winkey version 1 emulation +#define OPTION_WINKEY_INTERRUPTS_MEMORY_REPEAT +//#define OPTION_WINKEY_UCXLOG_9600_BAUD // use this only with UCXLog configured for Winkey 9600 baud mode +#define OPTION_WINKEY_2_HOST_CLOSE_NO_SERIAL_PORT_RESET // (Required for Win-Test to function) +// #define OPTION_WINKEY_FREQUENT_STATUS_REPORT // activate this to make Winkey emulation play better with RUMlog and RUMped +#define OPTION_WINKEY_IGNORE_LOWERCASE // Enable for typical K1EL Winkeyer behavior (use for SkookumLogger version 1.10.14 and prior to workaround bug) +//#define OPTION_WINKEY_BLINK_PTT_ON_HOST_OPEN +//#define OPTION_WINKEY_SEND_VERSION_ON_HOST_CLOSE +//#define OPTION_WINKEY_PINCONFIG_PTT_CONTROLS_PTT_LINE // Have Winkeyer PTT setting activate/deactivate PTT line rather than control buffered character PTT hold +//#define OPTION_REVERSE_BUTTON_ORDER // This is mainly for the DJ0MY NanoKeyer http://nanokeyer.wordpress.com/ +#define OPTION_PROG_MEM_TRIM_TRAILING_SPACES // trim trailing spaces from memory when programming in command mode +#define OPTION_DIT_PADDLE_NO_SEND_ON_MEM_RPT // this makes dit paddle memory interruption a little smoother +#define OPTION_MORE_DISPLAY_MSGS // additional optional display messages - comment out to save memory +//#define OPTION_WATCHDOG_TIMER // this enables a four second ATmega48/88/168/328 watchdog timer; use for unattended/remote operation only +//#define OPTION_MOUSE_MOVEMENT_PADDLE // experimental (just fooling around) - mouse movement will act like a paddle +//#define OPTION_NON_ENGLISH_EXTENSIONS // add support for additional CW characters (i.e. À, Å, Þ, etc.) +//#define OPTION_DISPLAY_NON_ENGLISH_EXTENSIONS // LCD display suport for non-English (NO/DK/DE) characters - Courtesy of OZ1JHM +//#define OPTION_UNKNOWN_CHARACTER_ERROR_TONE +// #define OPTION_DO_NOT_SAY_HI +//#define OPTION_PS2_NON_ENGLISH_CHAR_LCD_DISPLAY_SUPPORT // makes some non-English characters from the PS2 keyboard display correctly in the LCD display (donated by Marcin sp5iou) +//#define OPTION_PS2_KEYBOARD_RESET // reset the PS2 keyboard upon startup with 0xFF (contributed by Bill, W9BEL) +//#define OPTION_SAVE_MEMORY_NANOKEYER +//#define OPTION_INVERT_PADDLE_PIN_LOGIC +//#define OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR +//#define OPTION_ADVANCED_SPEED_DISPLAY //enables "nerd" speed visualization on display: wpm, cpm (char per min), duration of dit and dah in milliseconds and ratio (contributed by Giorgio, IZ2XBZ) +//#define OPTION_RUSSIAN_LANGUAGE_SEND_CLI // Russian language CLI sending support (contributed by Павел Бирюков, UA1AQC) +#define OPTION_DO_NOT_SEND_UNKNOWN_CHAR_QUESTION +//#define OPTION_CMOS_SUPER_KEYER_IAMBIC_B_TIMING_ON_BY_DEFAULT +//#define OPTION_SIDETONE_DIGITAL_OUTPUT_NO_SQUARE_WAVE + +// #define OPTION_DIRECT_PADDLE_PIN_READS_MEGA // only works with Mega and pins 2 and 5 - minor performance increase +// #define OPTION_DIRECT_PADDLE_PIN_READS_UNO // Unos or Nanos pins 2 and 5 - do not enable on a nanoKeyer, it uses different pins + +// #define OPTION_WORDSWORTH_CZECH +// #define OPTION_WORDSWORTH_DEUTSCH +// #define OPTION_WORDSWORTH_NORSK + +#define OPTION_EXCLUDE_EXTENDED_CLI_COMMANDS + +// #define OPTION_DFROBOT_LCD_COMMAND_BUTTONS + +// #define OPTION_EXCLUDE_MILL_MODE +// #define OPTION_NO_ULTIMATIC // reduce memory usage by removing ultimatic code. + +// #define OPTION_DISABLE_SERIAL_PORT_CHECKING_WHILE_SENDING_CW +// #define OPTION_PERSONALIZED_STARTUP_SCREEN // displays a user defined string of characters on the second or fourth row of the screen during startup. 1602 display requires OPTION_DO_NOT_SAY_HI +// #define OPTION_SWAP_PADDLE_PARAMETER_CHANGE_DIRECTION // reverses the up/down direction when using the paddles to change the wpm or sidetone frequency +#define OPTION_DISPLAY_MEMORY_CONTENTS_COMMAND_MODE + +// #define OPTION_BEACON_MODE_MEMORY_REPEAT_TIME // to space out the repeated playing of memory 1 when in beacon mode +// #define OPTION_BEACON_MODE_PTT_TAIL_TIME // adds the ptt tail time to each playing of memory 1 in beacon mode + +// #define OPTION_WINKEY_PROSIGN_COMPATIBILITY // Additional character mappings to support K1EL Winkey emulation prosigns diff --git a/k3ng_keyer/keyer_features_and_options_opencwkeyer_mk2.h b/k3ng_keyer/keyer_features_and_options_opencwkeyer_mk2.h new file mode 100755 index 0000000..4017ac2 --- /dev/null +++ b/k3ng_keyer/keyer_features_and_options_opencwkeyer_mk2.h @@ -0,0 +1,131 @@ +// compile time features and options - comment or uncomment to add or delete features +// FEATURES add more bytes to the compiled binary, OPTIONS change code behavior + + +#define FEATURE_BUTTONS +#define FEATURE_COMMAND_MODE +// #define FEATURE_COMMAND_LINE_INTERFACE // Command Line Interface functionality +#define FEATURE_MEMORIES // on the Arduino Due, you must have FEATURE_EEPROM_E24C1024 and E24C1024 EEPROM hardware in order to compile this +// #define FEATURE_MEMORY_MACROS +#define FEATURE_WINKEY_EMULATION // disabling Automatic Software Reset is highly recommended (see documentation) +// #define FEATURE_BEACON // Go into beacon mode if paddle_left pin is LOW at boot up +// #define FEATURE_BEACON_SETTING // Go into beacon mode at boot up if EEPROM setting is enabled (\_ CLI Command) +// #define FEATURE_TRAINING_COMMAND_LINE_INTERFACE +#define FEATURE_POTENTIOMETER // do not enable unless you have a potentiometer connected, otherwise noise will falsely trigger wpm changes +// #define FEATURE_SIDETONE_SWITCH // adds switch control for the sidetone output. requires an external toggle switch (assigned to an arduino pin - see keyer_pin_settings.h). +// #define FEATURE_SIDETONE_NEWTONE // Use the NewTone library, ~1k smaller code size than the standard tone library. Uses timer1 (pins 9 or 10) https://bitbucket.org/teckel12/arduino-new-tone/wiki/Home +// #define FEATURE_SERIAL_HELP +// #define FEATURE_HELL +// #define FEATURE_PS2_KEYBOARD // Use a PS2 keyboard to send code - Change keyboard layout (non-US) in K3NG_PS2Keyboard.h. Additional options below. +// #define FEATURE_USB_KEYBOARD // Use a USB keyboard to send code - Uncomment three lines in k3ng_keyer.ino (search for note_usb_uncomment_lines) +// #define FEATURE_CW_COMPUTER_KEYBOARD // Have an Arduino Due or Leonardo act as a USB HID (Human Interface Device) keyboard and use the paddle to "type" characters on the computer -- uncomment this line in ino file: #include +// #define FEATURE_DEAD_OP_WATCHDOG +// #define FEATURE_AUTOSPACE +// #define FEATURE_FARNSWORTH +// #define FEATURE_DL2SBA_BANKSWITCH // Switch memory banks feature as described here: http://dl2sba.com/index.php?option=com_content&view=article&id=131:nanokeyer&catid=15:shack&Itemid=27#english +// #define FEATURE_LCD_4BIT // classic LCD disidefplay using 4 I/O lines +// #define FEATURE_LCD_8BIT // classic LCD display using 8 I/O lines +// #define FEATURE_LCD_ADAFRUIT_I2C // Adafruit I2C LCD display using MCP23017 at addr 0x20 +// #define FEATURE_LCD_ADAFRUIT_BACKPACK // Adafruit I2C LCD Backup using MCP23008 (courtesy Josiah Ritchie, KE0BLL) +// #define FEATURE_LCD_YDv1 // YourDuino I2C LCD display with old LCM 1602 V1 ic +// #define FEATURE_LCD1602_N07DH // http://linksprite.com/wiki/index.php5?title=16_X_2_LCD_Keypad_Shield_for_Arduino +// #define FEATURE_LCD_SAINSMART_I2C +// #define FEATURE_LCD_FABO_PCF8574 // https://github.com/FaBoPlatform/FaBoLCD-PCF8574-Library +// #define FEATURE_LCD_MATHERTEL_PCF8574 // https://github.com/mathertel/LiquidCrystal_PCF8574 +// #define FEATURE_LCD_I2C_FDEBRABANDER //https://github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library +// #define FEATURE_LCD_HD44780 +// #define FEATURE_CW_DECODER // https://github.com/k3ng/k3ng_cw_keyer/wiki/385-Feature:-CW-Decoder +// #define FEATURE_SLEEP // go to sleep after x minutes to conserve battery power (not compatible with Arduino DUE, may have mixed results with Mega and Mega ADK) +// #define FEATURE_LCD_BACKLIGHT_AUTO_DIM // turn off LCD backlight and/or dim Power Indicator LED after x minutes (LED requires a PWM pin) +// #define FEATURE_ROTARY_ENCODER // rotary encoder speed control +// #define FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING +// #define FEATURE_USB_MOUSE // Uncomment three lines in k3ng_keyer.ino (search for note_usb_uncomment_lines) +// #define FEATURE_CAPACITIVE_PADDLE_PINS // remove the bypass capacitors on the paddle_left and paddle_right lines when using capactive paddles +// #define FEATURE_LED_RING // Mayhew Labs Led Ring support +// #define FEATURE_ALPHABET_SEND_PRACTICE // enables command mode S command - created by Ryan, KC2ZWM +// #define FEATURE_COMMAND_MODE_PROGRESSIVE_5_CHAR_ECHO_PRACTICE // enables command mode U +// #define FEATURE_PTT_INTERLOCK +// #define FEATURE_QLF +// #define FEATURE_EEPROM_E24C1024 +// #define FEATURE_STRAIGHT_KEY +// #define FEATURE_DYNAMIC_DAH_TO_DIT_RATIO +// #define FEATURE_PADDLE_ECHO +// #define FEATURE_STRAIGHT_KEY_ECHO +// #define FEATURE_AMERICAN_MORSE +// #define FEATURE_4x4_KEYPAD // code contributed by Jack, W0XR - documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/380-Feature:-Keypad +// #define FEATURE_3x4_KEYPAD // code contributed by Jack, W0XR - documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/380-Feature:-Keypad +// #define FEATURE_SEQUENCER +// #define FEATURE_SD_CARD_SUPPORT +// #define FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT +// #define FEATURE_WEB_SERVER // Details: https://github.com/k3ng/k3ng_cw_keyer/wiki/390-Feature:-Ethernet,-Web-Server,-and-Internet-Linking +// #define FEATURE_INTERNET_LINK // Details: https://github.com/k3ng/k3ng_cw_keyer/wiki/390-Feature:-Ethernet,-Web-Server,-and-Internet-Linking + +// #define FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT // Activate the Command Line interface on the secondary serial port +#define OPTION_PRIMARY_SERIAL_PORT_DEFAULT_WINKEY_EMULATION // Use when activating both FEATURE_WINKEY_EMULATION and FEATURE_COMMAND_LINE_INTERFACE + // simultaneously. This will make Winkey emulation be the default at boot up; + // hold command button down at boot up to activate CLI mode + +// #define OPTION_SUPPRESS_SERIAL_BOOT_MSG +#define OPTION_INCLUDE_PTT_TAIL_FOR_MANUAL_SENDING +#define OPTION_EXCLUDE_PTT_HANG_TIME_FOR_MANUAL_SENDING +// #define OPTION_WINKEY_DISCARD_BYTES_AT_STARTUP // if ASR is not disabled, you may need this to discard errant serial port bytes at startup +// #define OPTION_WINKEY_STRICT_EEPROM_WRITES_MAY_WEAR_OUT_EEPROM // with this activated the unit will write non-volatile settings to EEPROM when set by Winkey commands +// #define OPTION_WINKEY_SEND_WORDSPACE_AT_END_OF_BUFFER +#define OPTION_WINKEY_STRICT_HOST_OPEN // require an admin host open Winkey command before doing any other commands +#define OPTION_WINKEY_2_SUPPORT // comment out to revert to Winkey version 1 emulation +#define OPTION_WINKEY_INTERRUPTS_MEMORY_REPEAT +//#define OPTION_WINKEY_UCXLOG_9600_BAUD // use this only with UCXLog configured for Winkey 9600 baud mode +#define OPTION_WINKEY_2_HOST_CLOSE_NO_SERIAL_PORT_RESET // (Required for Win-Test to function) +// #define OPTION_WINKEY_FREQUENT_STATUS_REPORT // activate this to make Winkey emulation play better with RUMlog and RUMped +#define OPTION_WINKEY_IGNORE_LOWERCASE // Enable for typical K1EL Winkeyer behavior (use for SkookumLogger version 1.10.14 and prior to workaround "r" bug) +// #define OPTION_WINKEY_BLINK_PTT_ON_HOST_OPEN +// #define OPTION_WINKEY_SEND_VERSION_ON_HOST_CLOSE +// #define OPTION_WINKEY_PINCONFIG_PTT_CONTROLS_PTT_LINE // Have Winkeyer PTT setting activate/deactivate PTT line rather than control buffered character PTT hold +// #define OPTION_REVERSE_BUTTON_ORDER // This is mainly for the DJ0MY NanoKeyer http://nanokeyer.wordpress.com/ +#define OPTION_PROG_MEM_TRIM_TRAILING_SPACES // trim trailing spaces from memory when programming in command mode +#define OPTION_DIT_PADDLE_NO_SEND_ON_MEM_RPT // this makes dit paddle memory interruption a little smoother +// #define OPTION_MORE_DISPLAY_MSGS // additional optional display messages - comment out to save memory +// #define OPTION_WATCHDOG_TIMER // this enables a four second ATmega48/88/168/328 watchdog timer; use for unattended/remote operation only +// #define OPTION_MOUSE_MOVEMENT_PADDLE // experimental (just fooling around) - mouse movement will act like a paddle +// #define OPTION_NON_ENGLISH_EXTENSIONS // add support for additional CW characters (i.e. À, Å, Þ, etc.) +// #define OPTION_DISPLAY_NON_ENGLISH_EXTENSIONS // LCD display suport for non-English (NO/DK/DE) characters - Courtesy of OZ1JHM +// #define OPTION_UNKNOWN_CHARACTER_ERROR_TONE +// #define OPTION_DO_NOT_SAY_HI +// #define OPTION_PS2_NON_ENGLISH_CHAR_LCD_DISPLAY_SUPPORT // makes some non-English characters from the PS2 keyboard display correctly in the LCD display (donated by Marcin sp5iou) +// #define OPTION_PS2_KEYBOARD_RESET // reset the PS2 keyboard upon startup with 0xFF (contributed by Bill, W9BEL) +// #define OPTION_SAVE_MEMORY_NANOKEYER +#define OPTION_CW_KEYBOARD_CAPSLOCK_BEEP +// #define OPTION_CW_KEYBOARD_ITALIAN +// #define OPTION_CW_KEYBOARD_GERMAN +// #define OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR +// #define OPTION_INVERT_PADDLE_PIN_LOGIC +// #define OPTION_ADVANCED_SPEED_DISPLAY //enables "nerd" speed visualization on display: wpm, cpm (char per min), duration of dit and dah in milliseconds and ratio (contributed by Giorgio, IZ2XBZ) +// #define OPTION_PROSIGN_SUPPORT // additional prosign support for paddle and straight key echo on display, CLI, and in memory storage +// #define OPTION_RUSSIAN_LANGUAGE_SEND_CLI // Russian language CLI sending support (contributed by Павел Бирюков, UA1AQC) +#define OPTION_DO_NOT_SEND_UNKNOWN_CHAR_QUESTION +// #define OPTION_CMOS_SUPER_KEYER_IAMBIC_B_TIMING_ON_BY_DEFAULT +// #define OPTION_SIDETONE_DIGITAL_OUTPUT_NO_SQUARE_WAVE + +// #define OPTION_DIRECT_PADDLE_PIN_READS_MEGA // only works with Mega and pins 2 and 5 - minor performance increase +// #define OPTION_DIRECT_PADDLE_PIN_READS_UNO // Unos or Nanos pins 2 and 5 - do not enable on a nanoKeyer, it uses different pins + +// #define OPTION_WORDSWORTH_CZECH +// #define OPTION_WORDSWORTH_DEUTSCH +// #define OPTION_WORDSWORTH_NORSK + +#define OPTION_EXCLUDE_EXTENDED_CLI_COMMANDS + +// #define OPTION_DFROBOT_LCD_COMMAND_BUTTONS + +// #define OPTION_EXCLUDE_MILL_MODE +// #define OPTION_NO_ULTIMATIC // reduce memory usage by removing ultimatic code. + +// #define OPTION_DISABLE_SERIAL_PORT_CHECKING_WHILE_SENDING_CW +// #define OPTION_PERSONALIZED_STARTUP_SCREEN // displays a user defined string of characters on the second or fourth row of the screen during startup. 1602 display requires OPTION_DO_NOT_SAY_HI +// #define OPTION_SWAP_PADDLE_PARAMETER_CHANGE_DIRECTION // reverses the up/down direction when using the paddles to change the wpm or sidetone frequency +// #define OPTION_DISPLAY_MEMORY_CONTENTS_COMMAND_MODE + +// #define OPTION_BEACON_MODE_MEMORY_REPEAT_TIME // to space out the repeated playing of memory 1 when in beacon mode +// #define OPTION_BEACON_MODE_PTT_TAIL_TIME // adds the ptt tail time to each playing of memory 1 in beacon mode + +// #define OPTION_WINKEY_PROSIGN_COMPATIBILITY // Additional character mappings to support K1EL Winkey emulation prosigns diff --git a/k3ng_keyer/keyer_features_and_options_test.h b/k3ng_keyer/keyer_features_and_options_test.h new file mode 100755 index 0000000..1be5d7e --- /dev/null +++ b/k3ng_keyer/keyer_features_and_options_test.h @@ -0,0 +1,187 @@ + +// ######## ######## ###### ######## +// ## ## ## ## ## +// ## ## ## ## +// ## ###### ###### ## +// ## ## ## ## +// ## ## ## ## ## +// ## ######## ###### ## + +#define TEST_CONFIG_1 +// #define TEST_CONFIG_2 + +#if defined(TEST_CONFIG_1) + + +#define FEATURE_BUTTONS +#define FEATURE_COMMAND_MODE +#define FEATURE_COMMAND_LINE_INTERFACE // Command Line Interface functionality +#define FEATURE_MEMORIES // on the Arduino Due, you must have FEATURE_EEPROM_E24C1024 and E24C1024 EEPROM hardware in order to compile this +#define FEATURE_MEMORY_MACROS +// #define FEATURE_WINKEY_EMULATION // disabling Automatic Software Reset is highly recommended (see documentation) +// #define FEATURE_BEACON // Go into beacon mode if paddle_left pin is LOW at boot up +// #define FEATURE_BEACON_SETTING // Go into beacon mode at boot up if EEPROM setting is enabled (\_ CLI Command) +// #define FEATURE_TRAINING_COMMAND_LINE_INTERFACE +// #define FEATURE_POTENTIOMETER // do not enable unless you have a potentiometer connected, otherwise noise will falsely trigger wpm changes +// #define FEATURE_SIDETONE_SWITCH // adds switch control for the sidetone output. requires an external toggle switch (assigned to an arduino pin - see keyer_pin_settings.h). +// #define FEATURE_SIDETONE_NEWTONE // Use the NewTone library, ~1k smaller code size than the standard tone library. Uses timer1 (pins 9 or 10) https://bitbucket.org/teckel12/arduino-new-tone/wiki/Home +// #define FEATURE_SERIAL_HELP +// #define FEATURE_HELL +// #define FEATURE_PS2_KEYBOARD // Use a PS2 keyboard to send code - Change keyboard layout (non-US) in K3NG_PS2Keyboard.h. Additional options below. +// #define FEATURE_USB_KEYBOARD // Use a USB keyboard to send code - Uncomment three lines in k3ng_keyer.ino (search for note_usb_uncomment_lines) +// #define FEATURE_CW_COMPUTER_KEYBOARD // Have an Arduino Due or Leonardo act as a USB HID (Human Interface Device) keyboard and use the paddle to "type" characters on the computer +// #define FEATURE_DEAD_OP_WATCHDOG +#define FEATURE_AUTOSPACE +// #define FEATURE_FARNSWORTH +// #define FEATURE_DL2SBA_BANKSWITCH // Switch memory banks feature as described here: http://dl2sba.com/index.php?option=com_content&view=article&id=131:nanokeyer&catid=15:shack&Itemid=27#english +// #define FEATURE_LCD_4BIT // classic LCD display using 4 I/O lines +// #define FEATURE_LCD_8BIT // classic LCD display using 8 I/O lines +// #define FEATURE_LCD_ADAFRUIT_I2C // Adafruit I2C LCD display using MCP23017 at addr 0x20 +// #define FEATURE_LCD_ADAFRUIT_BACKPACK // Adafruit I2C LCD Backup using MCP23008 (courtesy Josiah Ritchie, KE0BLL) +// #define FEATURE_LCD_YDv1 // YourDuino I2C LCD display with old LCM 1602 V1 ic +// #define FEATURE_LCD1602_N07DH // http://linksprite.com/wiki/index.php5?title=16_X_2_LCD_Keypad_Shield_for_Arduino +// #define FEATURE_LCD_SAINSMART_I2C +// #define FEATURE_LCD_FABO_PCF8574 // https://github.com/FaBoPlatform/FaBoLCD-PCF8574-Library +// #define FEATURE_LCD_MATHERTEL_PCF8574 // https://github.com/mathertel/LiquidCrystal_PCF8574 +// #define FEATURE_LCD_I2C_FDEBRABANDER //https://github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library +// #define FEATURE_CW_DECODER // https://github.com/k3ng/k3ng_cw_keyer/wiki/385-Feature:-CW-Decoder +// #define FEATURE_SLEEP // go to sleep after x minutes to conserve battery power (not compatible with Arduino DUE, may have mixed results with Mega and Mega ADK) +// #define FEATURE_LCD_BACKLIGHT_AUTO_DIM // turn off LCD backlight and/or dim Power Indicator LED after x minutes (LED requires a PWM pin) +// #define FEATURE_ROTARY_ENCODER // rotary encoder speed control +// #define FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING +// #define FEATURE_USB_MOUSE // Uncomment three lines in k3ng_keyer.ino (search for note_usb_uncomment_lines) +// #define FEATURE_CAPACITIVE_PADDLE_PINS // remove the bypass capacitors on the paddle_left and paddle_right lines when using capactive paddles +// #define FEATURE_LED_RING // Mayhew Labs Led Ring support +// #define FEATURE_ALPHABET_SEND_PRACTICE // enables command mode S command - created by Ryan, KC2ZWM +// #define FEATURE_COMMAND_MODE_PROGRESSIVE_5_CHAR_ECHO_PRACTICE // enables command mode U +// #define FEATURE_PTT_INTERLOCK +// #define FEATURE_QLF +// #define FEATURE_EEPROM_E24C1024 +// #define FEATURE_STRAIGHT_KEY +// #define FEATURE_DYNAMIC_DAH_TO_DIT_RATIO +#define FEATURE_PADDLE_ECHO +// #define FEATURE_STRAIGHT_KEY_ECHO +// #define FEATURE_AMERICAN_MORSE +// #define FEATURE_4x4_KEYPAD // code contributed by Jack, W0XR - documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/380-Feature:-Keypad +// #define FEATURE_3x4_KEYPAD // code contributed by Jack, W0XR - documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/380-Feature:-Keypad +// #define FEATURE_EXPERIMENTAL_DDS_SINEWAVE_SIDETONE_MEGA // DDS output Mega Digital Pin 46 +// #define FEATURE_CLOCK // NOT FINISHED YET +// #define FEATURE_SEQUENCER +// #define FEATURE_SD_CARD_SUPPORT +// #define FEATURE_SO2R_BASE // SO2R Box base protocol extensions +// #define FEATURE_SO2R_SWITCHES // SO2R Box TX and RX switches +// #define FEATURE_SO2R_ANTENNA // SO2R Box antenna selection (not fully implemented) +#define FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT +// #define FEATURE_WEB_SERVER // Details: https://github.com/k3ng/k3ng_cw_keyer/wiki/390-Feature:-Ethernet,-Web-Server,-and-Internet-Linking +// #define FEATURE_INTERNET_LINK // Details: https://github.com/k3ng/k3ng_cw_keyer/wiki/390-Feature:-Ethernet,-Web-Server,-and-Internet-Linking + +// UNDER DEVELOPMENT +// #define FEATURE_SINEWAVE_SIDETONE_USING_TIMER_1 // Arduino Uno: sidetone_line = 9 or 10 ; Mega: sidetone_line = 11, 12, or 13 (Further info: https://www.pjrc.com/teensy/td_libs_TimerOne.html ) +// #define FEATURE_SINEWAVE_SIDETONE_USING_TIMER_3 // Arduino Mega: sidetone_line = 2, 3, or 5 (Further info: https://www.pjrc.com/teensy/td_libs_TimerOne.html) + + +#define FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT // Activate the Command Line interface on the secondary serial port +#define OPTION_PRIMARY_SERIAL_PORT_DEFAULT_WINKEY_EMULATION // Use when activating both FEATURE_WINKEY_EMULATION and FEATURE_COMMAND_LINE_INTERFACE + // simultaneously. This will make Winkey emulation be the default at boot up; + // hold command button down at boot up to activate CLI mode + +// #define OPTION_SUPPRESS_SERIAL_BOOT_MSG +#define OPTION_INCLUDE_PTT_TAIL_FOR_MANUAL_SENDING +#define OPTION_EXCLUDE_PTT_HANG_TIME_FOR_MANUAL_SENDING +// #define OPTION_WINKEY_DISCARD_BYTES_AT_STARTUP // if ASR is not disabled, you may need this to discard errant serial port bytes at startup +// #define OPTION_WINKEY_STRICT_EEPROM_WRITES_MAY_WEAR_OUT_EEPROM // with this activated the unit will write non-volatile settings to EEPROM when set by Winkey commands +// #define OPTION_WINKEY_SEND_WORDSPACE_AT_END_OF_BUFFER +#define OPTION_WINKEY_STRICT_HOST_OPEN // require an admin host open Winkey command before doing any other commands +#define OPTION_WINKEY_2_SUPPORT // comment out to revert to Winkey version 1 emulation +//this is removed from other features files - may depricate totally - 2016-09-28 - #define OPTION_WINKEY_SEND_BREAKIN_STATUS_BYTE // additional code to check_dit_paddle() and check_dah_paddle() to send 0xC2 status byte when paddles are hit +#define OPTION_WINKEY_INTERRUPTS_MEMORY_REPEAT +// #define OPTION_WINKEY_UCXLOG_9600_BAUD // use this only with UCXLog configured for Winkey 9600 baud mode +// #define OPTION_WINKEY_UCXLOG_SUPRESS_C4_STATUS_BYTE // use this only with UCXlog if having issues with function key macros +#define OPTION_WINKEY_2_HOST_CLOSE_NO_SERIAL_PORT_RESET // activate this when using Winkey 2 emulation and Win-Test +// #define OPTION_WINKEY_FREQUENT_STATUS_REPORT // activate this to make Winkey emulation play better with RUMlog and RUMped +// #define OPTION_WINKEY_EXTENDED_COMMANDS_WITH_DOLLAR_SIGNS // experimental +#define OPTION_WINKEY_IGNORE_LOWERCASE // Enable for typical K1EL Winkeyer behavior (use for SkookumLogger version 1.10.14 and prior to workaround "r" bug) +// #define OPTION_WINKEY_BLINK_PTT_ON_HOST_OPEN +// #define OPTION_WINKEY_ECHO_7C_BYTE // For debugging purposes only (only in this test feature and options file) (7C = half space character) +// #define OPTION_WINKEY_DO_NOT_SEND_7C_BYTE_HALF_SPACE // For debugging purposes only (only in this test feature and options file) +// #define OPTION_WINKEY_SEND_VERSION_ON_HOST_CLOSE +// #define OPTION_WINKEY_PINCONFIG_PTT_CONTROLS_PTT_LINE // Have Winkeyer PTT setting activate/deactivate PTT line rather than control buffered character PTT hold +// #define OPTION_REVERSE_BUTTON_ORDER // This is mainly for the DJ0MY NanoKeyer http://nanokeyer.wordpress.com/ +#define OPTION_PROG_MEM_TRIM_TRAILING_SPACES // trim trailing spaces from memory when programming in command mode +#define OPTION_DIT_PADDLE_NO_SEND_ON_MEM_RPT // this makes dit paddle memory interruption a little smoother +// #define OPTION_MORE_DISPLAY_MSGS // additional optional display messages - comment out to save memory +// #define OPTION_WATCHDOG_TIMER // this enables a four second ATmega48/88/168/328 watchdog timer; use for unattended/remote operation only +// #define OPTION_MOUSE_MOVEMENT_PADDLE // experimental (just fooling around) - mouse movement will act like a paddle +// #define OPTION_NON_ENGLISH_EXTENSIONS // add support for additional CW characters (i.e. À, Å, Þ, etc.) +// #define OPTION_DISPLAY_NON_ENGLISH_EXTENSIONS // LCD display suport for non-English (NO/DK/DE) characters - Courtesy of OZ1JHM +// #define OPTION_UNKNOWN_CHARACTER_ERROR_TONE +// #define OPTION_DO_NOT_SAY_HI +// #define OPTION_BLINK_HI_ON_PTT +// #define OPTION_PS2_NON_ENGLISH_CHAR_LCD_DISPLAY_SUPPORT // makes some non-English characters from the PS2 keyboard display correctly in the LCD display (donated by Marcin sp5iou) +// #define OPTION_PS2_KEYBOARD_RESET // reset the PS2 keyboard upon startup with 0xFF (contributed by Bill, W9BEL) +// #define OPTION_SAVE_MEMORY_NANOKEYER +#define OPTION_CW_KEYBOARD_CAPSLOCK_BEEP +// #define OPTION_CW_KEYBOARD_ITALIAN +// #define OPTION_CW_KEYBOARD_GERMAN +#define OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR +// #define OPTION_INVERT_PADDLE_PIN_LOGIC +#define OPTION_ADVANCED_SPEED_DISPLAY //enables "nerd" speed visualization on display: wpm, cpm (char per min), duration of dit and dah in milliseconds and ratio (contributed by Giorgio, IZ2XBZ) +// #define OPTION_PROSIGN_SUPPORT // additional prosign support for paddle and straight key echo on display, CLI, and in memory storage +// #define OPTION_RUSSIAN_LANGUAGE_SEND_CLI // Russian language CLI sending support (contributed by Павел Бирюков, UA1AQC) +#define OPTION_DO_NOT_SEND_UNKNOWN_CHAR_QUESTION +// #define OPTION_INTERNET_LINK_NO_UDP_SVC_DURING_KEY_DOWN +// #define OPTION_CMOS_SUPER_KEYER_IAMBIC_B_TIMING_ON_BY_DEFAULT +// #define OPTION_SIDETONE_DIGITAL_OUTPUT_NO_SQUARE_WAVE + +// #define OPTION_WORDSWORTH_CZECH +// #define OPTION_WORDSWORTH_DEUTSCH +// #define OPTION_WORDSWORTH_NORSK + +// #define FEATURE_COMPETITION_COMPRESSION_DETECTION //(Experimental) + +// #define OPTION_DIRECT_PADDLE_PIN_READS_MEGA // only works with Mega and pins 2 and 5 - minor performance increase +// #define OPTION_DIRECT_PADDLE_PIN_READS_UNO // Unos or Nanos pins 2 and 5 - do not enable on a nanoKeyer, it uses different pins + +// #define OPTION_EXCLUDE_EXTENDED_CLI_COMMANDS + +// #define OPTION_DFROBOT_LCD_COMMAND_BUTTONS + +#define OPTION_EXCLUDE_MILL_MODE +// #define OPTION_NO_ULTIMATIC // reduce memory usage by removing ultimatic code. + + +// #define OPTION_DISABLE_SERIAL_PORT_CHECKING_WHILE_SENDING_CW +// #define OPTION_PERSONALIZED_STARTUP_SCREEN // displays a user defined string of characters on the second or fourth row of the screen during startup. 1602 display requires OPTION_DO_NOT_SAY_HI +// #define OPTION_SWAP_PADDLE_PARAMETER_CHANGE_DIRECTION // reverses the up/down direction when using the paddles to change the wpm or sidetone frequency +// #define OPTION_DISPLAY_MEMORY_CONTENTS_COMMAND_MODE + +// #define OPTION_BEACON_MODE_MEMORY_REPEAT_TIME // to space out the repeated playing of memory 1 when in beacon mode +// #define OPTION_BEACON_MODE_PTT_TAIL_TIME // adds the ptt tail time to each playing of memory 1 in beacon mode + +// #define OPTION_WINKEY_PROSIGN_COMPATIBILITY // Additional character mappings to support K1EL Winkey emulation prosigns + + +#endif + + + + +//********************************************************************************************************************* +//********************************************************************************************************************* +//********************************************************************************************************************* +//********************************************************************************************************************* +//********************************************************************************************************************* + +#if defined(TEST_CONFIG_2) + + + +#endif //TEST_CONFIG_2 + +// ######## ######## ###### ######## +// ## ## ## ## ## +// ## ## ## ## +// ## ###### ###### ## +// ## ## ## ## +// ## ## ## ## ## +// ## ######## ###### ## diff --git a/k3ng_keyer/keyer_features_and_options_test_everything.h b/k3ng_keyer/keyer_features_and_options_test_everything.h new file mode 100755 index 0000000..7f79bcd --- /dev/null +++ b/k3ng_keyer/keyer_features_and_options_test_everything.h @@ -0,0 +1,145 @@ + + + +#define FEATURE_BUTTONS +#define FEATURE_COMMAND_MODE +#define FEATURE_COMMAND_LINE_INTERFACE // Command Line Interface functionality +#define FEATURE_MEMORIES // on the Arduino Due, you must have FEATURE_EEPROM_E24C1024 and E24C1024 EEPROM hardware in order to compile this +#define FEATURE_MEMORY_MACROS +#define FEATURE_WINKEY_EMULATION // disabling Automatic Software Reset is highly recommended (see documentation) +#define FEATURE_BEACON // Go into beacon mode if paddle_left pin is LOW at boot up +#define FEATURE_BEACON_SETTING // Go into beacon mode at boot up if EEPROM setting is enabled (\_ CLI Command) +#define FEATURE_TRAINING_COMMAND_LINE_INTERFACE +#define FEATURE_POTENTIOMETER // do not enable unless you have a potentiometer connected, otherwise noise will falsely trigger wpm changes +#define FEATURE_SIDETONE_SWITCH // adds switch control for the sidetone output. requires an external toggle switch (assigned to an arduino pin - see keyer_pin_settings.h). +#define FEATURE_SIDETONE_NEWTONE // Use the NewTone library, ~1k smaller code size than the standard tone library. Uses timer1 (pins 9 or 10) https://bitbucket.org/teckel12/arduino-new-tone/wiki/Home +#define FEATURE_SERIAL_HELP +#define FEATURE_HELL +#define FEATURE_PS2_KEYBOARD // Use a PS2 keyboard to send code - Change keyboard layout (non-US) in K3NG_PS2Keyboard.h. Additional options below. +// #define FEATURE_USB_KEYBOARD // Use a USB keyboard to send code - Uncomment three lines in k3ng_keyer.ino (search for note_usb_uncomment_lines) +// #define FEATURE_CW_COMPUTER_KEYBOARD // Have an Arduino Due or Leonardo act as a USB HID (Human Interface Device) keyboard and use the paddle to "type" characters on the computer +#define FEATURE_DEAD_OP_WATCHDOG +#define FEATURE_AUTOSPACE +#define FEATURE_FARNSWORTH +// #define FEATURE_DL2SBA_BANKSWITCH // Switch memory banks feature as described here: http://dl2sba.com/index.php?option=com_content&view=article&id=131:nanokeyer&catid=15:shack&Itemid=27#english +#define FEATURE_LCD_4BIT // classic LCD display using 4 I/O lines +// #define FEATURE_LCD_8BIT // classic LCD display using 8 I/O lines +// #define FEATURE_LCD_ADAFRUIT_I2C // Adafruit I2C LCD display using MCP23017 at addr 0x20 +// #define FEATURE_LCD_ADAFRUIT_BACKPACK // Adafruit I2C LCD Backup using MCP23008 (courtesy Josiah Ritchie, KE0BLL) +// #define FEATURE_LCD_YDv1 // YourDuino I2C LCD display with old LCM 1602 V1 ic +// #define FEATURE_LCD1602_N07DH // http://linksprite.com/wiki/index.php5?title=16_X_2_LCD_Keypad_Shield_for_Arduino +// #define FEATURE_LCD_SAINSMART_I2C +// #define FEATURE_LCD_I2C_FDEBRABANDER //https://github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library +#define FEATURE_CW_DECODER // https://github.com/k3ng/k3ng_cw_keyer/wiki/385-Feature:-CW-Decoder +#define FEATURE_SLEEP // go to sleep after x minutes to conserve battery power (not compatible with Arduino DUE, may have mixed results with Mega and Mega ADK) +// #define FEATURE_LCD_BACKLIGHT_AUTO_DIM // turn off LCD backlight and/or dim Power Indicator LED after x minutes (LED requires a PWM pin) +#define FEATURE_ROTARY_ENCODER // rotary encoder speed control +#define FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING +// #define FEATURE_USB_MOUSE // Uncomment three lines in k3ng_keyer.ino (search for note_usb_uncomment_lines) +#define FEATURE_CAPACITIVE_PADDLE_PINS // remove the bypass capacitors on the paddle_left and paddle_right lines when using capactive paddles +#define FEATURE_LED_RING // Mayhew Labs Led Ring support +#define FEATURE_ALPHABET_SEND_PRACTICE // enables command mode S command - created by Ryan, KC2ZWM +#define FEATURE_COMMAND_MODE_PROGRESSIVE_5_CHAR_ECHO_PRACTICE // enables command mode U +#define FEATURE_PTT_INTERLOCK +#define FEATURE_QLF +// #define FEATURE_EEPROM_E24C1024 +#define FEATURE_STRAIGHT_KEY +#define FEATURE_DYNAMIC_DAH_TO_DIT_RATIO +#define FEATURE_PADDLE_ECHO +#define FEATURE_STRAIGHT_KEY_ECHO +#define FEATURE_WEB_SERVER +#define FEATURE_INTERNET_LINK +#define FEATURE_AMERICAN_MORSE +// #define FEATURE_4x4_KEYPAD // code contributed by Jack, W0XR - documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/380-Feature:-Keypad +// #define FEATURE_3x4_KEYPAD // code contributed by Jack, W0XR - documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/380-Feature:-Keypad +// #define FEATURE_EXPERIMENTAL_DDS_SINEWAVE_SIDETONE_MEGA // DDS output Mega Digital Pin 46 +// #define FEATURE_CLOCK // NOT FINISHED YET +#define FEATURE_SD_CARD_SUPPORT +#define FEATURE_SEQUENCER +#define FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT +// #define FEATURE_WEB_SERVER // Details: https://github.com/k3ng/k3ng_cw_keyer/wiki/390-Feature:-Ethernet,-Web-Server,-and-Internet-Linking +// #define FEATURE_INTERNET_LINK // Details: https://github.com/k3ng/k3ng_cw_keyer/wiki/390-Feature:-Ethernet,-Web-Server,-and-Internet-Linking + +#define FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT // Activate the Command Line interface on the secondary serial port +#define OPTION_PRIMARY_SERIAL_PORT_DEFAULT_WINKEY_EMULATION // Use when activating both FEATURE_WINKEY_EMULATION and FEATURE_COMMAND_LINE_INTERFACE + // simultaneously. This will make Winkey emulation be the default at boot up; + // hold command button down at boot up to activate CLI mode + +// #define OPTION_SUPPRESS_SERIAL_BOOT_MSG +#define OPTION_INCLUDE_PTT_TAIL_FOR_MANUAL_SENDING +#define OPTION_EXCLUDE_PTT_HANG_TIME_FOR_MANUAL_SENDING +// #define OPTION_WINKEY_DISCARD_BYTES_AT_STARTUP // if ASR is not disabled, you may need this to discard errant serial port bytes at startup +// #define OPTION_WINKEY_STRICT_EEPROM_WRITES_MAY_WEAR_OUT_EEPROM // with this activated the unit will write non-volatile settings to EEPROM when set by Winkey commands +// #define OPTION_WINKEY_SEND_WORDSPACE_AT_END_OF_BUFFER +#define OPTION_WINKEY_STRICT_HOST_OPEN // require an admin host open Winkey command before doing any other commands +#define OPTION_WINKEY_2_SUPPORT // comment out to revert to Winkey version 1 emulation +//this is removed from other features files - may depricate totally - 2016-09-28 - #define OPTION_WINKEY_SEND_BREAKIN_STATUS_BYTE // additional code to check_dit_paddle() and check_dah_paddle() to send 0xC2 status byte when paddles are hit +#define OPTION_WINKEY_INTERRUPTS_MEMORY_REPEAT +#define OPTION_WINKEY_UCXLOG_9600_BAUD // use this only with UCXLog configured for Winkey 9600 baud mode +#define OPTION_WINKEY_UCXLOG_SUPRESS_C4_STATUS_BYTE // use this only with UCXlog if having issues with function key macros +#define OPTION_WINKEY_2_HOST_CLOSE_NO_SERIAL_PORT_RESET // activate this when using Winkey 2 emulation and Win-Test +#define OPTION_WINKEY_FREQUENT_STATUS_REPORT // activate this to make Winkey emulation play better with RUMlog and RUMped +// #define OPTION_WINKEY_EXTENDED_COMMANDS_WITH_DOLLAR_SIGNS // experimental +#define OPTION_WINKEY_IGNORE_LOWERCASE // Enable for typical K1EL Winkeyer behavior (use for SkookumLogger version 1.10.14 and prior to workaround "r" bug) +#define OPTION_WINKEY_BLINK_PTT_ON_HOST_OPEN +#define OPTION_WINKEY_ECHO_7C_BYTE // For debugging purposes only (only in this test feature and options file) (7C = half space character) +// #define OPTION_WINKEY_DO_NOT_SEND_7C_BYTE_HALF_SPACE // For debugging purposes only (only in this test feature and options file) +#define OPTION_WINKEY_SEND_VERSION_ON_HOST_CLOSE +// #define OPTION_WINKEY_PINCONFIG_PTT_CONTROLS_PTT_LINE // Have Winkeyer PTT setting activate/deactivate PTT line rather than control buffered character PTT hold +// #define OPTION_REVERSE_BUTTON_ORDER // This is mainly for the DJ0MY NanoKeyer http://nanokeyer.wordpress.com/ +#define OPTION_PROG_MEM_TRIM_TRAILING_SPACES // trim trailing spaces from memory when programming in command mode +#define OPTION_DIT_PADDLE_NO_SEND_ON_MEM_RPT // this makes dit paddle memory interruption a little smoother +#define OPTION_MORE_DISPLAY_MSGS // additional optional display messages - comment out to save memory +#define OPTION_WATCHDOG_TIMER // this enables a four second ATmega48/88/168/328 watchdog timer; use for unattended/remote operation only +// #define OPTION_MOUSE_MOVEMENT_PADDLE // experimental (just fooling around) - mouse movement will act like a paddle +#define OPTION_NON_ENGLISH_EXTENSIONS // add support for additional CW characters (i.e. À, Å, Þ, etc.) +#define OPTION_KEEP_PTT_KEYED_WHEN_CHARS_BUFFERED // this option keeps PTT high if there are characters buffered from the keyboard, the serial interface, or Winkey +#define OPTION_DISPLAY_NON_ENGLISH_EXTENSIONS // LCD display suport for non-English (NO/DK/DE) characters - Courtesy of OZ1JHM +#define OPTION_UNKNOWN_CHARACTER_ERROR_TONE +// #define OPTION_DO_NOT_SAY_HI +#define OPTION_PS2_NON_ENGLISH_CHAR_LCD_DISPLAY_SUPPORT // makes some non-English characters from the PS2 keyboard display correctly in the LCD display (donated by Marcin sp5iou) +#define OPTION_PS2_KEYBOARD_RESET // reset the PS2 keyboard upon startup with 0xFF (contributed by Bill, W9BEL) +// #define OPTION_SAVE_MEMORY_NANOKEYER +#define OPTION_CW_KEYBOARD_CAPSLOCK_BEEP +// #define OPTION_CW_KEYBOARD_ITALIAN +// #define OPTION_CW_KEYBOARD_GERMAN +#define OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR +// #define OPTION_INVERT_PADDLE_PIN_LOGIC +#define OPTION_ADVANCED_SPEED_DISPLAY //enables "nerd" speed visualization on display: wpm, cpm (char per min), duration of dit and dah in milliseconds and ratio (contributed by Giorgio, IZ2XBZ) +#define OPTION_PROSIGN_SUPPORT // additional prosign support for paddle and straight key echo on display, CLI, and in memory storage +// #define OPTION_RUSSIAN_LANGUAGE_SEND_CLI // Russian language CLI sending support (contributed by Павел Бирюков, UA1AQC) +#define OPTION_DO_NOT_SEND_UNKNOWN_CHAR_QUESTION +// #define OPTION_INTERNET_LINK_NO_UDP_SVC_DURING_KEY_DOWN +// #define OPTION_CMOS_SUPER_KEYER_IAMBIC_B_TIMING_ON_BY_DEFAULT +// #define OPTION_SIDETONE_DIGITAL_OUTPUT_NO_SQUARE_WAVE +#define FEATURE_SD_CARD_SUPPORT +#define FEATURE_SO2R_BASE // SO2R Box base protocol extensions +#define FEATURE_SO2R_SWITCHES // SO2R Box TX and RX switches +#define FEATURE_SO2R_ANTENNA // SO2R Box antenna selection (not fully implemented) + +// #define OPTION_DIRECT_PADDLE_PIN_READS_MEGA // only works with Mega and pins 2 and 5 - minor performance increase +// #define OPTION_DIRECT_PADDLE_PIN_READS_UNO // Unos or Nanos + +// #define OPTION_WORDSWORTH_CZECH +// #define OPTION_WORDSWORTH_DEUTSCH +// #define OPTION_WORDSWORTH_NORSK + +// #define FEATURE_COMPETITION_COMPRESSION_DETECTION //(Experimental) + +// #define OPTION_DIRECT_PADDLE_PIN_READS_MEGA // only works with Mega and pins 2 and 5 - minor performance increase... maybe? + +// #define OPTION_DFROBOT_LCD_COMMAND_BUTTONS + +// #define OPTION_EXCLUDE_MILL_MODE +// #define OPTION_NO_ULTIMATIC // reduce memory usage by removing ultimatic code. + + +// #define OPTION_DISABLE_SERIAL_PORT_CHECKING_WHILE_SENDING_CW +// #define OPTION_PERSONALIZED_STARTUP_SCREEN // displays a user defined string of characters on the second or fourth row of the screen during startup. 1602 display requires OPTION_DO_NOT_SAY_HI +// #define OPTION_SWAP_PADDLE_PARAMETER_CHANGE_DIRECTION // reverses the up/down direction when using the paddles to change the wpm or sidetone frequency +#define OPTION_DISPLAY_MEMORY_CONTENTS_COMMAND_MODE + +#define OPTION_BEACON_MODE_MEMORY_REPEAT_TIME // to space out the repeated playing of memory 1 when in beacon mode +#define OPTION_BEACON_MODE_PTT_TAIL_TIME // adds the ptt tail time to each playing of memory 1 in beacon mode + +#define OPTION_WINKEY_PROSIGN_COMPATIBILITY // Additional character mappings to support K1EL Winkey emulation prosigns \ No newline at end of file diff --git a/k3ng_keyer/keyer_features_and_options_tinykeyer.h b/k3ng_keyer/keyer_features_and_options_tinykeyer.h new file mode 100755 index 0000000..539b754 --- /dev/null +++ b/k3ng_keyer/keyer_features_and_options_tinykeyer.h @@ -0,0 +1,95 @@ +// compile time features and options - comment or uncomment to add or delete features +// FEATURES add more bytes to the compiled binary, OPTIONS change code behavior + +// features & options for TinyKeyer by OK1RR + +#define FEATURE_BUTTONS +#define FEATURE_COMMAND_MODE +//#define FEATURE_COMMAND_LINE_INTERFACE // (this no longer requires FEATURE_SERIAL) +#define FEATURE_MEMORIES +//#define FEATURE_MEMORY_MACROS +#define FEATURE_WINKEY_EMULATION // disabling Automatic Software Reset is highly recommended (see documentation) (this no longer requires FEATURE_SERIAL) +//#define FEATURE_POTENTIOMETER // do not enable unless you have a potentiometer connected, otherwise noise will falsely trigger wpm changes +//#define FEATURE_BEACON // Go into beacon mode if paddle_left pin is LOW at boot up +//#define FEATURE_BEACON_SETTING // Go into beacon mode at boot up if EEPROM setting is enabled (\_ CLI Command) +//#define FEATURE_SIDETONE_SWITCH // adds switch control for the sidetone output. requires an external toggle switch (assigned to an arduino pin - see keyer_pin_settings.h). +//#define FEATURE_SIDETONE_NEWTONE // Use the NewTone library, ~1k smaller code size than the standard tone library. Uses timer1 (pins 9 or 10) https://bitbucket.org/teckel12/arduino-new-tone/wiki/Home +//#define FEATURE_DEAD_OP_WATCHDOG +#define FEATURE_AUTOSPACE +//#define FEATURE_FARNSWORTH +//#define FEATURE_DL2SBA_BANKSWITCH // Switch memory banks feature as described here: http://dl2sba.com/index.php?option=com_content&view=article&id=131:nanokeyer&catid=15:shack&Itemid=27#english +//#define FEATURE_SLEEP // go to sleep after x minutes to conserve battery power +//#define FEATURE_LCD_BACKLIGHT_AUTO_DIM // turn off LCD backlight and/or dim Power Indicator LED after x minutes (LED requires a PWM pin) +#define FEATURE_ROTARY_ENCODER // rotary encoder speed control +//#define FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING +//#define FEATURE_DIT_DAH_BUFFER_CONTROL +//#define FEATURE_ALPHABET_SEND_PRACTICE // enables command mode S command - created by Ryan, KC2ZWM +//#define FEATURE_COMMAND_MODE_PROGRESSIVE_5_CHAR_ECHO_PRACTICE // enables command mode U +//#define FEATURE_PTT_INTERLOCK +//#define FEATURE_QLF +//#define FEATURE_EEPROM_E24C1024 +//#define FEATURE_STRAIGHT_KEY +//#define FEATURE_DYNAMIC_DAH_TO_DIT_RATIO +//#define FEATURE_AMERICAN_MORSE +// #define FEATURE_4x4_KEYPAD // code contributed by Jack, W0XR - documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/380-Feature:-Keypad +// #define FEATURE_3x4_KEYPAD // code contributed by Jack, W0XR - documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/380-Feature:-Keypad +// #define FEATURE_SEQUENCER +// #define FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT +// #define FEATURE_WEB_SERVER // Details: https://github.com/k3ng/k3ng_cw_keyer/wiki/390-Feature:-Ethernet,-Web-Server,-and-Internet-Linking +// #define FEATURE_INTERNET_LINK // Details: https://github.com/k3ng/k3ng_cw_keyer/wiki/390-Feature:-Ethernet,-Web-Server,-and-Internet-Linking + +#define OPTION_SUPPRESS_SERIAL_BOOT_MSG +#define OPTION_INCLUDE_PTT_TAIL_FOR_MANUAL_SENDING +#define OPTION_EXCLUDE_PTT_HANG_TIME_FOR_MANUAL_SENDING +//#define OPTION_SERIAL_PORT_DEFAULT_WINKEY_EMULATION // Use when activating both FEATURE_WINKEY_EMULATION and FEATURE_COMMAND_LINE_INTERFACE simultaneously. This will make Winkey emulation be the default at boot up; hold command button down at boot up to activate CLI mode +//#define OPTION_WINKEY_DISCARD_BYTES_AT_STARTUP // if ASR is not disabled, you may need this to discard errant serial port bytes at startup +//#define OPTION_WINKEY_STRICT_EEPROM_WRITES_MAY_WEAR_OUT_EEPROM // with this activated the unit will write non-volatile settings to EEPROM when set by Winkey commands +#define OPTION_WINKEY_SEND_WORDSPACE_AT_END_OF_BUFFER +#define OPTION_WINKEY_STRICT_HOST_OPEN // require an admin host open Winkey command before doing any other commands +#define OPTION_WINKEY_2_SUPPORT // comment out to revert to Winkey version 1 emulation +#define OPTION_WINKEY_INTERRUPTS_MEMORY_REPEAT +//#define OPTION_WINKEY_UCXLOG_9600_BAUD // use this only with UCXLog configured for Winkey 9600 baud mode +#define OPTION_WINKEY_2_HOST_CLOSE_NO_SERIAL_PORT_RESET // (Required for Win-Test to function) +//#define OPTION_WINKEY_FREQUENT_STATUS_REPORT // activate this to make Winkey emulation play better with RUMlog and RUMped +//#define OPTION_WINKEY_IGNORE_LOWERCASE // Enable for typical K1EL Winkeyer behavior (use for SkookumLogger version 1.10.14 and prior to workaround bug) +// #define OPTION_WINKEY_BLINK_PTT_ON_HOST_OPEN +// #define OPTION_WINKEY_SEND_VERSION_ON_HOST_CLOSE +// #define OPTION_WINKEY_PINCONFIG_PTT_CONTROLS_PTT_LINE // Have Winkeyer PTT setting activate/deactivate PTT line rather than control buffered character PTT hold +#define OPTION_PROG_MEM_TRIM_TRAILING_SPACES // trim trailing spaces from memory when programming in command mode +#define OPTION_DIT_PADDLE_NO_SEND_ON_MEM_RPT // this makes dit paddle memory interruption a little smoother +//#define OPTION_WATCHDOG_TIMER // this enables a four second ATmega48/88/168/328 watchdog timer; use for unattended/remote operation only +//#define OPTION_NON_ENGLISH_EXTENSIONS // add support for additional CW characters (i.e. À, Å, Þ, etc.) +//#define OPTION_UNKNOWN_CHARACTER_ERROR_TONE +//#define OPTION_DO_NOT_SAY_HI +#define OPTION_SAVE_MEMORY_NANOKEYER +//#define OPTION_INVERT_PADDLE_PIN_LOGIC +//#define OPTION_DIT_DAH_BUFFERS_OFF_BY_DEFAULT_FOR_FEATURE_DIT_DAH_BUFFER_CONTROL +//#define OPTION_RUSSIAN_LANGUAGE_SEND_CLI // Russian language CLI sending support (contributed by Павел Бирюков, UA1AQC) +#define OPTION_DO_NOT_SEND_UNKNOWN_CHAR_QUESTION +//#define OPTION_CMOS_SUPER_KEYER_IAMBIC_B_TIMING_ON_BY_DEFAULT +//#define OPTION_SIDETONE_DIGITAL_OUTPUT_NO_SQUARE_WAVE +//#define FEATURE_SD_CARD_SUPPORT + +// #define OPTION_DIRECT_PADDLE_PIN_READS_MEGA // only works with Mega and pins 2 and 5 - minor performance increase +// #define OPTION_DIRECT_PADDLE_PIN_READS_UNO // Unos or Nanos pins 2 and 5 - do not enable on a nanoKeyer, it uses different pins + +// #define OPTION_WORDSWORTH_CZECH +// #define OPTION_WORDSWORTH_DEUTSCH +// #define OPTION_WORDSWORTH_NORSK + +#define OPTION_EXCLUDE_EXTENDED_CLI_COMMANDS + +// #define OPTION_DFROBOT_LCD_COMMAND_BUTTONS + +#define OPTION_EXCLUDE_MILL_MODE +// #define OPTION_NO_ULTIMATIC // reduce memory usage by removing ultimatic code. + +#define OPTION_DISABLE_SERIAL_PORT_CHECKING_WHILE_SENDING_CW +// #define OPTION_PERSONALIZED_STARTUP_SCREEN // displays a user defined string of characters on the second or fourth row of the screen during startup. 1602 display requires OPTION_DO_NOT_SAY_HI +// #define OPTION_SWAP_PADDLE_PARAMETER_CHANGE_DIRECTION // reverses the up/down direction when using the paddles to change the wpm or sidetone frequency +// #define OPTION_DISPLAY_MEMORY_CONTENTS_COMMAND_MODE + +// #define OPTION_BEACON_MODE_MEMORY_REPEAT_TIME // to space out the repeated playing of memory 1 when in beacon mode +// #define OPTION_BEACON_MODE_PTT_TAIL_TIME // adds the ptt tail time to each playing of memory 1 in beacon mode + +// #define OPTION_WINKEY_PROSIGN_COMPATIBILITY // Additional character mappings to support K1EL Winkey emulation prosigns diff --git a/k3ng_keyer/keyer_features_and_options_yaacwk.h b/k3ng_keyer/keyer_features_and_options_yaacwk.h new file mode 100755 index 0000000..8369397 --- /dev/null +++ b/k3ng_keyer/keyer_features_and_options_yaacwk.h @@ -0,0 +1,126 @@ +// This file is for the Yaacwk interface http://i1cra.briata.org/yaacwk/ +// YAACWK stands for Yet Another Arduino CW Keyer, it's based on AtMega 644p +// see http://i1cra.briata.org/yaacwk/ for more info + +// compile time features and options - comment or uncomment to add or delete features +// FEATURES add more bytes to the compiled binary, OPTIONS change code behavior + +#define FEATURE_BUTTONS +#define FEATURE_COMMAND_MODE +#define FEATURE_COMMAND_LINE_INTERFACE // (this no longer requires FEATURE_SERIAL) +#define FEATURE_MEMORIES +//#define FEATURE_MEMORY_MACROS +#define FEATURE_WINKEY_EMULATION // disabling Automatic Software Reset is highly recommended (see documentation) (this no longer requires FEATURE_SERIAL) +//#define FEATURE_BEACON // Go into beacon mode if paddle_left pin is LOW at boot up +//#define FEATURE_BEACON_SETTING // Go into beacon mode at boot up if EEPROM setting is enabled (\_ CLI Command) +//#define FEATURE_TRAINING_COMMAND_LINE_INTERFACE +#define FEATURE_POTENTIOMETER // do not enable unless you have a potentiometer connected, otherwise noise will falsely trigger wpm changes +//#define FEATURE_SIDETONE_SWITCH // adds switch control for the sidetone output. requires an external toggle switch (assigned to an arduino pin - see keyer_pin_settings.h). +//#define FEATURE_SIDETONE_NEWTONE // Use the NewTone library, ~1k smaller code size than the standard tone library. Uses timer1 (pins 9 or 10) https://bitbucket.org/teckel12/arduino-new-tone/wiki/Home +//#define FEATURE_SERIAL_HELP +//#define FEATURE_HELL +#define FEATURE_PS2_KEYBOARD // Use a PS2 keyboard to send code - Change keyboard layout (non-US) in K3NG_PS2Keyboard.h. Additional options below. +//#define FEATURE_USB_KEYBOARD // Use a USB keyboard to send code - Uncomment three lines in k3ng_keyer.ino (search for note_usb_uncomment_lines) +//#define FEATURE_DEAD_OP_WATCHDOG +//#define FEATURE_AUTOSPACE +//#define FEATURE_FARNSWORTH +//#define FEATURE_DL2SBA_BANKSWITCH // Switch memory banks feature as described here: http://dl2sba.com/index.php?option=com_content&view=article&id=131:nanokeyer&catid=15:shack&Itemid=27#english +#define FEATURE_LCD_4BIT // classic LCD disidefplay using 4 I/O lines +//#define FEATURE_LCD_ADAFRUIT_I2C // Adafruit I2C LCD display using MCP23017 at addr 0x20 +//#define FEATURE_LCD_YDv1 // YourDuino I2C LCD display with old LCM 1602 V1 ic +//#define FEATURE_LCD_FABO_PCF8574 // https://github.com/FaBoPlatform/FaBoLCD-PCF8574-Library +#define FEATURE_CW_DECODER +//#define FEATURE_SLEEP // go to sleep after x minutes to conserve battery power +//#define FEATURE_LCD_BACKLIGHT_AUTO_DIM // turn off LCD backlight and/or dim Power Indicator LED after x minutes (LED requires a PWM pin) +//#define FEATURE_ROTARY_ENCODER // rotary encoder speed control +//#define FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING +//#define FEATURE_USB_MOUSE // Uncomment three lines in k3ng_keyer.ino (search for note_usb_uncomment_lines) +//#define FEATURE_CAPACITIVE_PADDLE_PINS // remove the bypass capacitors on the paddle_left and paddle_right lines when using capactive paddles +//#define FEATURE_LED_RING // Mayhew Labs Led Ring support +#define FEATURE_ALPHABET_SEND_PRACTICE // enables command mode S command - created by Ryan, KC2ZWM +//#define FEATURE_COMMAND_MODE_PROGRESSIVE_5_CHAR_ECHO_PRACTICE // enables command mode U +//#define FEATURE_PTT_INTERLOCK +//#define FEATURE_QLF +//#define FEATURE_EEPROM_E24C1024 +//#define FEATURE_STRAIGHT_KEY +//#define FEATURE_DYNAMIC_DAH_TO_DIT_RATIO +//#define FEATURE_PADDLE_ECHO +//#define FEATURE_STRAIGHT_KEY_ECHO +//#define FEATURE_AMERICAN_MORSE +//#define FEATURE_4x4_KEYPAD // code contributed by Jack, W0XR - documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/380-Feature:-Keypad +//#define FEATURE_3x4_KEYPAD // code contributed by Jack, W0XR - documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/380-Feature:-Keypad +//#define FEATURE_SEQUENCER +//#define FEATURE_SD_CARD_SUPPORT +// #define FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT + + +//#define FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT // Activate the Command Line interface on the secondary serial port +#define OPTION_PRIMARY_SERIAL_PORT_DEFAULT_WINKEY_EMULATION // Use when activating both FEATURE_WINKEY_EMULATION and FEATURE_COMMAND_LINE_INTERFACE + // simultaneously. This will make Winkey emulation be the default at boot up; + // hold command button down at boot up to activate CLI mode + +//#define OPTION_SUPPRESS_SERIAL_BOOT_MSG +#define OPTION_INCLUDE_PTT_TAIL_FOR_MANUAL_SENDING +#define OPTION_EXCLUDE_PTT_HANG_TIME_FOR_MANUAL_SENDING +#define OPTION_SERIAL_PORT_DEFAULT_WINKEY_EMULATION // Use when activating both FEATURE_WINKEY_EMULATION and FEATURE_COMMAND_LINE_INTERFACE simultaneously. This will make Winkey emulation be the default at boot up; hold command button down at boot up to activate CLI mode +//#define OPTION_WINKEY_DISCARD_BYTES_AT_STARTUP // if ASR is not disabled, you may need this to discard errant serial port bytes at startup +//#define OPTION_WINKEY_STRICT_EEPROM_WRITES_MAY_WEAR_OUT_EEPROM // with this activated the unit will write non-volatile settings to EEPROM when set by Winkey commands +//#define OPTION_WINKEY_SEND_WORDSPACE_AT_END_OF_BUFFER +#define OPTION_WINKEY_STRICT_HOST_OPEN // require an admin host open Winkey command before doing any other commands +#define OPTION_WINKEY_2_SUPPORT // comment out to revert to Winkey version 1 emulation +#define OPTION_WINKEY_INTERRUPTS_MEMORY_REPEAT +//#define OPTION_WINKEY_UCXLOG_9600_BAUD // use this only with UCXLog configured for Winkey 9600 baud mode +#define OPTION_WINKEY_2_HOST_CLOSE_NO_SERIAL_PORT_RESET // activate this when using Winkey 2 emulation and Win-Test +#define OPTION_WINKEY_FREQUENT_STATUS_REPORT // activate this to make Winkey emulation play better with RUMlog and RUMped +#define OPTION_WINKEY_IGNORE_LOWERCASE // Enable for typical K1EL Winkeyer behavior (use for SkookumLogger version 1.10.14 and prior to workaround bug) +//#define OPTION_WINKEY_SEND_VERSION_ON_HOST_CLOSE +//#define OPTION_WINKEY_PINCONFIG_PTT_CONTROLS_PTT_LINE // Have Winkeyer PTT setting activate/deactivate PTT line rather than control buffered character PTT hold +//#define OPTION_REVERSE_BUTTON_ORDER // This is mainly for the DJ0MY NanoKeyer http://nanokeyer.wordpress.com/ +#define OPTION_PROG_MEM_TRIM_TRAILING_SPACES // trim trailing spaces from memory when programming in command mode +#define OPTION_DIT_PADDLE_NO_SEND_ON_MEM_RPT // this makes dit paddle memory interruption a little smoother +//#define OPTION_MORE_DISPLAY_MSGS // additional optional display messages - comment out to save memory +//#define OPTION_WATCHDOG_TIMER // this enables a four second ATmega48/88/168/328 watchdog timer; use for unattended/remote operation only +//#define OPTION_MOUSE_MOVEMENT_PADDLE // experimental (just fooling around) - mouse movement will act like a paddle +//#define OPTION_NON_ENGLISH_EXTENSIONS // add support for additional CW characters (i.e. À, Å, Þ, etc.) +//#define OPTION_KEEP_PTT_KEYED_WHEN_CHARS_BUFFERED // this option keeps PTT high if there are characters buffered from the keyboard, the serial interface, or Winkey +//#define OPTION_DISPLAY_NON_ENGLISH_EXTENSIONS // LCD display suport for non-English (NO/DK/DE) characters - Courtesy of OZ1JHM +//#define OPTION_UNKNOWN_CHARACTER_ERROR_TONE +//#define OPTION_DO_NOT_SAY_HI +//#define OPTION_PS2_NON_ENGLISH_CHAR_LCD_DISPLAY_SUPPORT // makes some non-English characters from the PS2 keyboard display correctly in the LCD display (donated by Marcin sp5iou) +//#define OPTION_PS2_KEYBOARD_RESET // reset the PS2 keyboard upon startup with 0xFF (contributed by Bill, W9BEL) +//#define OPTION_SAVE_MEMORY_NANOKEYER +//#define OPTION_CW_KEYBOARD_CAPSLOCK_BEEP +#define OPTION_CW_KEYBOARD_ITALIAN +//#define OPTION_INVERT_PADDLE_PIN_LOGIC +#define OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR +//#define OPTION_ADVANCED_SPEED_DISPLAY //enables "nerd" speed visualization on display: wpm, cpm (char per min), duration of dit and dah in milliseconds and ratio (contributed by Giorgio, IZ2XBZ) +//#define OPTION_RUSSIAN_LANGUAGE_SEND_CLI // Russian language CLI sending support (contributed by Павел Бирюков, UA1AQC) +//#define OPTION_DO_NOT_SEND_UNKNOWN_CHAR_QUESTION +//#define OPTION_CMOS_SUPER_KEYER_IAMBIC_B_TIMING_ON_BY_DEFAULT +//#define OPTION_SIDETONE_DIGITAL_OUTPUT_NO_SQUARE_WAVE + +// #define OPTION_WORDSWORTH_CZECH +// #define OPTION_WORDSWORTH_DEUTSCH +// #define OPTION_WORDSWORTH_NORSK + +#define OPTION_EXCLUDE_EXTENDED_CLI_COMMANDS + +// #define OPTION_DFROBOT_LCD_COMMAND_BUTTONS + +#define OPTION_EXCLUDE_MILL_MODE +// #define OPTION_NO_ULTIMATIC // reduce memory usage by removing ultimatic code. + + +// #define OPTION_DISABLE_SERIAL_PORT_CHECKING_WHILE_SENDING_CW + +// #define OPTION_DIRECT_PADDLE_PIN_READS_MEGA // only works with Mega and pins 2 and 5 - minor performance increase +// #define OPTION_DIRECT_PADDLE_PIN_READS_UNO // Unos or Nanos pins 2 and 5 - do not enable on a nanoKeyer, it uses different pins + +// #define OPTION_PERSONALIZED_STARTUP_SCREEN // displays a user defined string of characters on the second or fourth row of the screen during startup. 1602 display requires OPTION_DO_NOT_SAY_HI +// #define OPTION_SWAP_PADDLE_PARAMETER_CHANGE_DIRECTION // reverses the up/down direction when using the paddles to change the wpm or sidetone frequency +// #define OPTION_DISPLAY_MEMORY_CONTENTS_COMMAND_MODE + +// #define OPTION_BEACON_MODE_MEMORY_REPEAT_TIME // to space out the repeated playing of memory 1 when in beacon mode +// #define OPTION_BEACON_MODE_PTT_TAIL_TIME // adds the ptt tail time to each playing of memory 1 in beacon mode + +// #define OPTION_WINKEY_PROSIGN_COMPATIBILITY // Additional character mappings to support K1EL Winkey emulation prosigns diff --git a/k3ng_keyer/keyer_features_and_options_yccc_so2r_mini.h b/k3ng_keyer/keyer_features_and_options_yccc_so2r_mini.h new file mode 100755 index 0000000..f8293ba --- /dev/null +++ b/k3ng_keyer/keyer_features_and_options_yccc_so2r_mini.h @@ -0,0 +1,140 @@ +// Y Y CCCCC CCCCC CCCCC SSSS OOOOO 222 RRRR M M IIIII N N IIIII +// Y Y C C C S O O 2 R R M M M I NN N I +// Y C C C SSSSS O O 2 RRRR M M I N N N I +// Y C C C S O O 2 R R M M I N NN I +// Y CCCCC CCCCC CCCCC SSSS OOOOO 22222 R R M M IIIII N N IIIII + +// compile time features and options - comment or uncomment to add or delete features +// FEATURES add more bytes to the compiled binary, OPTIONS change code behavior + + +// #define FEATURE_BUTTONS +// #define FEATURE_COMMAND_LINE_INTERFACE // Command Line Interface functionality +// #define FEATURE_MEMORIES // on the Arduino Due, you must have FEATURE_EEPROM_E24C1024 and E24C1024 EEPROM hardware in order to compile this +// #define FEATURE_MEMORY_MACROS +#define FEATURE_WINKEY_EMULATION // disabling Automatic Software Reset is highly recommended (see documentation) +// #define FEATURE_BEACON // Go into beacon mode if paddle_left pin is LOW at boot up +// #define FEATURE_BEACON_SETTING // Go into beacon mode at boot up if EEPROM setting is enabled (\_ CLI Command) +// #define FEATURE_TRAINING_COMMAND_LINE_INTERFACE +// #define FEATURE_POTENTIOMETER // do not enable unless you have a potentiometer connected, otherwise noise will falsely trigger wpm changes +// #define FEATURE_SIDETONE_SWITCH // adds switch control for the sidetone output. requires an external toggle switch (assigned to an arduino pin - see keyer_pin_settings.h). +// #define FEATURE_SIDETONE_NEWTONE // Use the NewTone library, ~1k smaller code size than the standard tone library. Uses timer1 (pins 9 or 10) https://bitbucket.org/teckel12/arduino-new-tone/wiki/Home +// #define FEATURE_SERIAL_HELP +// #define FEATURE_HELL +// #define FEATURE_PS2_KEYBOARD // Use a PS2 keyboard to send code - Change keyboard layout (non-US) in K3NG_PS2Keyboard.h. Additional options below. +// #define FEATURE_USB_KEYBOARD // Use a USB keyboard to send code - Uncomment three lines in k3ng_keyer.ino (search for note_usb_uncomment_lines) +// #define FEATURE_CW_COMPUTER_KEYBOARD // Have an Arduino Due or Leonardo act as a USB HID (Human Interface Device) keyboard and use the paddle to "type" characters on the computer -- uncomment this line in ino file: #include +// #define FEATURE_DEAD_OP_WATCHDOG +// #define FEATURE_AUTOSPACE +// #define FEATURE_FARNSWORTH +// #define FEATURE_DL2SBA_BANKSWITCH // Switch memory banks feature as described here: http://dl2sba.com/index.php?option=com_content&view=article&id=131:nanokeyer&catid=15:shack&Itemid=27#english +// #define FEATURE_LCD_4BIT // classic LCD disidefplay using 4 I/O lines +// #define FEATURE_LCD_8BIT // classic LCD display using 8 I/O lines +// #define FEATURE_LCD_ADAFRUIT_I2C // Adafruit I2C LCD display using MCP23017 at addr 0x20 +// #define FEATURE_LCD_ADAFRUIT_BACKPACK // Adafruit I2C LCD Backup using MCP23008 (courtesy Josiah Ritchie, KE0BLL) +// #define FEATURE_LCD_YDv1 // YourDuino I2C LCD display with old LCM 1602 V1 ic +// #define FEATURE_LCD1602_N07DH // http://linksprite.com/wiki/index.php5?title=16_X_2_LCD_Keypad_Shield_for_Arduino +// #define FEATURE_LCD_SAINSMART_I2C +// #define FEATURE_LCD_FABO_PCF8574 // https://github.com/FaBoPlatform/FaBoLCD-PCF8574-Library +// #define FEATURE_LCD_MATHERTEL_PCF8574 // https://github.com/mathertel/LiquidCrystal_PCF8574 +// #define FEATURE_LCD_I2C_FDEBRABANDER //https://github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library + +// #define FEATURE_LCD_HD44780 +// #define FEATURE_CW_DECODER +// #define FEATURE_SLEEP // go to sleep after x minutes to conserve battery power (not compatible with Arduino DUE, may have mixed results with Mega and Mega ADK) +// #define FEATURE_LCD_BACKLIGHT_AUTO_DIM // turn off LCD backlight and/or dim Power Indicator LED after x minutes (LED requires a PWM pin) +// #define FEATURE_ROTARY_ENCODER // rotary encoder speed control +// #define FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING +// #define FEATURE_USB_MOUSE // Uncomment three lines in k3ng_keyer.ino (search for note_usb_uncomment_lines) +// #define FEATURE_CAPACITIVE_PADDLE_PINS // remove the bypass capacitors on the paddle_left and paddle_right lines when using capactive paddles +// #define FEATURE_LED_RING // Mayhew Labs Led Ring support +// #define FEATURE_ALPHABET_SEND_PRACTICE // enables command mode S command - created by Ryan, KC2ZWM +// #define FEATURE_COMMAND_MODE_PROGRESSIVE_5_CHAR_ECHO_PRACTICE // enables command mode U +// #define FEATURE_PTT_INTERLOCK +// #define FEATURE_QLF +// #define FEATURE_EEPROM_E24C1024 +// #define FEATURE_STRAIGHT_KEY +// #define FEATURE_DYNAMIC_DAH_TO_DIT_RATIO +// #define FEATURE_PADDLE_ECHO +// #define FEATURE_STRAIGHT_KEY_ECHO +// #define FEATURE_AMERICAN_MORSE +// #define FEATURE_4x4_KEYPAD // code contributed by Jack, W0XR - documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/380-Feature:-Keypad +// #define FEATURE_3x4_KEYPAD // code contributed by Jack, W0XR - documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/380-Feature:-Keypad +// #define FEATURE_SEQUENCER +#define FEATURE_SO2R_BASE // SO2R Box base protocol extensions +//#define FEATURE_SO2R_SWITCHES // SO2R Box TX and RX switches +// #define FEATURE_SO2R_ANTENNA // SO2R Box antenna selection (not fully implemented) +// #define FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT +// #define FEATURE_WEB_SERVER // Details: https://github.com/k3ng/k3ng_cw_keyer/wiki/390-Feature:-Ethernet,-Web-Server,-and-Internet-Linking +// #define FEATURE_INTERNET_LINK // Details: https://github.com/k3ng/k3ng_cw_keyer/wiki/390-Feature:-Ethernet,-Web-Server,-and-Internet-Linking + +// #define FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT // Activate the Command Line interface on the secondary serial port +#define OPTION_PRIMARY_SERIAL_PORT_DEFAULT_WINKEY_EMULATION // Use when activating both FEATURE_WINKEY_EMULATION and FEATURE_COMMAND_LINE_INTERFACE + // simultaneously. This will make Winkey emulation be the default at boot up; + // hold command button down at boot up to activate CLI mode + +// #define OPTION_SUPPRESS_SERIAL_BOOT_MSG +#define OPTION_INCLUDE_PTT_TAIL_FOR_MANUAL_SENDING +#define OPTION_EXCLUDE_PTT_HANG_TIME_FOR_MANUAL_SENDING +// #define OPTION_WINKEY_DISCARD_BYTES_AT_STARTUP // if ASR is not disabled, you may need this to discard errant serial port bytes at startup +// #define OPTION_WINKEY_STRICT_EEPROM_WRITES_MAY_WEAR_OUT_EEPROM // with this activated the unit will write non-volatile settings to EEPROM when set by Winkey commands +// #define OPTION_WINKEY_SEND_WORDSPACE_AT_END_OF_BUFFER +// #define OPTION_WINKEY_STRICT_HOST_OPEN // require an admin host open Winkey command before doing any other commands +#define OPTION_WINKEY_2_SUPPORT // comment out to revert to Winkey version 1 emulation +#define OPTION_WINKEY_INTERRUPTS_MEMORY_REPEAT +//#define OPTION_WINKEY_UCXLOG_9600_BAUD // use this only with UCXLog configured for Winkey 9600 baud mode +#define OPTION_WINKEY_2_HOST_CLOSE_NO_SERIAL_PORT_RESET // (Required for Win-Test to function) +// #define OPTION_WINKEY_FREQUENT_STATUS_REPORT // activate this to make Winkey emulation play better with RUMlog and RUMped +#define OPTION_WINKEY_IGNORE_LOWERCASE // Enable for typical K1EL Winkeyer behavior (use for SkookumLogger version 1.10.14 and prior to workaround "r" bug) +// #define OPTION_WINKEY_BLINK_PTT_ON_HOST_OPEN +// #define OPTION_WINKEY_PINCONFIG_PTT_CONTROLS_PTT_LINE // Have Winkeyer PTT setting activate/deactivate PTT line rather than control buffered character PTT hold +// #define OPTION_REVERSE_BUTTON_ORDER // This is mainly for the DJ0MY NanoKeyer http://nanokeyer.wordpress.com/ +#define OPTION_PROG_MEM_TRIM_TRAILING_SPACES // trim trailing spaces from memory when programming in command mode +#define OPTION_DIT_PADDLE_NO_SEND_ON_MEM_RPT // this makes dit paddle memory interruption a little smoother +// #define OPTION_MORE_DISPLAY_MSGS // additional optional display messages - comment out to save memory +// #define OPTION_WATCHDOG_TIMER // this enables a four second ATmega48/88/168/328 watchdog timer; use for unattended/remote operation only +// #define OPTION_MOUSE_MOVEMENT_PADDLE // experimental (just fooling around) - mouse movement will act like a paddle +// #define OPTION_NON_ENGLISH_EXTENSIONS // add support for additional CW characters (i.e. À, Å, Þ, etc.) +// #define OPTION_DISPLAY_NON_ENGLISH_EXTENSIONS // LCD display suport for non-English (NO/DK/DE) characters - Courtesy of OZ1JHM +// #define OPTION_UNKNOWN_CHARACTER_ERROR_TONE +#define OPTION_DO_NOT_SAY_HI +// #define OPTION_BLINK_HI_ON_PTT +// #define OPTION_PS2_NON_ENGLISH_CHAR_LCD_DISPLAY_SUPPORT // makes some non-English characters from the PS2 keyboard display correctly in the LCD display (donated by Marcin sp5iou) +// #define OPTION_PS2_KEYBOARD_RESET // reset the PS2 keyboard upon startup with 0xFF (contributed by Bill, W9BEL) +// #define OPTION_SAVE_MEMORY_NANOKEYER +// #define OPTION_CW_KEYBOARD_CAPSLOCK_BEEP +// #define OPTION_CW_KEYBOARD_ITALIAN +// #define OPTION_CW_KEYBOARD_GERMAN +// #define OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR +// #define OPTION_INVERT_PADDLE_PIN_LOGIC +// #define OPTION_ADVANCED_SPEED_DISPLAY //enables "nerd" speed visualization on display: wpm, cpm (char per min), duration of dit and dah in milliseconds and ratio (contributed by Giorgio, IZ2XBZ) +// #define OPTION_PROSIGN_SUPPORT // additional prosign support for paddle and straight key echo on display, CLI, and in memory storage +// #define OPTION_RUSSIAN_LANGUAGE_SEND_CLI // Russian language CLI sending support (contributed by Павел Бирюков, UA1AQC) +#define OPTION_DO_NOT_SEND_UNKNOWN_CHAR_QUESTION +// #define OPTION_CMOS_SUPER_KEYER_IAMBIC_B_TIMING_ON_BY_DEFAULT +#define OPTION_SIDETONE_DIGITAL_OUTPUT_NO_SQUARE_WAVE + +// #define OPTION_DIRECT_PADDLE_PIN_READS_MEGA // only works with Mega and pins 2 and 5 - minor performance increase +// #define OPTION_DIRECT_PADDLE_PIN_READS_UNO // Unos or Nanos pins 2 and 5 - do not enable on a nanoKeyer, it uses different pins + +// #define OPTION_WORDSWORTH_CZECH +// #define OPTION_WORDSWORTH_DEUTSCH +// #define OPTION_WORDSWORTH_NORSK + +#define OPTION_EXCLUDE_EXTENDED_CLI_COMMANDS + +// #define OPTION_DFROBOT_LCD_COMMAND_BUTTONS + +// #define OPTION_EXCLUDE_MILL_MODE +// #define OPTION_NO_ULTIMATIC // reduce memory usage by removing ultimatic code. + + +// #define OPTION_DISABLE_SERIAL_PORT_CHECKING_WHILE_SENDING_CW +// #define OPTION_PERSONALIZED_STARTUP_SCREEN // displays a user defined string of characters on the second or fourth row of the screen during startup. 1602 display requires OPTION_DO_NOT_SAY_HI +// #define OPTION_SWAP_PADDLE_PARAMETER_CHANGE_DIRECTION // reverses the up/down direction when using the paddles to change the wpm or sidetone frequency +// #define OPTION_DISPLAY_MEMORY_CONTENTS_COMMAND_MODE + +// #define OPTION_BEACON_MODE_MEMORY_REPEAT_TIME // to space out the repeated playing of memory 1 when in beacon mode +// #define OPTION_BEACON_MODE_PTT_TAIL_TIME // adds the ptt tail time to each playing of memory 1 in beacon mode + +// #define OPTION_WINKEY_PROSIGN_COMPATIBILITY // Additional character mappings to support K1EL Winkey emulation prosigns \ No newline at end of file diff --git a/k3ng_keyer/keyer_hardware.h b/k3ng_keyer/keyer_hardware.h new file mode 100755 index 0000000..37681c6 --- /dev/null +++ b/k3ng_keyer/keyer_hardware.h @@ -0,0 +1,97 @@ +/* keyer_hardware.h + + + Edit this file to enable specific hardware profiles. + + When enabling a hardware option below, the default keyer_pin_settings.h, keyer_features_and_options.h, and keyer_settings.h files are not compiled. + + See the comments on each line to determine what files are compiled and should be customized. + +*/ + +// #define HARDWARE_NANOKEYER_REV_B // https://nanokeyer.wordpress.com/nanokeyer-info/ edit these files: keyer_pin_settings_nanokeyer_rev_b.h, keyer_features_and_options_nanokeyer_rev_b.h, keyer_settings_nanokeyer_rev_b.h +// #define HARDWARE_NANOKEYER_REV_D // https://nanokeyer.wordpress.com/nanokeyer-info/ edit these files: keyer_pin_settings_nanokeyer_rev_d.h, keyer_features_and_options_nanokeyer_rev_d.h, keyer_settings_nanokeyer_rev_d.h +// #define HARDWARE_OPEN_INTERFACE // http://remoteqth.com/open-interface.php edit these files: keyer_pin_settings_open_interface.h, keyer_features_and_options_open_interface.h, keyer_settings_open_interface.h +// #define HARDWARE_TINYKEYER // http://www.ok1rr.com/index.php/technical-topics/122-the-tinykeyer edit these files: keyer_pin_settings_tinykeyer.h, keyer_features_and_options_tinykeyer.h, keyer_settings_tinykeyer.h +// #define HARDWARE_FK_10 // Funtronics K3NG Keyer FK-10 - See notes below!!! http://www.elekitsorparts.com/product/funtronics-k3ng-keyer-fk-10-99-winkey-emulation/ files: keyer_pin_settings_fk_10.h, keyer_features_and_options_fk_10.h, keyer_settings_fk_10.h +// #define HARDWARE_FK_11 // Funtronics K3NG Keyer FK-11 - See notes below! https://www.elekitsorparts.com/?product=funtronics-k3ng-keyer-with-99-winkey-emulation files: keyer_pin_settings_fk_11.h, keyer_features_and_options_fk_11.h, keyer_settings_fk_11.h +// #define HARDWARE_MAPLE_MINI // edit these files: keyer_pin_settings_maple_mini.h, keyer_settings_maple_mini.h, keyer_features_and_options_maple_mini.h +// #define HARDWARE_GENERIC_STM32F103C // edit these files: keyer_pin_settings_generic_STM32F103C.h, keyer_settings_generic_STM32F103C.h, keyer_features_and_options_generic_STM32F103C.h //sp5iou 20180329 +// #define HARDWARE_MORTTY // edit these files: keyer_pin_settings_mortty.h, keyer_settings_mortty.h, keyer_features_and_options_mortty.h +// #define HARDWARE_MORTTY_REGULAR // edit these files: keyer_pin_settings_mortty_regular.h, keyer_settings_mortty_regular.h, keyer_features_and_options_mortty_regular.h +// #define HARDWARE_MORTTY_REGULAR_WITH_POTENTIOMETER // edit these files: keyer_pin_settings_mortty_regular_with_potentiometer.h, keyer_settings_mortty_regular_with_potentiometer.h, keyer_features_and_options_mortty_regular_with_potentiometer.h +// #define HARDWARE_MORTTY_SO2R // edit these files: keyer_pin_settings_mortty_so2r.h, keyer_settings_mortty_so2r.h, keyer_features_and_options_mortty_so2r.h +// #define HARDWARE_MORTTY_SO2R_WITH_POTENTIOMETER // edit these files: keyer_pin_settings_mortty_so2r_with_potentiometer.h, keyer_settings_mortty_so2r_with_potentiometer.h, keyer_features_and_options_mortty_so2r_with_potentiometer.h +// #define HARDWARE_YAACWK // http://i1cra.briata.org/yaacwk/ files: keyer_pin_settings_yaacwk.h, keyer_features_and_options_yaacwk.h, keyer_settings_yaacwk.h +// #define HARDWARE_K5BCQ // edit these files: keyer_pin_settings_k5bcq.h, keyer_features_and_options_k5bcq.h, keyer_settings_k5bcq.h +// #define HARDWARE_MEGAKEYER // https://github.com/w6ipa/megakeyer +// #define HARDWARE_OPENCWKEYER_MK2 // https://github.com/ok1cdj/OpenCWKeyerMK2 edit these files: keyer_features_and_options_opencwkeyer_mk2.h keyer_pin_settings_opencwkeyer_mk2.h keyer_settings_opencwkeyer_mk2.h +// #define HARDWARE_IZ3GME // https://github.com/iz3gme/k3ng_cw_keyer edit these files: keyer_features_and_options_iz3gme.h keyer_pin_settings_iz3gme.h keyer_settings.h +// #define HARDWARE_YCCC_SO2R_MINI // edit these files: keyer_pin_settings_yccc_so2r_mini.h, keyer_settings_yccc_so2r_mini.h, keyer_features_and_options_yccc_so2r_mini.h +// #define HARDWARE_TEST_EVERYTHING +// #define HARDWARE_TEST + + +/* + + + HARDWARE_GENERIC_STM32F103C (Contributed by sp5iou) + + How to deal with those boards with Arduino: https://www.techshopbd.com/uploads/product_document/STM32bluepillarduinoguide(1).pdf + + + Funtronics FK-10 Programming Notes (Contributed by Disneysw 2016-12-10) + + Programming the unit is accomplished by selecting "Mega2560" as the target processor and uploading to the rear USB port with the front + switch set to the Arduino position + + Note: in order to get the FK-10 USB Host port working correctly you will need to patch the file "UsbCore.h" in the USB_Host_Shield library. + At the time of writing it is line 41 that needs modified to change "P10" to "P53" i.e. from + + #else + typedef MAX3421e MAX3421E; // Official Arduinos (UNO, Duemilanove, Mega, 2560, Leonardo, Due etc.) or Teensy 2.0 and 3.0 + #endif + + to: + + #else + typedef MAX3421e MAX3421E; // Official Arduinos (UNO, Duemilanove, Mega, 2560, Leonardo, Due etc.) or Teensy 2.0 and 3.0 + #endif + + Funtronics FK-11 Programming Notes + + Programming the unit is accomplished by selecting "Arduino Mega 2560" as the target processor and uploading to the rear USB port with the + front switch set to the Arduino position. + + Note: in order to get the FK-11 display working correctly you will need to: + - Install FaBo_212_LCD_PCF8574 library + - Set the slave address to 0x27 in FaBo_212_LCD_PCF8574/src/FaBoLCD_PCF8574.h + #define PCF8574_SLAVE_ADDRESS 0x27 + + Note: in order to get the FK-11 USB Host port working correctly you will need to: + - Uncomment three lines in in file: k3ng_keyer.ino below 'note_usb_uncomment_lines'. + - Add line to file k3ng_cw_keyer/libraries/USB_Host_Shield/settings.h, below 'Manual board activation': + #define BOARD_MEGA_ADK +*/ + + + +// Serial port class definitions for various devices + +#if defined(ARDUINO_MAPLE_MINI)||defined(ARDUINO_GENERIC_STM32F103C) //sp5iou 20180329 + #define PRIMARY_SERIAL_CLS USBSerial + #define SECONDARY_SERIAL_CLS USBSerial +#elif defined(ARDUINO_AVR_PROMICRO) || defined(ARDUINO_AVR_LEONARDO) || defined(ARDUINO_AVR_MICRO) || defined(ARDUINO_AVR_YUN) || defined(ARDUINO_AVR_ESPLORA) || defined(ARDUINO_AVR_LILYPAD_USB) || defined(ARDUINO_AVR_ROBOT_CONTROL) || defined(ARDUINO_AVR_ROBOT_MOTOR) || defined(ARDUINO_AVR_LEONARDO_ETH) || defined(ARDUINO_SAMD_VARIANT_COMPLIANCE) + #define PRIMARY_SERIAL_CLS Serial_ + #define SECONDARY_SERIAL_CLS Serial_ +#elif defined(TEENSYDUINO) + #define PRIMARY_SERIAL_CLS usb_serial_class + #define SECONDARY_SERIAL_CLS usb_serial_class +#elif defined(_BOARD_PIC32_PINGUINO_) // || defined(_BOARD_PIC32_PINGUINO_OTG_) + #define PRIMARY_SERIAL_CLS USBSerial + #define SECONDARY_SERIAL_CLS HardwareSerial +#else + #define PRIMARY_SERIAL_CLS HardwareSerial + #define SECONDARY_SERIAL_CLS HardwareSerial +#endif + diff --git a/k3ng_keyer/keyer_pin_settings.h b/k3ng_keyer/keyer_pin_settings.h new file mode 100755 index 0000000..9a81180 --- /dev/null +++ b/k3ng_keyer/keyer_pin_settings.h @@ -0,0 +1,161 @@ +/* Pins - you must review these and configure ! */ +#ifndef keyer_pin_settings_h +#define keyer_pin_settings_h + +#define paddle_left 6 +#define paddle_right 5 +#define tx_key_line_1 12 // (high = key down/tx on) +#define tx_key_line_2 0 +#define tx_key_line_3 0 +#define tx_key_line_4 0 +#define tx_key_line_5 0 +#define tx_key_line_6 0 +#define sidetone_line 4 // connect a speaker for sidetone +#define potentiometer A0 // Speed potentiometer (0 to 5 V) Use pot from 1k to 10k +#define ptt_tx_1 11 // PTT ("push to talk") lines +#define ptt_tx_2 0 // Can be used for keying fox transmitter, T/R switch, or keying slow boatanchors +#define ptt_tx_3 0 // These are optional - set to 0 if unused +#define ptt_tx_4 0 +#define ptt_tx_5 0 +#define ptt_tx_6 0 +#define tx_key_dit 0 // if defined, goes active for dit (any transmitter) - customized with tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state +#define tx_key_dah 0 // if defined, goes active for dah (any transmitter) - customized with tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state + +#define potentiometer_enable_pin 0 // if defined, the potentiometer will be enabled only when this pin is held low; set to 0 to ignore this pin + +#ifdef FEATURE_BUTTONS + #define analog_buttons_pin A1 + #define command_mode_active_led 7 +#endif //FEATURE_BUTTONS + +/* +FEATURE_SIDETONE_SWITCH + Enabling this feature and an external toggle switch adds switch control for playing cw sidetone. + ST Switch status is displayed in the status command. This feature will override the software control of the sidetone (\o). + Arduino pin is assigned by SIDETONE_SWITCH +*/ + +#ifdef FEATURE_SIDETONE_SWITCH + #define SIDETONE_SWITCH 8 +#endif //FEATURE_SIDETONE_SWITCH + + +//lcd pins +#if defined(FEATURE_LCD_4BIT) || defined(FEATURE_LCD_8BIT) + #define lcd_rs A2 + #define lcd_enable 10 // pin 10 is used by Ethernet shield and will conflict with that + #define lcd_d4 6 + #define lcd_d5 7 + #define lcd_d6 8 + #define lcd_d7 9 +#endif //FEATURE_LCD_4BIT || defined(FEATURE_LCD_8BIT) + +#if defined(FEATURE_LCD_8BIT) // addition four data lines for 8 bit LCD control + #define lcd_d0 20 + #define lcd_d1 21 + #define lcd_d2 22 + #define lcd_d3 23 +#endif //FEATURE_LCD_4BIT || defined(FEATURE_LCD_8BIT) + +#ifdef FEATURE_LCD1602_N07DH + #define lcd_rs 8 + #define lcd_enable 9 + #define lcd_d4 4 + #define lcd_d5 5 + #define lcd_d6 6 + #define lcd_d7 7 +#endif //FEATURE_LCD1602_N07DH + +//ps2 keyboard pins +#ifdef FEATURE_PS2_KEYBOARD + #define ps2_keyboard_data 2 + #define ps2_keyboard_clock 3 // this must be on an interrupt capable pin! +#endif //FEATURE_PS2_KEYBOARD + +// rotary encoder pins and options - rotary encoder code from Jim Balls M0CKE +#ifdef FEATURE_ROTARY_ENCODER + #define OPTION_ENCODER_HALF_STEP_MODE // Half-step mode? + #define rotary_pin1 0 // CW Encoder Pin + #define rotary_pin2 0 // CCW Encoder Pin + #define OPTION_ENCODER_ENABLE_PULLUPS // define to enable weak pullups. +#endif //FEATURE_ROTARY_ENCODER + +#ifdef FEATURE_LED_RING + #define led_ring_sdi A10 //2 //Data + #define led_ring_clk A9 //3 //Clock + #define led_ring_le A8 //4 //Latch +#endif //FEATURE_LED_RING + +#define correct_answer_led 0 +#define wrong_answer_led 0 + +#ifdef FEATURE_PTT_INTERLOCK + #define ptt_interlock 0 // this pin disables PTT and TX KEY +#endif //FEATURE_PTT_INTERLOCK + +#ifdef FEATURE_STRAIGHT_KEY + #define pin_straight_key 52 +#endif //FEATURE_STRAIGHT_KEY + +// FEATURE_CW_DECODER & OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR +// See https://github.com/k3ng/k3ng_cw_keyer/wiki/385-Feature:-CW-Decoder for details +#define cw_decoder_pin 0 // This is for use with external decoding hardware +#define cw_decoder_audio_input_pin 0 // This is for audio detection decoding using OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR; this must be an analog pin! +#define cw_decoder_indicator 0 // Output - goes HIGH when cw tone is detected by OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR + +#if defined(FEATURE_COMPETITION_COMPRESSION_DETECTION) + #define compression_detection_pin 13 +#endif //FEATURE_COMPETITION_COMPRESSION_DETECTION + +#if defined(FEATURE_SLEEP) + #define keyer_awake 0 +#endif + +#if defined(FEATURE_LCD_BACKLIGHT_AUTO_DIM) + #define keyer_power_led 0 // must be a PWM-capable pin +#endif + +#if defined(FEATURE_CAPACITIVE_PADDLE_PINS) + #define capactive_paddle_pin_inhibit_pin 0 // if this pin is defined and is set high, the capacitive paddle pins will switch to normal (non-capacitive) sensing mode +#endif + +#ifdef FEATURE_4x4_KEYPAD + #define Row3 33 + #define Row2 32 + #define Row1 31 + #define Row0 30 + #define Col3 37 + #define Col2 36 + #define Col1 35 + #define Col0 34 +#endif + +#ifdef FEATURE_3x4_KEYPAD + #define Row3 33 + #define Row2 32 + #define Row1 31 + #define Row0 30 + #define Col2 36 + #define Col1 35 + #define Col0 34 +#endif + +#ifdef FEATURE_SEQUENCER + #define sequencer_1_pin 0 + #define sequencer_2_pin 0 + #define sequencer_3_pin 0 + #define sequencer_4_pin 0 + #define sequencer_5_pin 0 +#endif //FEATURE_SEQUENCER + +#define ptt_input_pin 0 + +#define tx_inhibit_pin 0 +#define tx_pause_pin 0 + +#else + + #error "Multiple pin_settings.h files included somehow..." + +#endif //keyer_pin_settings_h + diff --git a/k3ng_keyer/keyer_pin_settings_fk_10.h b/k3ng_keyer/keyer_pin_settings_fk_10.h new file mode 100755 index 0000000..689556d --- /dev/null +++ b/k3ng_keyer/keyer_pin_settings_fk_10.h @@ -0,0 +1,142 @@ +/* Pins - you must review these and configure ! */ + +// ######## ## ## ## ## ## ## ######## ## ## ######## ######## +// ## ## ## ### ## ## ## ## ## ## ## ## ## +// ## ## ## #### ## ## ## ## #### ## ## ## +// ###### ## ## ## ## ## ##### ###### ## ###### ######## +// ## ## ## ## #### ## ## ## ## ## ## ## +// ## ## ## ## ### ## ## ## ## ## ## ## +// ## ####### ## ## ## ## ######## ## ######## ## ## + + +// ######## ## ## ## ##### +// ## ## ## #### ## ## +// ## ## ## ## ## ## +// ###### ##### ## ## ## +// ## ## ## ## ## ## +// ## ## ## ## ## ## +// ## ## ## ###### ##### + +// Contributed by Disneysw + + + +#ifndef keyer_pin_settings_h +#define keyer_pin_settings_h + +#define paddle_left 2 +#define paddle_right 5 +#define tx_key_line_1 39 // (high = key down/tx on) +#define tx_key_line_2 0 +#define tx_key_line_3 0 +#define tx_key_line_4 0 +#define tx_key_line_5 0 +#define tx_key_line_6 0 +#define sidetone_line 3 // connect a speaker for sidetone +#define potentiometer A0 // Speed potentiometer (0 to 5 V) Use pot from 1k to 10k +#define ptt_tx_1 40 // PTT ("push to talk") lines +#define ptt_tx_2 0 // Can be used for keying fox transmitter, T/R switch, or keying slow boatanchors +#define ptt_tx_3 0 // These are optional - set to 0 if unused +#define ptt_tx_4 0 +#define ptt_tx_5 0 +#define ptt_tx_6 0 +#define tx_key_dit 0 // if defined, goes active for dit (any transmitter) - customized with tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state +#define tx_key_dah 0 // if defined, goes active for dah (any transmitter) - customized with tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state + +#define potentiometer_enable_pin 0 // if defined, the potentiometer will be enabled only when this pin is held low; set to 0 to ignore this pin + +#ifdef FEATURE_BUTTONS + #define analog_buttons_pin A2 + #define command_mode_active_led 0 +#endif //FEATURE_BUTTONS + + +//lcd pins +#ifdef FEATURE_LCD_4BIT + #define lcd_rs 49 + #define lcd_enable 48 + #define lcd_d4 47 + #define lcd_d5 46 + #define lcd_d6 45 + #define lcd_d7 44 +#endif //FEATURE_LCD_4BIT + + +//ps2 keyboard pins +#ifdef FEATURE_PS2_KEYBOARD + #define ps2_keyboard_data A3 + #define ps2_keyboard_clock 3 // this must be on an interrupt capable pin! +#endif //FEATURE_PS2_KEYBOARD + +// rotary encoder pins and options - rotary encoder code from Jim Balls M0CKE +#ifdef FEATURE_ROTARY_ENCODER + #define OPTION_ENCODER_HALF_STEP_MODE // Half-step mode? + #define rotary_pin1 42 // CW Encoder Pin + #define rotary_pin2 43 // CCW Encoder Pin + #define OPTION_ENCODER_ENABLE_PULLUPS // define to enable weak pullups. +#endif //FEATURE_ROTARY_ENCODER + +#define correct_answer_led 0 +#define wrong_answer_led 0 + +#ifdef FEATURE_PTT_INTERLOCK + #define ptt_interlock 0 // this pin disables PTT and TX KEY +#endif //FEATURE_PTT_INTERLOCK + +#ifdef FEATURE_STRAIGHT_KEY + #define pin_straight_key 52 +#endif //FEATURE_STRAIGHT_KEY + +// FEATURE_CW_DECODER & OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR +// See https://github.com/k3ng/k3ng_cw_keyer/wiki/385-Feature:-CW-Decoder for details +#define cw_decoder_pin A4 // This is for use with external decoding hardware +#define cw_decoder_audio_input_pin A4 // This is for audio detection decoding using OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR; this must be an analog pin! +#define cw_decoder_indicator 4 // Output - goes HIGH when cw tone is detected by OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR + +#if defined(FEATURE_COMPETITION_COMPRESSION_DETECTION) + #define compression_detection_pin 13 +#endif //FEATURE_COMPETITION_COMPRESSION_DETECTION + +#if defined(FEATURE_SLEEP) + #define keyer_awake 0 +#endif + +#if defined(FEATURE_LCD_BACKLIGHT_AUTO_DIM) + #define keyer_power_led 0 // must be a PWM-capable pin +#endif + +#if defined(FEATURE_CAPACITIVE_PADDLE_PINS) + #define capactive_paddle_pin_inhibit_pin 0 // if this pin is defined and is set high, the capacitive paddle pins will switch to normal (non-capacitive) sensing mode +#endif + +/* +FEATURE_SIDETONE_SWITCH + Enabling this feature and an external toggle switch adds switch control for playing cw sidetone. + ST Switch status is displayed in the status command. This feature will override the software control of the sidetone (\o). + Arduino pin is assigned by SIDETONE_SWITCH +*/ + +#ifdef FEATURE_SIDETONE_SWITCH + #define SIDETONE_SWITCH 8 +#endif //FEATURE_SIDETONE_SWITCH + + +#ifdef FEATURE_SEQUENCER + #define sequencer_1_pin 0 + #define sequencer_2_pin 0 + #define sequencer_3_pin 0 + #define sequencer_4_pin 0 + #define sequencer_5_pin 0 +#endif //FEATURE_SEQUENCER + +#define ptt_input_pin 0 + +#define tx_inhibit_pin 0 +#define tx_pause_pin 0 + +#else + + #error "Multiple pin_settings.h files included somehow..." + +#endif //keyer_pin_settings_h + diff --git a/k3ng_keyer/keyer_pin_settings_fk_11.h b/k3ng_keyer/keyer_pin_settings_fk_11.h new file mode 100755 index 0000000..52f6cc8 --- /dev/null +++ b/k3ng_keyer/keyer_pin_settings_fk_11.h @@ -0,0 +1,164 @@ +// Funtronics FK-11 + +/* Pins - you must review these and configure ! */ +#ifndef keyer_pin_settings_h +#define keyer_pin_settings_h + +#define paddle_left 2 +#define paddle_right 5 +#define tx_key_line_1 39 // (high = key down/tx on) +#define tx_key_line_2 0 +#define tx_key_line_3 0 +#define tx_key_line_4 0 +#define tx_key_line_5 0 +#define tx_key_line_6 0 +#define sidetone_line 3 // connect a speaker for sidetone +#define potentiometer A0 // Speed potentiometer (0 to 5 V) Use pot from 1k to 10k +#define ptt_tx_1 40 // PTT ("push to talk") lines +#define ptt_tx_2 0 // Can be used for keying fox transmitter, T/R switch, or keying slow boatanchors +#define ptt_tx_3 0 // These are optional - set to 0 if unused +#define ptt_tx_4 0 +#define ptt_tx_5 0 +#define ptt_tx_6 0 +#define tx_key_dit 0 // if defined, goes active for dit (any transmitter) - customized with tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state +#define tx_key_dah 0 // if defined, goes active for dah (any transmitter) - customized with tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state + +#define potentiometer_enable_pin 0 // if defined, the potentiometer will be enabled only when this pin is held low; set to 0 to ignore this pin + +#ifdef FEATURE_BUTTONS + #define analog_buttons_pin A2 + #define command_mode_active_led 0 +#endif //FEATURE_BUTTONS + +/* +FEATURE_SIDETONE_SWITCH + Enabling this feature and an external toggle switch adds switch control for playing cw sidetone. + ST Switch status is displayed in the status command. This feature will override the software control of the sidetone (\o). + Arduino pin is assigned by SIDETONE_SWITCH +*/ + +#ifdef FEATURE_SIDETONE_SWITCH + #define SIDETONE_SWITCH 8 +#endif //FEATURE_SIDETONE_SWITCH + + +//lcd pins +#if defined(FEATURE_LCD_4BIT) || defined(FEATURE_LCD_8BIT) + #define lcd_rs A2 + #define lcd_enable 10 // pin 10 is used by Ethernet shield and will conflict with that + #define lcd_d4 6 + #define lcd_d5 7 + #define lcd_d6 8 + #define lcd_d7 9 +#endif //FEATURE_LCD_4BIT || defined(FEATURE_LCD_8BIT) + +#if defined(FEATURE_LCD_8BIT) // addition four data lines for 8 bit LCD control + #define lcd_d0 20 + #define lcd_d1 21 + #define lcd_d2 22 + #define lcd_d3 23 +#endif //FEATURE_LCD_4BIT || defined(FEATURE_LCD_8BIT) + +#ifdef FEATURE_LCD1602_N07DH + #define lcd_rs 8 + #define lcd_enable 9 + #define lcd_d4 4 + #define lcd_d5 5 + #define lcd_d6 6 + #define lcd_d7 7 +#endif //FEATURE_LCD1602_N07DH + +//ps2 keyboard pins +#ifdef FEATURE_PS2_KEYBOARD + #define ps2_keyboard_data A3 + #define ps2_keyboard_clock 3 // this must be on an interrupt capable pin! +#endif //FEATURE_PS2_KEYBOARD + +// rotary encoder pins and options - rotary encoder code from Jim Balls M0CKE +#ifdef FEATURE_ROTARY_ENCODER + #define OPTION_ENCODER_HALF_STEP_MODE // Half-step mode? + #define rotary_pin1 43 // CW Encoder Pin + #define rotary_pin2 42 // CCW Encoder Pin + #define OPTION_ENCODER_ENABLE_PULLUPS // define to enable weak pullups. +#endif //FEATURE_ROTARY_ENCODER + +#ifdef FEATURE_LED_RING + #define led_ring_sdi A10 //2 //Data + #define led_ring_clk A9 //3 //Clock + #define led_ring_le A8 //4 //Latch +#endif //FEATURE_LED_RING + +#define correct_answer_led 0 +#define wrong_answer_led 0 + +#ifdef FEATURE_PTT_INTERLOCK + #define ptt_interlock 0 // this pin disables PTT and TX KEY +#endif //FEATURE_PTT_INTERLOCK + +#ifdef FEATURE_STRAIGHT_KEY + #define pin_straight_key 52 +#endif //FEATURE_STRAIGHT_KEY + +// FEATURE_CW_DECODER & OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR +// See https://github.com/k3ng/k3ng_cw_keyer/wiki/385-Feature:-CW-Decoder for details +#define cw_decoder_pin A4 // This is for use with external decoding hardware +#define cw_decoder_audio_input_pin A4 // This is for audio detection decoding using OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR; this must be an analog pin! +#define cw_decoder_indicator 4 // Output - goes HIGH when cw tone is detected by OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR + + +#if defined(FEATURE_COMPETITION_COMPRESSION_DETECTION) + #define compression_detection_pin 13 +#endif //FEATURE_COMPETITION_COMPRESSION_DETECTION + +#if defined(FEATURE_SLEEP) + #define keyer_awake 0 +#endif + +#if defined(FEATURE_LCD_BACKLIGHT_AUTO_DIM) + #define keyer_power_led 0 // must be a PWM-capable pin +#endif + +#if defined(FEATURE_CAPACITIVE_PADDLE_PINS) + #define capactive_paddle_pin_inhibit_pin 0 // if this pin is defined and is set high, the capacitive paddle pins will switch to normal (non-capacitive) sensing mode +#endif + +#ifdef FEATURE_4x4_KEYPAD + #define Row3 33 + #define Row2 32 + #define Row1 31 + #define Row0 30 + #define Col3 37 + #define Col2 36 + #define Col1 35 + #define Col0 34 +#endif + +#ifdef FEATURE_3x4_KEYPAD + #define Row3 33 + #define Row2 32 + #define Row1 31 + #define Row0 30 + #define Col2 36 + #define Col1 35 + #define Col0 34 +#endif + +#ifdef FEATURE_SEQUENCER + #define sequencer_1_pin 0 + #define sequencer_2_pin 0 + #define sequencer_3_pin 0 + #define sequencer_4_pin 0 + #define sequencer_5_pin 0 +#endif //FEATURE_SEQUENCER + +#define ptt_input_pin 0 + +#define tx_inhibit_pin 0 +#define tx_pause_pin 0 + +#else + + #error "Multiple pin_settings.h files included somehow..." + +#endif //keyer_pin_settings_h + diff --git a/k3ng_keyer/keyer_pin_settings_generic_STM32F103C.h b/k3ng_keyer/keyer_pin_settings_generic_STM32F103C.h new file mode 100755 index 0000000..e66380e --- /dev/null +++ b/k3ng_keyer/keyer_pin_settings_generic_STM32F103C.h @@ -0,0 +1,169 @@ +/* + +GENERIC STM32F103C Blue Pill board + +*/ + +/* Pins - you must review these and configure ! */ +#ifndef keyer_pin_settings_h +#define keyer_pin_settings_h + +#define paddle_left PA1 +#define paddle_right PA0 +#define tx_key_line_1 PC15 // (high = key down/tx on) +#define tx_key_line_2 0 +#define tx_key_line_3 0 +#define tx_key_line_4 0 +#define tx_key_line_5 0 +#define tx_key_line_6 0 +#define sidetone_line PB9 // connect a speaker for sidetone +#define potentiometer 0 // Speed potentiometer (0 to 5 V) Use pot from 1k to 10k +#define ptt_tx_1 PC14 // PTT ("push to talk") lines +#define ptt_tx_2 0 // Can be used for keying fox transmitter, T/R switch, or keying slow boatanchors +#define ptt_tx_3 0 // These are optional - set to 0 if unused +#define ptt_tx_4 0 +#define ptt_tx_5 0 +#define ptt_tx_6 0 +#define tx_key_dit 0 // if defined, goes active for dit (any transmitter) - customized with tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state +#define tx_key_dah 0 // if defined, goes active for dah (any transmitter) - customized with tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state + +#define potentiometer_enable_pin 0 // if defined, the potentiometer will be enabled only when this pin is held low; set to 0 to ignore this pin + +#ifdef FEATURE_BUTTONS + #define analog_buttons_pin PA3 + #define command_mode_active_led PC13 //for Generic STM32103C On Board LED +#endif //FEATURE_BUTTONS + +/* +FEATURE_SIDETONE_SWITCH + Enabling this feature and an external toggle switch adds switch control for playing cw sidetone. + ST Switch status is displayed in the status command. This feature will override the software control of the sidetone (\o). + Arduino pin is assigned by SIDETONE_SWITCH +*/ + +#ifdef FEATURE_SIDETONE_SWITCH + #define SIDETONE_SWITCH 0 //PA15 +#endif //FEATURE_SIDETONE_SWITCH + + +//lcd pins +#ifdef FEATURE_LCD_4BIT + #define lcd_rs PB12 + #define lcd_enable PB13 + #define lcd_d4 PB14 + #define lcd_d5 PB15 + #define lcd_d6 PA8 + #define lcd_d7 PA9 +#endif //FEATURE_LCD_4BIT + +#if defined(FEATURE_LCD_8BIT) // addition four data lines for 8 bit LCD control + #define lcd_d0 20 + #define lcd_d1 21 + #define lcd_d2 22 + #define lcd_d3 23 +#endif //FEATURE_LCD_4BIT || defined(FEATURE_LCD_8BIT) + + +#ifdef FEATURE_LCD1602_N07DH + #define lcd_rs PB12 + #define lcd_enable PB13 + #define lcd_d4 PB14 + #define lcd_d5 PB15 + #define lcd_d6 PA8 + #define lcd_d7 PA9 +#endif //FEATURE_LCD1602_N07DH + +//ps2 keyboard pins +#ifdef FEATURE_PS2_KEYBOARD + #define ps2_keyboard_data 0 // for STM boards it can not be Ax - muste use pin numbers only or PBx, PCx etc... sp5iou + #define ps2_keyboard_clock 0 // this must be on an interrupt capable pin! +#endif //FEATURE_PS2_KEYBOARD + +// rotary encoder pins and options - rotary encoder code from Jim Balls M0CKE +#ifdef FEATURE_ROTARY_ENCODER + #define OPTION_ENCODER_HALF_STEP_MODE // Half-step mode? + #define rotary_pin1 PB7 // CW Encoder Pin + #define rotary_pin2 PB8 // CCW Encoder Pin + #define OPTION_ENCODER_ENABLE_PULLUPS // define to enable weak pullups. +#endif //FEATURE_ROTARY_ENCODER + +#ifdef FEATURE_LED_RING + #define led_ring_sdi 0 //Data + #define led_ring_clk 0 //Clock + #define led_ring_le 0 //Latch +#endif //FEATURE_LED_RING + +#define correct_answer_led 0 +#define wrong_answer_led 0 + +#ifdef FEATURE_PTT_INTERLOCK + #define ptt_interlock 0 // this pin disables PTT and TX KEY +#endif //FEATURE_PTT_INTERLOCK + +#ifdef FEATURE_STRAIGHT_KEY + #define pin_straight_key 0 +#endif //FEATURE_STRAIGHT_KEY + +// FEATURE_CW_DECODER & OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR +// See https://github.com/k3ng/k3ng_cw_keyer/wiki/385-Feature:-CW-Decoder for details +#define cw_decoder_pin 0 // This is for use with external decoding hardware +#define cw_decoder_audio_input_pin 0 // This is for audio detection decoding using OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR; this must be an analog pin! +#define cw_decoder_indicator 0 // Output - goes HIGH when cw tone is detected by OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR + + +#if defined(FEATURE_COMPETITION_COMPRESSION_DETECTION) + #define compression_detection_pin 0 +#endif //FEATURE_COMPETITION_COMPRESSION_DETECTION + +#if defined(FEATURE_SLEEP) + #define keyer_awake 0 +#endif + +#if defined(FEATURE_LCD_BACKLIGHT_AUTO_DIM) + #define keyer_power_led 0 // must be a PWM-capable pin +#endif + +#if defined(FEATURE_CAPACITIVE_PADDLE_PINS) + #define capactive_paddle_pin_inhibit_pin 0 // if this pin is defined and is set high, the capacitive paddle pins will switch to normal (non-capacitive) sensing mode +#endif + +#ifdef FEATURE_4x4_KEYPAD + #define Row3 PB11 + #define Row2 PB10 + #define Row1 PB1 + #define Row0 PB0 + #define Col3 PA7 + #define Col2 PA6 + #define Col1 PA5 + #define Col0 PA4 +#endif + +#ifdef FEATURE_3x4_KEYPAD + #define Row3 PB11 + #define Row2 PB10 + #define Row1 PB1 + #define Row0 PB0 + #define Col2 PA7 + #define Col1 PA6 + #define Col0 PA5 +#endif + +#ifdef FEATURE_SEQUENCER + #define sequencer_1_pin 0 + #define sequencer_2_pin 0 + #define sequencer_3_pin 0 + #define sequencer_4_pin 0 + #define sequencer_5_pin 0 +#endif //FEATURE_SEQUENCER + +#define ptt_input_pin 0 + +#define tx_inhibit_pin 0 +#define tx_pause_pin 0 + +#else + + #error "Multiple pin_settings.h files included somehow..." + +#endif //keyer_pin_settings_h + diff --git a/k3ng_keyer/keyer_pin_settings_iz3gme.h b/k3ng_keyer/keyer_pin_settings_iz3gme.h new file mode 100755 index 0000000..0617e67 --- /dev/null +++ b/k3ng_keyer/keyer_pin_settings_iz3gme.h @@ -0,0 +1,161 @@ +/* Pins - you must review these and configure ! */ +#ifndef keyer_pin_settings_h +#define keyer_pin_settings_h + +#define paddle_left A3 +#define paddle_right A4 +#define tx_key_line_1 A2 // (high = key down/tx on) +#define tx_key_line_2 0 +#define tx_key_line_3 0 +#define tx_key_line_4 0 +#define tx_key_line_5 0 +#define tx_key_line_6 0 +#define sidetone_line 0 // connect a speaker for sidetone +#define potentiometer A5 // Speed potentiometer (0 to 5 V) Use pot from 1k to 10k +#define ptt_tx_1 0 // PTT ("push to talk") lines +#define ptt_tx_2 0 // Can be used for keying fox transmitter, T/R switch, or keying slow boatanchors +#define ptt_tx_3 0 // These are optional - set to 0 if unused +#define ptt_tx_4 0 +#define ptt_tx_5 0 +#define ptt_tx_6 0 +#define tx_key_dit 0 // if defined, goes active for dit (any transmitter) - customized with tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state +#define tx_key_dah 0 // if defined, goes active for dah (any transmitter) - customized with tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state + +#define potentiometer_enable_pin 0 // if defined, the potentiometer will be enabled only when this pin is held low; set to 0 to ignore this pin + +#ifdef FEATURE_BUTTONS + #define analog_buttons_pin A6 + #define command_mode_active_led 13 +#endif //FEATURE_BUTTONS + +/* +FEATURE_SIDETONE_SWITCH + Enabling this feature and an external toggle switch adds switch control for playing cw sidetone. + ST Switch status is displayed in the status command. This feature will override the software control of the sidetone (\o). + Arduino pin is assigned by SIDETONE_SWITCH +*/ + +#ifdef FEATURE_SIDETONE_SWITCH + #define SIDETONE_SWITCH 8 +#endif //FEATURE_SIDETONE_SWITCH + + +//lcd pins +#if defined(FEATURE_LCD_4BIT) || defined(FEATURE_LCD_8BIT) + #define lcd_rs 7 + #define lcd_enable 6 // pin 10 is used by Ethernet shield and will conflict with that + #define lcd_d4 5 + #define lcd_d5 4 + #define lcd_d6 3 + #define lcd_d7 1 +#endif //FEATURE_LCD_4BIT || defined(FEATURE_LCD_8BIT) + +#if defined(FEATURE_LCD_8BIT) // addition four data lines for 8 bit LCD control + #define lcd_d0 20 + #define lcd_d1 21 + #define lcd_d2 22 + #define lcd_d3 23 +#endif //FEATURE_LCD_4BIT || defined(FEATURE_LCD_8BIT) + +#ifdef FEATURE_LCD1602_N07DH + #define lcd_rs 8 + #define lcd_enable 9 + #define lcd_d4 4 + #define lcd_d5 5 + #define lcd_d6 6 + #define lcd_d7 7 +#endif //FEATURE_LCD1602_N07DH + +//ps2 keyboard pins +#ifdef FEATURE_PS2_KEYBOARD + #define ps2_keyboard_data A3 + #define ps2_keyboard_clock 3 // this must be on an interrupt capable pin! +#endif //FEATURE_PS2_KEYBOARD + +// rotary encoder pins and options - rotary encoder code from Jim Balls M0CKE +#ifdef FEATURE_ROTARY_ENCODER + #define OPTION_ENCODER_HALF_STEP_MODE // Half-step mode? + #define rotary_pin1 0 // CW Encoder Pin + #define rotary_pin2 0 // CCW Encoder Pin + #define OPTION_ENCODER_ENABLE_PULLUPS // define to enable weak pullups. +#endif //FEATURE_ROTARY_ENCODER + +#ifdef FEATURE_LED_RING + #define led_ring_sdi A10 //2 //Data + #define led_ring_clk A9 //3 //Clock + #define led_ring_le A8 //4 //Latch +#endif //FEATURE_LED_RING + +#define correct_answer_led 0 +#define wrong_answer_led 0 + +#ifdef FEATURE_PTT_INTERLOCK + #define ptt_interlock 0 // this pin disables PTT and TX KEY +#endif //FEATURE_PTT_INTERLOCK + +#ifdef FEATURE_STRAIGHT_KEY + #define pin_straight_key 52 +#endif //FEATURE_STRAIGHT_KEY + +// FEATURE_CW_DECODER & OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR +// See https://github.com/k3ng/k3ng_cw_keyer/wiki/385-Feature:-CW-Decoder for details +#define cw_decoder_pin 0 // This is for use with external decoding hardware +#define cw_decoder_audio_input_pin 0 // This is for audio detection decoding using OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR; this must be an analog pin! +#define cw_decoder_indicator 0 // Output - goes HIGH when cw tone is detected by OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR + + +#if defined(FEATURE_COMPETITION_COMPRESSION_DETECTION) + #define compression_detection_pin 13 +#endif //FEATURE_COMPETITION_COMPRESSION_DETECTION + +#if defined(FEATURE_SLEEP) + #define keyer_awake 0 +#endif + +#if defined(FEATURE_LCD_BACKLIGHT_AUTO_DIM) + #define keyer_power_led 0 // must be a PWM-capable pin +#endif + +#if defined(FEATURE_CAPACITIVE_PADDLE_PINS) + #define capactive_paddle_pin_inhibit_pin 0 // if this pin is defined and is set high, the capacitive paddle pins will switch to normal (non-capacitive) sensing mode +#endif + +#ifdef FEATURE_4x4_KEYPAD + #define Row3 33 + #define Row2 32 + #define Row1 31 + #define Row0 30 + #define Col3 37 + #define Col2 36 + #define Col1 35 + #define Col0 34 +#endif + +#ifdef FEATURE_3x4_KEYPAD + #define Row3 33 + #define Row2 32 + #define Row1 31 + #define Row0 30 + #define Col2 36 + #define Col1 35 + #define Col0 34 +#endif + +#ifdef FEATURE_SEQUENCER + #define sequencer_1_pin 0 + #define sequencer_2_pin 0 + #define sequencer_3_pin 0 + #define sequencer_4_pin 0 + #define sequencer_5_pin 0 +#endif //FEATURE_SEQUENCER + +#define ptt_input_pin 0 + +#define tx_inhibit_pin 0 +#define tx_pause_pin 0 + +#else + + #error "Multiple pin_settings.h files included somehow..." + +#endif //keyer_pin_settings_h diff --git a/k3ng_keyer/keyer_pin_settings_k5bcq.h b/k3ng_keyer/keyer_pin_settings_k5bcq.h new file mode 100755 index 0000000..c771b63 --- /dev/null +++ b/k3ng_keyer/keyer_pin_settings_k5bcq.h @@ -0,0 +1,161 @@ +/* Pins - you must review these and configure ! */ +#ifndef keyer_pin_settings_h +#define keyer_pin_settings_h + +#define paddle_left 5 +#define paddle_right 4 +#define tx_key_line_1 8 // (high = key down/tx on) +#define tx_key_line_2 9 +#define tx_key_line_3 0 +#define tx_key_line_4 0 +#define tx_key_line_5 0 +#define tx_key_line_6 0 +#define sidetone_line 31 // connect a speaker for sidetone Set to "12" if not using Twin T oscillator +#define potentiometer A0 // Speed potentiometer (0 to 5 V) Use pot from 1k to 10k +#define ptt_tx_1 10 // PTT ("push to talk") lines +#define ptt_tx_2 11 // Can be used for keying fox transmitter, T/R switch, or keying slow boatanchors +#define ptt_tx_3 0 // These are optional - set to 0 if unused +#define ptt_tx_4 0 +#define ptt_tx_5 0 +#define ptt_tx_6 0 +#define tx_key_dit 0 // if defined, goes active for dit (any transmitter) - customized with tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state +#define tx_key_dah 0 // if defined, goes active for dah (any transmitter) - customized with tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state + +#define potentiometer_enable_pin 0 // if defined, the potentiometer will be enabled only when this pin is held low; set to 0 to ignore this pin + +#ifdef FEATURE_BUTTONS + #define analog_buttons_pin A1 + #define command_mode_active_led 29 +#endif //FEATURE_BUTTONS + +/* +FEATURE_SIDETONE_SWITCH + Enabling this feature and an external toggle switch adds switch control for playing cw sidetone. + ST Switch status is displayed in the status command. This feature will override the software control of the sidetone (\o). + Arduino pin is assigned by SIDETONE_SWITCH +*/ + +#ifdef FEATURE_SIDETONE_SWITCH + #define SIDETONE_SWITCH 8 +#endif //FEATURE_SIDETONE_SWITCH + + +//lcd pins +#if defined(FEATURE_LCD_4BIT) || defined(FEATURE_LCD_8BIT) + #define lcd_rs 38 + #define lcd_enable 32 // pin 10 is used by Ethernet shield and will conflict with that + #define lcd_d4 33 + #define lcd_d5 35 + #define lcd_d6 37 + #define lcd_d7 39 +#endif //FEATURE_LCD_4BIT || defined(FEATURE_LCD_8BIT) + +#if defined(FEATURE_LCD_8BIT) // addition four data lines for 8 bit LCD control + #define lcd_d0 20 + #define lcd_d1 21 + #define lcd_d2 22 + #define lcd_d3 23 +#endif //FEATURE_LCD_4BIT || defined(FEATURE_LCD_8BIT) + +#ifdef FEATURE_LCD1602_N07DH + #define lcd_rs 8 + #define lcd_enable 9 + #define lcd_d4 4 + #define lcd_d5 5 + #define lcd_d6 6 + #define lcd_d7 7 +#endif //FEATURE_LCD1602_N07DH + +//ps2 keyboard pins +#ifdef FEATURE_PS2_KEYBOARD + #define ps2_keyboard_data A3 + #define ps2_keyboard_clock 3 // this must be on an interrupt capable pin! +#endif //FEATURE_PS2_KEYBOARD + +// rotary encoder pins and options - rotary encoder code from Jim Balls M0CKE +#ifdef FEATURE_ROTARY_ENCODER + #define OPTION_ENCODER_HALF_STEP_MODE // Half-step mode? + #define rotary_pin1 6 // CW Encoder Pin + #define rotary_pin2 7 // CCW Encoder Pin + #define OPTION_ENCODER_ENABLE_PULLUPS // define to enable weak pullups. +#endif //FEATURE_ROTARY_ENCODER + +#ifdef FEATURE_LED_RING + #define led_ring_sdi A10 //2 //Data + #define led_ring_clk A9 //3 //Clock + #define led_ring_le A8 //4 //Latch +#endif //FEATURE_LED_RING + +#define correct_answer_led 27 +#define wrong_answer_led 25 + +#ifdef FEATURE_PTT_INTERLOCK + #define ptt_interlock 0 // this pin disables PTT and TX KEY +#endif //FEATURE_PTT_INTERLOCK + +#ifdef FEATURE_STRAIGHT_KEY + #define pin_straight_key 52 +#endif //FEATURE_STRAIGHT_KEY + +// FEATURE_CW_DECODER & OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR +// See https://github.com/k3ng/k3ng_cw_keyer/wiki/385-Feature:-CW-Decoder for details +#define cw_decoder_pin A3 // This is for use with external decoding hardware +#define cw_decoder_audio_input_pin A0 // This is for audio detection decoding using OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR; this must be an analog pin! +#define cw_decoder_indicator 23 // Output - goes HIGH when cw tone is detected by OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR + + +#if defined(FEATURE_COMPETITION_COMPRESSION_DETECTION) + #define compression_detection_pin 13 +#endif //FEATURE_COMPETITION_COMPRESSION_DETECTION + +#if defined(FEATURE_SLEEP) + #define keyer_awake 0 +#endif + +#if defined(FEATURE_LCD_BACKLIGHT_AUTO_DIM) + #define keyer_power_led 0 // must be a PWM-capable pin +#endif + +#if defined(FEATURE_CAPACITIVE_PADDLE_PINS) + #define capactive_paddle_pin_inhibit_pin 0 // if this pin is defined and is set high, the capacitive paddle pins will switch to normal (non-capacitive) sensing mode +#endif + +#ifdef FEATURE_4x4_KEYPAD + #define Row3 43 + #define Row2 42 + #define Row1 41 + #define Row0 40 + #define Col3 51 + #define Col2 50 + #define Col1 49 + #define Col0 48 +#endif + +#ifdef FEATURE_3x4_KEYPAD + #define Row3 43 + #define Row2 42 + #define Row1 41 + #define Row0 40 + #define Col2 50 + #define Col1 49 + #define Col0 48 +#endif + +#ifdef FEATURE_SEQUENCER + #define sequencer_1_pin 0 + #define sequencer_2_pin 0 + #define sequencer_3_pin 0 + #define sequencer_4_pin 0 + #define sequencer_5_pin 0 +#endif //FEATURE_SEQUENCER + +#define ptt_input_pin 0 + +#define tx_inhibit_pin 0 +#define tx_pause_pin 0 + +#else + + #error "Multiple pin_settings.h files included somehow..." + +#endif //keyer_pin_settings_h diff --git a/k3ng_keyer/keyer_pin_settings_maple_mini.h b/k3ng_keyer/keyer_pin_settings_maple_mini.h new file mode 100755 index 0000000..8f016e5 --- /dev/null +++ b/k3ng_keyer/keyer_pin_settings_maple_mini.h @@ -0,0 +1,176 @@ +/* + + +# # ## ##### # ###### # # # # # # +## ## # # # # # # ## ## # ## # # +# ## # # # # # # ##### # ## # # # # # # +# # ###### ##### # # # # # # # # # +# # # # # # # # # # # ## # +# # # # # ###### ###### # # # # # # + + +*/ + +/* Pins - you must review these and configure ! */ +#ifndef keyer_pin_settings_h +#define keyer_pin_settings_h + +#define paddle_left 10 +#define paddle_right 11 +#define tx_key_line_1 12 // (high = key down/tx on) +#define tx_key_line_2 15 +#define tx_key_line_3 0 +#define tx_key_line_4 0 +#define tx_key_line_5 0 +#define tx_key_line_6 0 +//#define sidetone_line 8 // connect a speaker for sidetone +uint8_t sidetone_line = 8; // sp5iou must be variable declaration instead of #define... for STM32 boards. +#define potentiometer 4 // Speed potentiometer (0 to 5 V) Use pot from 1k to 10k +#define ptt_tx_1 13 // PTT ("push to talk") lines +#define ptt_tx_2 16 // Can be used for keying fox transmitter, T/R switch, or keying slow boatanchors +#define ptt_tx_3 0 // These are optional - set to 0 if unused +#define ptt_tx_4 0 +#define ptt_tx_5 0 +#define ptt_tx_6 0 +#define tx_key_dit 0 // if defined, goes active for dit (any transmitter) - customized with tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state +#define tx_key_dah 0 // if defined, goes active for dah (any transmitter) - customized with tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state + +#define potentiometer_enable_pin 0 // if defined, the potentiometer will be enabled only when this pin is held low; set to 0 to ignore this pin + +#ifdef FEATURE_BUTTONS + #define analog_buttons_pin 3 + #define command_mode_active_led PB1 +#endif //FEATURE_BUTTONS + +/* +FEATURE_SIDETONE_SWITCH + Enabling this feature and an external toggle switch adds switch control for playing cw sidetone. + ST Switch status is displayed in the status command. This feature will override the software control of the sidetone (\o). + Arduino pin is assigned by SIDETONE_SWITCH +*/ + +#ifdef FEATURE_SIDETONE_SWITCH + #define SIDETONE_SWITCH 0 +#endif //FEATURE_SIDETONE_SWITCH + + +//lcd pins +#ifdef FEATURE_LCD_4BIT + #define lcd_rs 17 + #define lcd_enable 18 + #define lcd_d4 19 + #define lcd_d5 20 + #define lcd_d6 21 + #define lcd_d7 22 +#endif //FEATURE_LCD_4BIT + +#if defined(FEATURE_LCD_8BIT) // addition four data lines for 8 bit LCD control + #define lcd_d0 23 + #define lcd_d1 24 + #define lcd_d2 25 + #define lcd_d3 26 +#endif //FEATURE_LCD_4BIT || defined(FEATURE_LCD_8BIT) + +#ifdef FEATURE_LCD1602_N07DH + #define lcd_rs 17 + #define lcd_enable 18 + #define lcd_d4 19 + #define lcd_d5 20 + #define lcd_d6 21 + #define lcd_d7 22 +#endif //FEATURE_LCD1602_N07DH + +//ps2 keyboard pins +#ifdef FEATURE_PS2_KEYBOARD + #define ps2_keyboard_data A3 // for maple it can not be Ax - muste use pin numbers only or PBx, PCx etc... sp5iou + #define ps2_keyboard_clock 3 // this must be on an interrupt capable pin! +#endif //FEATURE_PS2_KEYBOARD + +// rotary encoder pins and options - rotary encoder code from Jim Balls M0CKE +#ifdef FEATURE_ROTARY_ENCODER + #define OPTION_ENCODER_HALF_STEP_MODE // Half-step mode? + #define rotary_pin1 0 // CW Encoder Pin + #define rotary_pin2 0 // CCW Encoder Pin + #define OPTION_ENCODER_ENABLE_PULLUPS // define to enable weak pullups. +#endif //FEATURE_ROTARY_ENCODER + +#ifdef FEATURE_LED_RING + #define led_ring_sdi A10 //2 //Data + #define led_ring_clk A9 //3 //Clock + #define led_ring_le A8 //4 //Latch +#endif //FEATURE_LED_RING + +#define correct_answer_led 0 +#define wrong_answer_led 0 + +#ifdef FEATURE_PTT_INTERLOCK + #define ptt_interlock 0 // this pin disables PTT and TX KEY +#endif //FEATURE_PTT_INTERLOCK + +#ifdef FEATURE_STRAIGHT_KEY + #define pin_straight_key 52 +#endif //FEATURE_STRAIGHT_KEY + +// FEATURE_CW_DECODER & OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR +// See https://github.com/k3ng/k3ng_cw_keyer/wiki/385-Feature:-CW-Decoder for details +#define cw_decoder_pin 0 // This is for use with external decoding hardware +#define cw_decoder_audio_input_pin 0 // This is for audio detection decoding using OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR; this must be an analog pin! +#define cw_decoder_indicator 0 // Output - goes HIGH when cw tone is detected by OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR + + +#if defined(FEATURE_COMPETITION_COMPRESSION_DETECTION) + #define compression_detection_pin 13 +#endif //FEATURE_COMPETITION_COMPRESSION_DETECTION + +#if defined(FEATURE_SLEEP) + #define keyer_awake 6 +#endif + +#if defined(FEATURE_LCD_BACKLIGHT_AUTO_DIM) + #define keyer_power_led 0 // must be a PWM-capable pin +#endif + +#if defined(FEATURE_CAPACITIVE_PADDLE_PINS) + #define capactive_paddle_pin_inhibit_pin 0 // if this pin is defined and is set high, the capacitive paddle pins will switch to normal (non-capacitive) sensing mode +#endif + +#ifdef FEATURE_4x4_KEYPAD + #define Row3 33 + #define Row2 32 + #define Row1 31 + #define Row0 30 + #define Col3 37 + #define Col2 36 + #define Col1 35 + #define Col0 34 +#endif + +#ifdef FEATURE_3x4_KEYPAD + #define Row3 33 + #define Row2 32 + #define Row1 31 + #define Row0 30 + #define Col2 36 + #define Col1 35 + #define Col0 34 +#endif + +#ifdef FEATURE_SEQUENCER + #define sequencer_1_pin 0 + #define sequencer_2_pin 0 + #define sequencer_3_pin 0 + #define sequencer_4_pin 0 + #define sequencer_5_pin 0 +#endif //FEATURE_SEQUENCER + +#define ptt_input_pin 0 + +#define tx_inhibit_pin 0 +#define tx_pause_pin 0 + +#else + + #error "Multiple pin_settings.h files included somehow..." + +#endif //keyer_pin_settings_h + diff --git a/k3ng_keyer/keyer_pin_settings_megakeyer.h b/k3ng_keyer/keyer_pin_settings_megakeyer.h new file mode 100755 index 0000000..8a5a1eb --- /dev/null +++ b/k3ng_keyer/keyer_pin_settings_megakeyer.h @@ -0,0 +1,162 @@ +/* Pins - you must review these and configure ! */ +#ifndef keyer_pin_settings_h +#define keyer_pin_settings_h + +#define paddle_left 34 +#define paddle_right 32 +#define tx_key_line_1 46 // (high = key down/tx on) +#define tx_key_line_2 12 +#define tx_key_line_3 0 +#define tx_key_line_4 0 +#define tx_key_line_5 0 +#define tx_key_line_6 0 +#define sidetone_line 3 // connect a speaker for sidetone +#define potentiometer A0 // Speed potentiometer (0 to 5 V) Use pot from 1k to 10k +#define ptt_tx_1 40 // PTT ("push to talk") lines +#define ptt_tx_2 0 // Can be used for keying fox transmitter, T/R switch, or keying slow boatanchors +#define ptt_tx_3 0 // These are optional - set to 0 if unused +#define ptt_tx_4 0 +#define ptt_tx_5 0 +#define ptt_tx_6 0 +#define tx_key_dit 0 // if defined, goes active for dit (any transmitter) - customized with tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state +#define tx_key_dah 0 // if defined, goes active for dah (any transmitter) - customized with tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state + +#define potentiometer_enable_pin 0 // if defined, the potentiometer will be enabled only when this pin is held low; set to 0 to ignore this pin + +#ifdef FEATURE_BUTTONS + #define analog_buttons_pin A3 + #define command_mode_active_led 0 +#endif //FEATURE_BUTTONS + +/* +FEATURE_SIDETONE_SWITCH + Enabling this feature and an external toggle switch adds switch control for playing cw sidetone. + ST Switch status is displayed in the status command. This feature will override the software control of the sidetone (\o). + Arduino pin is assigned by SIDETONE_SWITCH +*/ + +#ifdef FEATURE_SIDETONE_SWITCH + #define SIDETONE_SWITCH 8 +#endif //FEATURE_SIDETONE_SWITCH + + +//lcd pins +#if defined(FEATURE_LCD_4BIT) || defined(FEATURE_LCD_8BIT) + #define lcd_rs A2 + #define lcd_enable 10 // pin 10 is used by Ethernet shield and will conflict with that + #define lcd_d4 6 + #define lcd_d5 7 + #define lcd_d6 8 + #define lcd_d7 9 +#endif //FEATURE_LCD_4BIT || defined(FEATURE_LCD_8BIT) + +#if defined(FEATURE_LCD_8BIT) // addition four data lines for 8 bit LCD control + #define lcd_d0 20 + #define lcd_d1 21 + #define lcd_d2 22 + #define lcd_d3 23 +#endif //FEATURE_LCD_4BIT || defined(FEATURE_LCD_8BIT) + +#ifdef FEATURE_LCD1602_N07DH + #define lcd_rs 8 + #define lcd_enable 9 + #define lcd_d4 4 + #define lcd_d5 5 + #define lcd_d6 6 + #define lcd_d7 7 +#endif //FEATURE_LCD1602_N07DH + +//ps2 keyboard pins +#ifdef FEATURE_PS2_KEYBOARD + #define ps2_keyboard_data 16 + #define ps2_keyboard_clock 18 // this must be on an interrupt capable pin! +#endif //FEATURE_PS2_KEYBOARD + +// rotary encoder pins and options - rotary encoder code from Jim Balls M0CKE +#ifdef FEATURE_ROTARY_ENCODER + #define OPTION_ENCODER_HALF_STEP_MODE // Half-step mode? + #define rotary_pin1 6 // CW Encoder Pin + #define rotary_pin2 8 // CCW Encoder Pin + // #define OPTION_ENCODER_ENABLE_PULLUPS // define to enable weak pullups. +#endif //FEATURE_ROTARY_ENCODER + +#ifdef FEATURE_LED_RING + #define led_ring_sdi A10 //2 //Data + #define led_ring_clk A9 //3 //Clock + #define led_ring_le A8 //4 //Latch +#endif //FEATURE_LED_RING + +#define correct_answer_led 0 +#define wrong_answer_led 0 + +#ifdef FEATURE_PTT_INTERLOCK + #define ptt_interlock 0 // this pin disables PTT and TX KEY +#endif //FEATURE_PTT_INTERLOCK + +#ifdef FEATURE_STRAIGHT_KEY + #define pin_straight_key 52 +#endif //FEATURE_STRAIGHT_KEY + +// FEATURE_CW_DECODER & OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR +// See https://github.com/k3ng/k3ng_cw_keyer/wiki/385-Feature:-CW-Decoder for details +#define cw_decoder_pin 0 // This is for use with external decoding hardware +#define cw_decoder_audio_input_pin 0 // This is for audio detection decoding using OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR; this must be an analog pin! +#define cw_decoder_indicator 0 // Output - goes HIGH when cw tone is detected by OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR + + +#if defined(FEATURE_COMPETITION_COMPRESSION_DETECTION) + #define compression_detection_pin 13 +#endif //FEATURE_COMPETITION_COMPRESSION_DETECTION + +#if defined(FEATURE_SLEEP) + #define keyer_awake 0 +#endif + +#if defined(FEATURE_LCD_BACKLIGHT_AUTO_DIM) + #define keyer_power_led 0 // must be a PWM-capable pin +#endif + +#if defined(FEATURE_CAPACITIVE_PADDLE_PINS) + #define capactive_paddle_pin_inhibit_pin 0 // if this pin is defined and is set high, the capacitive paddle pins will switch to normal (non-capacitive) sensing mode +#endif + +#ifdef FEATURE_4x4_KEYPAD + #define Row3 33 + #define Row2 32 + #define Row1 31 + #define Row0 30 + #define Col3 37 + #define Col2 36 + #define Col1 35 + #define Col0 34 +#endif + +#ifdef FEATURE_3x4_KEYPAD + #define Row3 33 + #define Row2 32 + #define Row1 31 + #define Row0 30 + #define Col2 36 + #define Col1 35 + #define Col0 34 +#endif + +#ifdef FEATURE_SEQUENCER + #define sequencer_1_pin 0 + #define sequencer_2_pin 0 + #define sequencer_3_pin 0 + #define sequencer_4_pin 0 + #define sequencer_5_pin 0 +#endif //FEATURE_SEQUENCER + +#define ptt_input_pin 0 + +#define tx_inhibit_pin 0 +#define tx_pause_pin 0 + +#else + + #error "Multiple pin_settings.h files included somehow..." + +#endif //keyer_pin_settings_h + diff --git a/k3ng_keyer/keyer_pin_settings_mortty.h b/k3ng_keyer/keyer_pin_settings_mortty.h new file mode 100755 index 0000000..b43397d --- /dev/null +++ b/k3ng_keyer/keyer_pin_settings_mortty.h @@ -0,0 +1,162 @@ +/* Pins - you must review these and configure ! */ +#ifndef keyer_pin_settings_h +#define keyer_pin_settings_h + +#define paddle_left 2 +#define paddle_right 5 +#define tx_key_line_1 11 // (high = key down/tx on) +#define tx_key_line_2 12 +#define tx_key_line_3 0 +#define tx_key_line_4 0 +#define tx_key_line_5 0 +#define tx_key_line_6 0 +#define sidetone_line 4 // connect a speaker for sidetone +#define potentiometer A0 // Speed potentiometer (0 to 5 V) Use pot from 1k to 10k +#define ptt_tx_1 13 // PTT ("push to talk") lines +#define ptt_tx_2 0 // Can be used for keying fox transmitter, T/R switch, or keying slow boatanchors +#define ptt_tx_3 0 // These are optional - set to 0 if unused +#define ptt_tx_4 0 +#define ptt_tx_5 0 +#define ptt_tx_6 0 +#define tx_key_dit 0 // if defined, goes active for dit (any transmitter) - customized with tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state +#define tx_key_dah 0 // if defined, goes active for dah (any transmitter) - customized with tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state + +#define potentiometer_enable_pin 0 // if defined, the potentiometer will be enabled only when this pin is held low; set to 0 to ignore this pin + +#ifdef FEATURE_BUTTONS + #define analog_buttons_pin A1 + #define command_mode_active_led 0 +#endif //FEATURE_BUTTONS + +/* +FEATURE_SIDETONE_SWITCH + Enabling this feature and an external toggle switch adds switch control for playing cw sidetone. + ST Switch status is displayed in the status command. This feature will override the software control of the sidetone (\o). + Arduino pin is assigned by SIDETONE_SWITCH +*/ + +#ifdef FEATURE_SIDETONE_SWITCH + #define SIDETONE_SWITCH 8 +#endif //FEATURE_SIDETONE_SWITCH + + +//lcd pins +#if defined(FEATURE_LCD_4BIT) || defined(FEATURE_LCD_8BIT) + #define lcd_rs A2 + #define lcd_enable 10 // pin 10 is used by Ethernet shield and will conflict with that + #define lcd_d4 6 + #define lcd_d5 7 + #define lcd_d6 8 + #define lcd_d7 9 +#endif //FEATURE_LCD_4BIT || defined(FEATURE_LCD_8BIT) + +#if defined(FEATURE_LCD_8BIT) // addition four data lines for 8 bit LCD control + #define lcd_d0 20 + #define lcd_d1 21 + #define lcd_d2 22 + #define lcd_d3 23 +#endif //FEATURE_LCD_4BIT || defined(FEATURE_LCD_8BIT) + +#ifdef FEATURE_LCD1602_N07DH + #define lcd_rs 8 + #define lcd_enable 9 + #define lcd_d4 4 + #define lcd_d5 5 + #define lcd_d6 6 + #define lcd_d7 7 +#endif //FEATURE_LCD1602_N07DH + +//ps2 keyboard pins +#ifdef FEATURE_PS2_KEYBOARD + #define ps2_keyboard_data A3 + #define ps2_keyboard_clock 3 // this must be on an interrupt capable pin! +#endif //FEATURE_PS2_KEYBOARD + +// rotary encoder pins and options - rotary encoder code from Jim Balls M0CKE +#ifdef FEATURE_ROTARY_ENCODER + #define OPTION_ENCODER_HALF_STEP_MODE // Half-step mode? + #define rotary_pin1 0 // CW Encoder Pin + #define rotary_pin2 0 // CCW Encoder Pin + #define OPTION_ENCODER_ENABLE_PULLUPS // define to enable weak pullups. +#endif //FEATURE_ROTARY_ENCODER + +#ifdef FEATURE_LED_RING + #define led_ring_sdi A10 //2 //Data + #define led_ring_clk A9 //3 //Clock + #define led_ring_le A8 //4 //Latch +#endif //FEATURE_LED_RING + +#define correct_answer_led 0 +#define wrong_answer_led 0 + +#ifdef FEATURE_PTT_INTERLOCK + #define ptt_interlock 0 // this pin disables PTT and TX KEY +#endif //FEATURE_PTT_INTERLOCK + +#ifdef FEATURE_STRAIGHT_KEY + #define pin_straight_key 52 +#endif //FEATURE_STRAIGHT_KEY + +// FEATURE_CW_DECODER & OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR +// See https://github.com/k3ng/k3ng_cw_keyer/wiki/385-Feature:-CW-Decoder for details +#define cw_decoder_pin 0 // This is for use with external decoding hardware +#define cw_decoder_audio_input_pin 0 // This is for audio detection decoding using OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR; this must be an analog pin! +#define cw_decoder_indicator 0 // Output - goes HIGH when cw tone is detected by OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR + + +#if defined(FEATURE_COMPETITION_COMPRESSION_DETECTION) + #define compression_detection_pin 13 +#endif //FEATURE_COMPETITION_COMPRESSION_DETECTION + +#if defined(FEATURE_SLEEP) + #define keyer_awake 0 +#endif + +#if defined(FEATURE_LCD_BACKLIGHT_AUTO_DIM) + #define keyer_power_led 0 // must be a PWM-capable pin +#endif + +#if defined(FEATURE_CAPACITIVE_PADDLE_PINS) + #define capactive_paddle_pin_inhibit_pin 0 // if this pin is defined and is set high, the capacitive paddle pins will switch to normal (non-capacitive) sensing mode +#endif + +#ifdef FEATURE_4x4_KEYPAD + #define Row3 33 + #define Row2 32 + #define Row1 31 + #define Row0 30 + #define Col3 37 + #define Col2 36 + #define Col1 35 + #define Col0 34 +#endif + +#ifdef FEATURE_3x4_KEYPAD + #define Row3 33 + #define Row2 32 + #define Row1 31 + #define Row0 30 + #define Col2 36 + #define Col1 35 + #define Col0 34 +#endif + +#ifdef FEATURE_SEQUENCER + #define sequencer_1_pin 40 + #define sequencer_2_pin 41 + #define sequencer_3_pin 42 + #define sequencer_4_pin 43 + #define sequencer_5_pin 44 +#endif //FEATURE_SEQUENCER + +#define ptt_input_pin 0 + +#define tx_inhibit_pin 0 +#define tx_pause_pin 0 + +#else + + #error "Multiple pin_settings.h files included somehow..." + +#endif //keyer_pin_settings_h + diff --git a/k3ng_keyer/keyer_pin_settings_mortty_regular.h b/k3ng_keyer/keyer_pin_settings_mortty_regular.h new file mode 100755 index 0000000..7bf0002 --- /dev/null +++ b/k3ng_keyer/keyer_pin_settings_mortty_regular.h @@ -0,0 +1,157 @@ +/* Pins - you must review these and configure ! */ +#ifndef keyer_pin_settings_h +#define keyer_pin_settings_h + +#define paddle_left 2 +#define paddle_right 5 +#define tx_key_line_1 11 // (high = key down/tx on) +#define tx_key_line_2 12 +#define tx_key_line_3 0 +#define tx_key_line_4 0 +#define tx_key_line_5 0 +#define tx_key_line_6 0 +#define sidetone_line 4 // connect a speaker for sidetone +#define potentiometer A0 // Speed potentiometer (0 to 5 V) Use pot from 1k to 10k +#define ptt_tx_1 13 // PTT ("push to talk") lines +#define ptt_tx_2 0 // Can be used for keying fox transmitter, T/R switch, or keying slow boatanchors +#define ptt_tx_3 0 // These are optional - set to 0 if unused +#define ptt_tx_4 0 +#define ptt_tx_5 0 +#define ptt_tx_6 0 +#define tx_key_dit 0 // if defined, goes active for dit (any transmitter) - customized with tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state +#define tx_key_dah 0 // if defined, goes active for dah (any transmitter) - customized with tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state +#define potentiometer_enable_pin 0 // if defined, the potentiometer will be enabled only when this pin is held low; set to 0 to ignore this pin + + +#ifdef FEATURE_BUTTONS + #define analog_buttons_pin A1 + #define command_mode_active_led 0 +#endif //FEATURE_BUTTONS + +/* +FEATURE_SIDETONE_SWITCH + Enabling this feature and an external toggle switch adds switch control for playing cw sidetone. + ST Switch status is displayed in the status command. This feature will override the software control of the sidetone (\o). + Arduino pin is assigned by SIDETONE_SWITCH +*/ + +#ifdef FEATURE_SIDETONE_SWITCH + #define SIDETONE_SWITCH 8 +#endif //FEATURE_SIDETONE_SWITCH + + +//lcd pins +#ifdef FEATURE_LCD_4BIT + #define lcd_rs A2 + #define lcd_enable 10 + #define lcd_d4 6 + #define lcd_d5 7 + #define lcd_d6 8 + #define lcd_d7 9 +#endif //FEATURE_LCD_4BIT + +#ifdef FEATURE_LCD1602_N07DH + #define lcd_rs 8 + #define lcd_enable 9 + #define lcd_d4 4 + #define lcd_d5 5 + #define lcd_d6 6 + #define lcd_d7 7 +#endif //FEATURE_LCD1602_N07DH + +//ps2 keyboard pins +#ifdef FEATURE_PS2_KEYBOARD + #define ps2_keyboard_data A3 + #define ps2_keyboard_clock 3 // this must be on an interrupt capable pin! +#endif //FEATURE_PS2_KEYBOARD + +// rotary encoder pins and options - rotary encoder code from Jim Balls M0CKE +#ifdef FEATURE_ROTARY_ENCODER + #define OPTION_ENCODER_HALF_STEP_MODE // Half-step mode? + #define rotary_pin1 0 // CW Encoder Pin + #define rotary_pin2 0 // CCW Encoder Pin + #define OPTION_ENCODER_ENABLE_PULLUPS // define to enable weak pullups. +#endif //FEATURE_ROTARY_ENCODER + +#ifdef FEATURE_LED_RING + #define led_ring_sdi A10 //2 //Data + #define led_ring_clk A9 //3 //Clock + #define led_ring_le A8 //4 //Latch +#endif //FEATURE_LED_RING + +#define correct_answer_led 0 +#define wrong_answer_led 0 + +#ifdef FEATURE_PTT_INTERLOCK + #define ptt_interlock 0 // this pin disables PTT and TX KEY +#endif //FEATURE_PTT_INTERLOCK + +#ifdef FEATURE_STRAIGHT_KEY + #define pin_straight_key 52 +#endif //FEATURE_STRAIGHT_KEY + +// FEATURE_CW_DECODER & OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR +// See https://github.com/k3ng/k3ng_cw_keyer/wiki/385-Feature:-CW-Decoder for details +#define cw_decoder_pin 0 // This is for use with external decoding hardware +#define cw_decoder_audio_input_pin 0 // This is for audio detection decoding using OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR; this must be an analog pin! +#define cw_decoder_indicator 0 // Output - goes HIGH when cw tone is detected by OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR + + +#if defined(FEATURE_COMPETITION_COMPRESSION_DETECTION) + #define compression_detection_pin 13 +#endif //FEATURE_COMPETITION_COMPRESSION_DETECTION + +#if defined(FEATURE_SLEEP) + #define keyer_awake 0 +#endif + +#if defined(FEATURE_LCD_BACKLIGHT_AUTO_DIM) + #define keyer_power_led 0 // must be a PWM-capable pin +#endif + +#if defined(FEATURE_CAPACITIVE_PADDLE_PINS) + #define capactive_paddle_pin_inhibit_pin 0 // if this pin is defined and is set high, the capacitive paddle pins will switch to normal (non-capacitive) sensing mode +#endif + +#ifdef FEATURE_4x4_KEYPAD + #define Row3 33 + #define Row2 32 + #define Row1 31 + #define Row0 30 + #define Col3 37 + #define Col2 36 + #define Col1 35 + #define Col0 34 +#endif + +#ifdef FEATURE_3x4_KEYPAD + #define Row3 33 + #define Row2 32 + #define Row1 31 + #define Row0 30 + #define Col2 36 + #define Col1 35 + #define Col0 34 +#endif + +#ifdef FEATURE_SEQUENCER + #define sequencer_1_pin 40 + #define sequencer_2_pin 41 + #define sequencer_3_pin 42 + #define sequencer_4_pin 43 + #define sequencer_5_pin 44 +#endif //FEATURE_SEQUENCER + +#define ptt_input_pin 0 + +#define tx_inhibit_pin 0 +#define tx_pause_pin 0 + +#else + + #error "Multiple pin_settings.h files included somehow..." + +#endif //keyer_pin_settings_h + + + diff --git a/k3ng_keyer/keyer_pin_settings_mortty_regular_with_potentiometer.h b/k3ng_keyer/keyer_pin_settings_mortty_regular_with_potentiometer.h new file mode 100755 index 0000000..2e94549 --- /dev/null +++ b/k3ng_keyer/keyer_pin_settings_mortty_regular_with_potentiometer.h @@ -0,0 +1,155 @@ +/* Pins - you must review these and configure ! */ +#ifndef keyer_pin_settings_h +#define keyer_pin_settings_h + +#define paddle_left 2 +#define paddle_right 5 +#define tx_key_line_1 11 // (high = key down/tx on) +#define tx_key_line_2 12 +#define tx_key_line_3 0 +#define tx_key_line_4 0 +#define tx_key_line_5 0 +#define tx_key_line_6 0 +#define sidetone_line 4 // connect a speaker for sidetone +#define potentiometer A0 // Speed potentiometer (0 to 5 V) Use pot from 1k to 10k +#define ptt_tx_1 13 // PTT ("push to talk") lines +#define ptt_tx_2 0 // Can be used for keying fox transmitter, T/R switch, or keying slow boatanchors +#define ptt_tx_3 0 // These are optional - set to 0 if unused +#define ptt_tx_4 0 +#define ptt_tx_5 0 +#define ptt_tx_6 0 +#define tx_key_dit 0 // if defined, goes active for dit (any transmitter) - customized with tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state +#define tx_key_dah 0 // if defined, goes active for dah (any transmitter) - customized with tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state +#define potentiometer_enable_pin 0 // if defined, the potentiometer will be enabled only when this pin is held low; set to 0 to ignore this pin + + +#ifdef FEATURE_BUTTONS + #define analog_buttons_pin A1 + #define command_mode_active_led 0 +#endif //FEATURE_BUTTONS + +/* +FEATURE_SIDETONE_SWITCH + Enabling this feature and an external toggle switch adds switch control for playing cw sidetone. + ST Switch status is displayed in the status command. This feature will override the software control of the sidetone (\o). + Arduino pin is assigned by SIDETONE_SWITCH +*/ + +#ifdef FEATURE_SIDETONE_SWITCH + #define SIDETONE_SWITCH 8 +#endif //FEATURE_SIDETONE_SWITCH + + +//lcd pins +#ifdef FEATURE_LCD_4BIT + #define lcd_rs A2 + #define lcd_enable 10 + #define lcd_d4 6 + #define lcd_d5 7 + #define lcd_d6 8 + #define lcd_d7 9 +#endif //FEATURE_LCD_4BIT + +#ifdef FEATURE_LCD1602_N07DH + #define lcd_rs 8 + #define lcd_enable 9 + #define lcd_d4 4 + #define lcd_d5 5 + #define lcd_d6 6 + #define lcd_d7 7 +#endif //FEATURE_LCD1602_N07DH + +//ps2 keyboard pins +#ifdef FEATURE_PS2_KEYBOARD + #define ps2_keyboard_data A3 + #define ps2_keyboard_clock 3 // this must be on an interrupt capable pin! +#endif //FEATURE_PS2_KEYBOARD + +// rotary encoder pins and options - rotary encoder code from Jim Balls M0CKE +#ifdef FEATURE_ROTARY_ENCODER + #define OPTION_ENCODER_HALF_STEP_MODE // Half-step mode? + #define rotary_pin1 0 // CW Encoder Pin + #define rotary_pin2 0 // CCW Encoder Pin + #define OPTION_ENCODER_ENABLE_PULLUPS // define to enable weak pullups. +#endif //FEATURE_ROTARY_ENCODER + +#ifdef FEATURE_LED_RING + #define led_ring_sdi A10 //2 //Data + #define led_ring_clk A9 //3 //Clock + #define led_ring_le A8 //4 //Latch +#endif //FEATURE_LED_RING + +#define correct_answer_led 0 +#define wrong_answer_led 0 + +#ifdef FEATURE_PTT_INTERLOCK + #define ptt_interlock 0 // this pin disables PTT and TX KEY +#endif //FEATURE_PTT_INTERLOCK + +#ifdef FEATURE_STRAIGHT_KEY + #define pin_straight_key 52 +#endif //FEATURE_STRAIGHT_KEY + +// FEATURE_CW_DECODER & OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR +// See https://github.com/k3ng/k3ng_cw_keyer/wiki/385-Feature:-CW-Decoder for details +#define cw_decoder_pin 0 // This is for use with external decoding hardware +#define cw_decoder_audio_input_pin 0 // This is for audio detection decoding using OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR; this must be an analog pin! +#define cw_decoder_indicator 0 // Output - goes HIGH when cw tone is detected by OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR + + +#if defined(FEATURE_COMPETITION_COMPRESSION_DETECTION) + #define compression_detection_pin 13 +#endif //FEATURE_COMPETITION_COMPRESSION_DETECTION + +#if defined(FEATURE_SLEEP) + #define keyer_awake 0 +#endif + +#if defined(FEATURE_LCD_BACKLIGHT_AUTO_DIM) + #define keyer_power_led 0 // must be a PWM-capable pin +#endif + +#if defined(FEATURE_CAPACITIVE_PADDLE_PINS) + #define capactive_paddle_pin_inhibit_pin 0 // if this pin is defined and is set high, the capacitive paddle pins will switch to normal (non-capacitive) sensing mode +#endif + +#ifdef FEATURE_4x4_KEYPAD + #define Row3 33 + #define Row2 32 + #define Row1 31 + #define Row0 30 + #define Col3 37 + #define Col2 36 + #define Col1 35 + #define Col0 34 +#endif + +#ifdef FEATURE_3x4_KEYPAD + #define Row3 33 + #define Row2 32 + #define Row1 31 + #define Row0 30 + #define Col2 36 + #define Col1 35 + #define Col0 34 +#endif + +#ifdef FEATURE_SEQUENCER + #define sequencer_1_pin 40 + #define sequencer_2_pin 41 + #define sequencer_3_pin 42 + #define sequencer_4_pin 43 + #define sequencer_5_pin 44 +#endif //FEATURE_SEQUENCER + +#define ptt_input_pin 0 + +#define tx_inhibit_pin 0 +#define tx_pause_pin 0 + +#else + + #error "Multiple pin_settings.h files included somehow..." + +#endif //keyer_pin_settings_h + diff --git a/k3ng_keyer/keyer_pin_settings_mortty_so2r.h b/k3ng_keyer/keyer_pin_settings_mortty_so2r.h new file mode 100755 index 0000000..f530440 --- /dev/null +++ b/k3ng_keyer/keyer_pin_settings_mortty_so2r.h @@ -0,0 +1,155 @@ +/* Pins - you must review these and configure ! */ +#ifndef keyer_pin_settings_h +#define keyer_pin_settings_h + +#define paddle_left 2 +#define paddle_right 5 +#define tx_key_line_1 11 // (high = key down/tx on) +#define tx_key_line_2 13 +#define tx_key_line_3 0 +#define tx_key_line_4 0 +#define tx_key_line_5 0 +#define tx_key_line_6 0 +#define sidetone_line 4 // connect a speaker for sidetone +#define potentiometer A0 // Speed potentiometer (0 to 5 V) Use pot from 1k to 10k +#define ptt_tx_1 0 // PTT ("push to talk") lines +#define ptt_tx_2 0 // Can be used for keying fox transmitter, T/R switch, or keying slow boatanchors +#define ptt_tx_3 0 // These are optional - set to 0 if unused +#define ptt_tx_4 0 +#define ptt_tx_5 0 +#define ptt_tx_6 0 +#define tx_key_dit 0 // if defined, goes active for dit (any transmitter) - customized with tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state +#define tx_key_dah 0 // if defined, goes active for dah (any transmitter) - customized with tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state +#define potentiometer_enable_pin 0 // if defined, the potentiometer will be enabled only when this pin is held low; set to 0 to ignore this pin + + +#ifdef FEATURE_BUTTONS + #define analog_buttons_pin A1 + #define command_mode_active_led 0 +#endif //FEATURE_BUTTONS + +/* +FEATURE_SIDETONE_SWITCH + Enabling this feature and an external toggle switch adds switch control for playing cw sidetone. + ST Switch status is displayed in the status command. This feature will override the software control of the sidetone (\o). + Arduino pin is assigned by SIDETONE_SWITCH +*/ + +#ifdef FEATURE_SIDETONE_SWITCH + #define SIDETONE_SWITCH 8 +#endif //FEATURE_SIDETONE_SWITCH + + +//lcd pins +#ifdef FEATURE_LCD_4BIT + #define lcd_rs A2 + #define lcd_enable 10 + #define lcd_d4 6 + #define lcd_d5 7 + #define lcd_d6 8 + #define lcd_d7 9 +#endif //FEATURE_LCD_4BIT + +#ifdef FEATURE_LCD1602_N07DH + #define lcd_rs 8 + #define lcd_enable 9 + #define lcd_d4 4 + #define lcd_d5 5 + #define lcd_d6 6 + #define lcd_d7 7 +#endif //FEATURE_LCD1602_N07DH + +//ps2 keyboard pins +#ifdef FEATURE_PS2_KEYBOARD + #define ps2_keyboard_data A3 + #define ps2_keyboard_clock 3 // this must be on an interrupt capable pin! +#endif //FEATURE_PS2_KEYBOARD + +// rotary encoder pins and options - rotary encoder code from Jim Balls M0CKE +#ifdef FEATURE_ROTARY_ENCODER + #define OPTION_ENCODER_HALF_STEP_MODE // Half-step mode? + #define rotary_pin1 0 // CW Encoder Pin + #define rotary_pin2 0 // CCW Encoder Pin + #define OPTION_ENCODER_ENABLE_PULLUPS // define to enable weak pullups. +#endif //FEATURE_ROTARY_ENCODER + +#ifdef FEATURE_LED_RING + #define led_ring_sdi A10 //2 //Data + #define led_ring_clk A9 //3 //Clock + #define led_ring_le A8 //4 //Latch +#endif //FEATURE_LED_RING + +#define correct_answer_led 0 +#define wrong_answer_led 0 + +#ifdef FEATURE_PTT_INTERLOCK + #define ptt_interlock 0 // this pin disables PTT and TX KEY +#endif //FEATURE_PTT_INTERLOCK + +#ifdef FEATURE_STRAIGHT_KEY + #define pin_straight_key 52 +#endif //FEATURE_STRAIGHT_KEY + +// FEATURE_CW_DECODER & OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR +// See https://github.com/k3ng/k3ng_cw_keyer/wiki/385-Feature:-CW-Decoder for details +#define cw_decoder_pin 0 // This is for use with external decoding hardware +#define cw_decoder_audio_input_pin 0 // This is for audio detection decoding using OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR; this must be an analog pin! +#define cw_decoder_indicator 0 // Output - goes HIGH when cw tone is detected by OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR + + +#if defined(FEATURE_COMPETITION_COMPRESSION_DETECTION) + #define compression_detection_pin 13 +#endif //FEATURE_COMPETITION_COMPRESSION_DETECTION + +#if defined(FEATURE_SLEEP) + #define keyer_awake 0 +#endif + +#if defined(FEATURE_LCD_BACKLIGHT_AUTO_DIM) + #define keyer_power_led 0 // must be a PWM-capable pin +#endif + +#if defined(FEATURE_CAPACITIVE_PADDLE_PINS) + #define capactive_paddle_pin_inhibit_pin 0 // if this pin is defined and is set high, the capacitive paddle pins will switch to normal (non-capacitive) sensing mode +#endif + +#ifdef FEATURE_4x4_KEYPAD + #define Row3 33 + #define Row2 32 + #define Row1 31 + #define Row0 30 + #define Col3 37 + #define Col2 36 + #define Col1 35 + #define Col0 34 +#endif + +#ifdef FEATURE_3x4_KEYPAD + #define Row3 33 + #define Row2 32 + #define Row1 31 + #define Row0 30 + #define Col2 36 + #define Col1 35 + #define Col0 34 +#endif + +#ifdef FEATURE_SEQUENCER + #define sequencer_1_pin 40 + #define sequencer_2_pin 41 + #define sequencer_3_pin 42 + #define sequencer_4_pin 43 + #define sequencer_5_pin 44 +#endif //FEATURE_SEQUENCER + +#define ptt_input_pin 0 + +#define tx_inhibit_pin 0 +#define tx_pause_pin 0 + +#else + + #error "Multiple pin_settings.h files included somehow..." + +#endif //keyer_pin_settings_h + diff --git a/k3ng_keyer/keyer_pin_settings_mortty_so2r_with_potentiometer.h b/k3ng_keyer/keyer_pin_settings_mortty_so2r_with_potentiometer.h new file mode 100755 index 0000000..f530440 --- /dev/null +++ b/k3ng_keyer/keyer_pin_settings_mortty_so2r_with_potentiometer.h @@ -0,0 +1,155 @@ +/* Pins - you must review these and configure ! */ +#ifndef keyer_pin_settings_h +#define keyer_pin_settings_h + +#define paddle_left 2 +#define paddle_right 5 +#define tx_key_line_1 11 // (high = key down/tx on) +#define tx_key_line_2 13 +#define tx_key_line_3 0 +#define tx_key_line_4 0 +#define tx_key_line_5 0 +#define tx_key_line_6 0 +#define sidetone_line 4 // connect a speaker for sidetone +#define potentiometer A0 // Speed potentiometer (0 to 5 V) Use pot from 1k to 10k +#define ptt_tx_1 0 // PTT ("push to talk") lines +#define ptt_tx_2 0 // Can be used for keying fox transmitter, T/R switch, or keying slow boatanchors +#define ptt_tx_3 0 // These are optional - set to 0 if unused +#define ptt_tx_4 0 +#define ptt_tx_5 0 +#define ptt_tx_6 0 +#define tx_key_dit 0 // if defined, goes active for dit (any transmitter) - customized with tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state +#define tx_key_dah 0 // if defined, goes active for dah (any transmitter) - customized with tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state +#define potentiometer_enable_pin 0 // if defined, the potentiometer will be enabled only when this pin is held low; set to 0 to ignore this pin + + +#ifdef FEATURE_BUTTONS + #define analog_buttons_pin A1 + #define command_mode_active_led 0 +#endif //FEATURE_BUTTONS + +/* +FEATURE_SIDETONE_SWITCH + Enabling this feature and an external toggle switch adds switch control for playing cw sidetone. + ST Switch status is displayed in the status command. This feature will override the software control of the sidetone (\o). + Arduino pin is assigned by SIDETONE_SWITCH +*/ + +#ifdef FEATURE_SIDETONE_SWITCH + #define SIDETONE_SWITCH 8 +#endif //FEATURE_SIDETONE_SWITCH + + +//lcd pins +#ifdef FEATURE_LCD_4BIT + #define lcd_rs A2 + #define lcd_enable 10 + #define lcd_d4 6 + #define lcd_d5 7 + #define lcd_d6 8 + #define lcd_d7 9 +#endif //FEATURE_LCD_4BIT + +#ifdef FEATURE_LCD1602_N07DH + #define lcd_rs 8 + #define lcd_enable 9 + #define lcd_d4 4 + #define lcd_d5 5 + #define lcd_d6 6 + #define lcd_d7 7 +#endif //FEATURE_LCD1602_N07DH + +//ps2 keyboard pins +#ifdef FEATURE_PS2_KEYBOARD + #define ps2_keyboard_data A3 + #define ps2_keyboard_clock 3 // this must be on an interrupt capable pin! +#endif //FEATURE_PS2_KEYBOARD + +// rotary encoder pins and options - rotary encoder code from Jim Balls M0CKE +#ifdef FEATURE_ROTARY_ENCODER + #define OPTION_ENCODER_HALF_STEP_MODE // Half-step mode? + #define rotary_pin1 0 // CW Encoder Pin + #define rotary_pin2 0 // CCW Encoder Pin + #define OPTION_ENCODER_ENABLE_PULLUPS // define to enable weak pullups. +#endif //FEATURE_ROTARY_ENCODER + +#ifdef FEATURE_LED_RING + #define led_ring_sdi A10 //2 //Data + #define led_ring_clk A9 //3 //Clock + #define led_ring_le A8 //4 //Latch +#endif //FEATURE_LED_RING + +#define correct_answer_led 0 +#define wrong_answer_led 0 + +#ifdef FEATURE_PTT_INTERLOCK + #define ptt_interlock 0 // this pin disables PTT and TX KEY +#endif //FEATURE_PTT_INTERLOCK + +#ifdef FEATURE_STRAIGHT_KEY + #define pin_straight_key 52 +#endif //FEATURE_STRAIGHT_KEY + +// FEATURE_CW_DECODER & OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR +// See https://github.com/k3ng/k3ng_cw_keyer/wiki/385-Feature:-CW-Decoder for details +#define cw_decoder_pin 0 // This is for use with external decoding hardware +#define cw_decoder_audio_input_pin 0 // This is for audio detection decoding using OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR; this must be an analog pin! +#define cw_decoder_indicator 0 // Output - goes HIGH when cw tone is detected by OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR + + +#if defined(FEATURE_COMPETITION_COMPRESSION_DETECTION) + #define compression_detection_pin 13 +#endif //FEATURE_COMPETITION_COMPRESSION_DETECTION + +#if defined(FEATURE_SLEEP) + #define keyer_awake 0 +#endif + +#if defined(FEATURE_LCD_BACKLIGHT_AUTO_DIM) + #define keyer_power_led 0 // must be a PWM-capable pin +#endif + +#if defined(FEATURE_CAPACITIVE_PADDLE_PINS) + #define capactive_paddle_pin_inhibit_pin 0 // if this pin is defined and is set high, the capacitive paddle pins will switch to normal (non-capacitive) sensing mode +#endif + +#ifdef FEATURE_4x4_KEYPAD + #define Row3 33 + #define Row2 32 + #define Row1 31 + #define Row0 30 + #define Col3 37 + #define Col2 36 + #define Col1 35 + #define Col0 34 +#endif + +#ifdef FEATURE_3x4_KEYPAD + #define Row3 33 + #define Row2 32 + #define Row1 31 + #define Row0 30 + #define Col2 36 + #define Col1 35 + #define Col0 34 +#endif + +#ifdef FEATURE_SEQUENCER + #define sequencer_1_pin 40 + #define sequencer_2_pin 41 + #define sequencer_3_pin 42 + #define sequencer_4_pin 43 + #define sequencer_5_pin 44 +#endif //FEATURE_SEQUENCER + +#define ptt_input_pin 0 + +#define tx_inhibit_pin 0 +#define tx_pause_pin 0 + +#else + + #error "Multiple pin_settings.h files included somehow..." + +#endif //keyer_pin_settings_h + diff --git a/k3ng_keyer/keyer_pin_settings_nanokeyer_rev_b.h b/k3ng_keyer/keyer_pin_settings_nanokeyer_rev_b.h new file mode 100755 index 0000000..c4ede07 --- /dev/null +++ b/k3ng_keyer/keyer_pin_settings_nanokeyer_rev_b.h @@ -0,0 +1,96 @@ +#ifndef keyer_pin_settings_h +#define keyer_pin_settings_h + +#define paddle_left 2 +#define paddle_right 5 +#define tx_key_line_1 11 // (high = key down/tx on) +#define tx_key_line_2 13 // Rev A & B nanoKeyer PTT port +#define tx_key_line_3 0 +#define tx_key_line_4 0 +#define tx_key_line_5 0 +#define tx_key_line_6 0 +#define sidetone_line 4 // connect a speaker for sidetone +#define potentiometer A7 // Rev A & B nanoKeyer +#define ptt_tx_1 0 // PTT ("push to talk") lines +//#define ptt_tx_1 13 // nanoKeyer PTT port +#define ptt_tx_2 0 // Can be used for keying fox transmitter, T/R switch, or keying slow boatanchors +#define ptt_tx_3 0 // These are optional - set to 0 if unused +#define ptt_tx_4 0 +#define ptt_tx_5 0 +#define ptt_tx_6 0 +#define analog_buttons_pin A6 // Rev A & B nanokeyer +#define lcd_rs A2 +#define lcd_enable 10 +#define lcd_d4 6 +#define lcd_d5 7 +#define lcd_d6 8 +#define lcd_d7 9 +#define ps2_keyboard_data A5 // Rev A & B nanokeyer +#define ps2_keyboard_clock 3 +#define tx_key_dit 0 // if defined, goes active for dit (any transmitter) - customized with tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state +#define tx_key_dah 0 // if defined, goes active for dah (any transmitter) - customized with tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state + +#define potentiometer_enable_pin 0 // if defined, the potentiometer will be enabled only when this pin is held low; set to 0 to ignore this pin + +#if defined(FEATURE_SLEEP) + #define keyer_awake 13 // Goes active when keyer is awake, inactive when in sleep mode; change active and inactive states in keyer_settings file +#endif + +#if defined(FEATURE_LCD_BACKLIGHT_AUTO_DIM) + #define keyer_power_led 0 // must be a PWM-capable pin +#endif + +#if defined(FEATURE_CAPACITIVE_PADDLE_PINS) + #define capactive_paddle_pin_inhibit_pin 0 // if this pin is defined and is set high, the capacitive paddle pins will switch to normal (non-capacitive) sensing mode +#endif + +/* +FEATURE_SIDETONE_SWITCH + Enabling this feature and an external toggle switch adds switch control for playing cw sidetone. + ST Switch status is displayed in the status command. This feature will override the software control of the sidetone (\o). + Arduino pin is assigned by SIDETONE_SWITCH +*/ + +#ifdef FEATURE_SIDETONE_SWITCH + #define SIDETONE_SWITCH 8 +#endif //FEATURE_SIDETONE_SWITCH + +#ifdef FEATURE_4x4_KEYPAD + #define Row3 33 + #define Row2 32 + #define Row1 31 + #define Row0 30 + #define Col3 37 + #define Col2 36 + #define Col1 35 + #define Col0 34 +#endif + +#ifdef FEATURE_3x4_KEYPAD + #define Row3 33 + #define Row2 32 + #define Row1 31 + #define Row0 30 + #define Col2 36 + #define Col1 35 + #define Col0 34 +#endif + +#ifdef FEATURE_SEQUENCER + #define sequencer_1_pin 0 + #define sequencer_2_pin 0 + #define sequencer_3_pin 0 + #define sequencer_4_pin 0 + #define sequencer_5_pin 0 +#endif //FEATURE_SEQUENCER + +#define ptt_input_pin 0 + +#define tx_inhibit_pin 0 +#define tx_pause_pin 0 + +#else + + #error "Multiple pin_settings.h files included somehow..." + +#endif //keyer_pin_settings_h \ No newline at end of file diff --git a/k3ng_keyer/keyer_pin_settings_nanokeyer_rev_d.h b/k3ng_keyer/keyer_pin_settings_nanokeyer_rev_d.h new file mode 100755 index 0000000..a7b376a --- /dev/null +++ b/k3ng_keyer/keyer_pin_settings_nanokeyer_rev_d.h @@ -0,0 +1,74 @@ +#ifndef keyer_pin_settings_h +#define keyer_pin_settings_h + +#define paddle_left 2 +#define paddle_right 5 +#define tx_key_line_1 11 // (high = key down/tx on) +#define tx_key_line_2 0 // nanoKeyer PTT port (Pin 13) may be used as 2nd keying line for SO2R +#define tx_key_line_3 0 +#define tx_key_line_4 0 +#define tx_key_line_5 0 +#define tx_key_line_6 0 +#define sidetone_line 4 // connect a speaker for sidetone +#define potentiometer A0 // Rev D nanoKeyer +#define ptt_tx_1 13 // nanoKeyer PTT port (uncomment if using above as 2nd CW port for SO2R operation) +#define ptt_tx_2 0 // Can be used for keying fox transmitter, T/R switch, or keying slow boatanchors +#define ptt_tx_3 0 // These are optional - set to 0 if unused +#define ptt_tx_4 0 +#define ptt_tx_5 0 +#define ptt_tx_6 0 +#define analog_buttons_pin A1 // Rev D nanokeyer +#define lcd_rs A2 +#define lcd_enable 10 +#define lcd_d4 6 +#define lcd_d5 7 +#define lcd_d6 8 +#define lcd_d7 9 +#define ps2_keyboard_data A3 // Rev D nanokeyer +#define ps2_keyboard_clock 3 +#define tx_key_dit 0 // if defined, goes active for dit (any transmitter) - customized with tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state +#define tx_key_dah 0 // if defined, goes active for dah (any transmitter) - customized with tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state + +#define potentiometer_enable_pin 0 // if defined, the potentiometer will be enabled only when this pin is held low; set to 0 to ignore this pin + +#if defined(FEATURE_SLEEP) + #define keyer_awake 13 // Goes active when keyer is awake, inactive when in sleep mode; change active and inactive states in keyer_settings file +#endif + +#if defined(FEATURE_LCD_BACKLIGHT_AUTO_DIM) + #define keyer_power_led 0 // must be a PWM-capable pin +#endif + +#if defined(FEATURE_CAPACITIVE_PADDLE_PINS) + #define capactive_paddle_pin_inhibit_pin 0 // if this pin is defined and is set high, the capacitive paddle pins will switch to normal (non-capacitive) sensing mode +#endif + +/* +FEATURE_SIDETONE_SWITCH + Enabling this feature and an external toggle switch adds switch control for playing cw sidetone. + ST Switch status is displayed in the status command. This feature will override the software control of the sidetone (\o). + Arduino pin is assigned by SIDETONE_SWITCH +*/ + +#ifdef FEATURE_SIDETONE_SWITCH + #define SIDETONE_SWITCH 8 +#endif //FEATURE_SIDETONE_SWITCH + +#ifdef FEATURE_SEQUENCER + #define sequencer_1_pin 0 + #define sequencer_2_pin 0 + #define sequencer_3_pin 0 + #define sequencer_4_pin 0 + #define sequencer_5_pin 0 +#endif //FEATURE_SEQUENCER + +#define ptt_input_pin 0 + +#define tx_inhibit_pin 0 +#define tx_pause_pin 0 + +#else + + #error "Multiple pin_settings.h files included somehow..." + +#endif //keyer_pin_settings_h diff --git a/k3ng_keyer/keyer_pin_settings_open_interface.h b/k3ng_keyer/keyer_pin_settings_open_interface.h new file mode 100755 index 0000000..1ed72b5 --- /dev/null +++ b/k3ng_keyer/keyer_pin_settings_open_interface.h @@ -0,0 +1,143 @@ +// This file is for the Open Interface http://remoteqth.com/open-interface.php + +/* Pins - you must review these and configure ! */ +#ifndef keyer_pin_settings_h +#define keyer_pin_settings_h + +#define paddle_left 2 +#define paddle_right 5 +#define tx_key_line_1 11 // (high = key down/tx on) +#define tx_key_line_2 12 +#define tx_key_line_3 0 +#define tx_key_line_4 0 +#define tx_key_line_5 0 +#define tx_key_line_6 0 +#define sidetone_line 4 // connect a speaker for sidetone +#define potentiometer A0 // Speed potentiometer (0 to 5 V) Use pot from 1k to 10k +#define ptt_tx_1 0 // PTT ("push to talk") lines +#define ptt_tx_2 0 // Can be used for keying fox transmitter, T/R switch, or keying slow boatanchors +#define ptt_tx_3 0 // These are optional - set to 0 if unused +#define ptt_tx_4 0 +#define ptt_tx_5 0 +#define ptt_tx_6 0 +#define tx_key_dit 0 // if defined, goes active for dit (any transmitter) - customized with tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state +#define tx_key_dah 0 // if defined, goes active for dah (any transmitter) - customized with tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state + +#define potentiometer_enable_pin 0 // if defined, the potentiometer will be enabled only when this pin is held low; set to 0 to ignore this pin + +#ifdef FEATURE_BUTTONS + #define analog_buttons_pin A1 + #define command_mode_active_led 0 +#endif //FEATURE_BUTTONS + +//lcd pins +#ifdef FEATURE_LCD_4BIT + #define lcd_rs A2 + #define lcd_enable 10 + #define lcd_d4 6 + #define lcd_d5 7 + #define lcd_d6 8 + #define lcd_d7 9 +#endif //FEATURE_LCD_4BIT + +//ps2 keyboard pins +#ifdef FEATURE_PS2_KEYBOARD + #define ps2_keyboard_data A3 + #define ps2_keyboard_clock 3 // this must be on an interrupt capable pin! +#endif //FEATURE_PS2_KEYBOARD + +// rotary encoder pins and options - rotary encoder code from Jim Balls M0CKE +#ifdef FEATURE_ROTARY_ENCODER + #define OPTION_ENCODER_HALF_STEP_MODE // Half-step mode? + #define rotary_pin1 0 // CW Encoder Pin + #define rotary_pin2 0 // CCW Encoder Pin + #define OPTION_ENCODER_ENABLE_PULLUPS // define to enable weak pullups. +#endif //FEATURE_ROTARY_ENCODER + +#ifdef FEATURE_LED_RING + #define led_ring_sdi A10 //2 //Data + #define led_ring_clk A9 //3 //Clock + #define led_ring_le A8 //4 //Latch +#endif //FEATURE_LED_RING + +#define correct_answer_led 0 +#define wrong_answer_led 0 + +#ifdef FEATURE_PTT_INTERLOCK + #define ptt_interlock 0 // this pin disables PTT and TX KEY +#endif //FEATURE_PTT_INTERLOCK + + +// FEATURE_CW_DECODER & OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR +// See https://github.com/k3ng/k3ng_cw_keyer/wiki/385-Feature:-CW-Decoder for details +#define cw_decoder_pin 0 // This is for use with external decoding hardware +#define cw_decoder_audio_input_pin 0 // This is for audio detection decoding using OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR; this must be an analog pin! +#define cw_decoder_indicator 0 // Output - goes HIGH when cw tone is detected by OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR + +#if defined(FEATURE_COMPETITION_COMPRESSION_DETECTION) + #define compression_detection_pin 0 +#endif //FEATURE_COMPETITION_COMPRESSION_DETECTION + +#if defined(FEATURE_SLEEP) + #define keyer_awake 13 // Goes active when keyer is awake, inactive when in sleep mode; change active and inactive states in keyer_settings file +#endif + +#if defined(FEATURE_LCD_BACKLIGHT_AUTO_DIM) + #define keyer_power_led 0 // must be a PWM-capable pin +#endif + +#if defined(FEATURE_CAPACITIVE_PADDLE_PINS) + #define capactive_paddle_pin_inhibit_pin 0 // if this pin is defined and is set high, the capacitive paddle pins will switch to normal (non-capacitive) sensing mode +#endif + +/* +FEATURE_SIDETONE_SWITCH + Enabling this feature and an external toggle switch adds switch control for playing cw sidetone. + ST Switch status is displayed in the status command. This feature will override the software control of the sidetone (\o). + Arduino pin is assigned by SIDETONE_SWITCH +*/ + +#ifdef FEATURE_SIDETONE_SWITCH + #define SIDETONE_SWITCH 8 +#endif //FEATURE_SIDETONE_SWITCH + +#ifdef FEATURE_4x4_KEYPAD + #define Row3 33 + #define Row2 32 + #define Row1 31 + #define Row0 30 + #define Col3 37 + #define Col2 36 + #define Col1 35 + #define Col0 34 +#endif + +#ifdef FEATURE_3x4_KEYPAD + #define Row3 33 + #define Row2 32 + #define Row1 31 + #define Row0 30 + #define Col2 36 + #define Col1 35 + #define Col0 34 +#endif + +#ifdef FEATURE_SEQUENCER + #define sequencer_1_pin 0 + #define sequencer_2_pin 0 + #define sequencer_3_pin 0 + #define sequencer_4_pin 0 + #define sequencer_5_pin 0 +#endif //FEATURE_SEQUENCER + +#define ptt_input_pin 0 + +#define tx_inhibit_pin 0 +#define tx_pause_pin 0 + +#else + + #error "Multiple pin_settings.h files included somehow..." + +#endif //keyer_pin_settings_h + diff --git a/k3ng_keyer/keyer_pin_settings_opencwkeyer_mk2.h b/k3ng_keyer/keyer_pin_settings_opencwkeyer_mk2.h new file mode 100755 index 0000000..5e41079 --- /dev/null +++ b/k3ng_keyer/keyer_pin_settings_opencwkeyer_mk2.h @@ -0,0 +1,163 @@ +/* Pins - you must review these and configure ! */ +#ifndef keyer_pin_settings_h +#define keyer_pin_settings_h + + +#define paddle_left 2 +#define paddle_right 6 +#define tx_key_line_1 11 // (high = key down/tx on) +#define tx_key_line_2 13 +#define tx_key_line_3 0 +#define tx_key_line_4 0 +#define tx_key_line_5 0 +#define tx_key_line_6 0 +#define sidetone_line 4 // connect a speaker for sidetone +#define potentiometer A0 // Speed potentiometer (0 to 5 V) Use pot from 1k to 10k +#define ptt_tx_1 12 // PTT ("push to talk") lines +#define ptt_tx_2 13 // Can be used for keying fox transmitter, T/R switch, or keying slow boatanchors +#define ptt_tx_3 0 // These are optional - set to 0 if unused +#define ptt_tx_4 0 +#define ptt_tx_5 0 +#define ptt_tx_6 0 +#define tx_key_dit 0 // if defined, goes active for dit (any transmitter) - customized with tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state +#define tx_key_dah 0 // if defined, goes active for dah (any transmitter) - customized with tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state + +#define potentiometer_enable_pin 0 // if defined, the potentiometer will be enabled only when this pin is held low; set to 0 to ignore this pin + +#ifdef FEATURE_BUTTONS + #define analog_buttons_pin A1 + #define command_mode_active_led 0 +#endif //FEATURE_BUTTONS + +/* +FEATURE_SIDETONE_SWITCH + Enabling this feature and an external toggle switch adds switch control for playing cw sidetone. + ST Switch status is displayed in the status command. This feature will override the software control of the sidetone (\o). + Arduino pin is assigned by SIDETONE_SWITCH +*/ + +#ifdef FEATURE_SIDETONE_SWITCH + #define SIDETONE_SWITCH 8 +#endif //FEATURE_SIDETONE_SWITCH + + +//lcd pins +#if defined(FEATURE_LCD_4BIT) || defined(FEATURE_LCD_8BIT) + #define lcd_rs A2 + #define lcd_enable 10 // pin 10 is used by Ethernet shield and will conflict with that + #define lcd_d4 6 + #define lcd_d5 7 + #define lcd_d6 8 + #define lcd_d7 9 +#endif //FEATURE_LCD_4BIT || defined(FEATURE_LCD_8BIT) + +#if defined(FEATURE_LCD_8BIT) // addition four data lines for 8 bit LCD control + #define lcd_d0 20 + #define lcd_d1 21 + #define lcd_d2 22 + #define lcd_d3 23 +#endif //FEATURE_LCD_4BIT || defined(FEATURE_LCD_8BIT) + +#ifdef FEATURE_LCD1602_N07DH + #define lcd_rs 8 + #define lcd_enable 9 + #define lcd_d4 4 + #define lcd_d5 5 + #define lcd_d6 6 + #define lcd_d7 7 +#endif //FEATURE_LCD1602_N07DH + +//ps2 keyboard pins +#ifdef FEATURE_PS2_KEYBOARD + #define ps2_keyboard_data A3 + #define ps2_keyboard_clock 3 // this must be on an interrupt capable pin! +#endif //FEATURE_PS2_KEYBOARD + +// rotary encoder pins and options - rotary encoder code from Jim Balls M0CKE +#ifdef FEATURE_ROTARY_ENCODER + #define OPTION_ENCODER_HALF_STEP_MODE // Half-step mode? + #define rotary_pin1 0 // CW Encoder Pin + #define rotary_pin2 0 // CCW Encoder Pin + #define OPTION_ENCODER_ENABLE_PULLUPS // define to enable weak pullups. +#endif //FEATURE_ROTARY_ENCODER + +#ifdef FEATURE_LED_RING + #define led_ring_sdi A10 //2 //Data + #define led_ring_clk A9 //3 //Clock + #define led_ring_le A8 //4 //Latch +#endif //FEATURE_LED_RING + +#define correct_answer_led 0 +#define wrong_answer_led 0 + +#ifdef FEATURE_PTT_INTERLOCK + #define ptt_interlock 0 // this pin disables PTT and TX KEY +#endif //FEATURE_PTT_INTERLOCK + +#ifdef FEATURE_STRAIGHT_KEY + #define pin_straight_key 52 +#endif //FEATURE_STRAIGHT_KEY + +// FEATURE_CW_DECODER & OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR +// See https://github.com/k3ng/k3ng_cw_keyer/wiki/385-Feature:-CW-Decoder for details +#define cw_decoder_pin 0 // This is for use with external decoding hardware +#define cw_decoder_audio_input_pin 0 // This is for audio detection decoding using OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR; this must be an analog pin! +#define cw_decoder_indicator 0 // Output - goes HIGH when cw tone is detected by OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR + + +#if defined(FEATURE_COMPETITION_COMPRESSION_DETECTION) + #define compression_detection_pin 13 +#endif //FEATURE_COMPETITION_COMPRESSION_DETECTION + +#if defined(FEATURE_SLEEP) + #define keyer_awake 0 +#endif + +#if defined(FEATURE_LCD_BACKLIGHT_AUTO_DIM) + #define keyer_power_led 0 // must be a PWM-capable pin +#endif + +#if defined(FEATURE_CAPACITIVE_PADDLE_PINS) + #define capactive_paddle_pin_inhibit_pin 0 // if this pin is defined and is set high, the capacitive paddle pins will switch to normal (non-capacitive) sensing mode +#endif + +#ifdef FEATURE_4x4_KEYPAD + #define Row3 33 + #define Row2 32 + #define Row1 31 + #define Row0 30 + #define Col3 37 + #define Col2 36 + #define Col1 35 + #define Col0 34 +#endif + +#ifdef FEATURE_3x4_KEYPAD + #define Row3 33 + #define Row2 32 + #define Row1 31 + #define Row0 30 + #define Col2 36 + #define Col1 35 + #define Col0 34 +#endif + +#ifdef FEATURE_SEQUENCER + #define sequencer_1_pin 0 + #define sequencer_2_pin 0 + #define sequencer_3_pin 0 + #define sequencer_4_pin 0 + #define sequencer_5_pin 0 +#endif //FEATURE_SEQUENCER + +#define ptt_input_pin 0 + +#define tx_inhibit_pin 0 +#define tx_pause_pin 0 + +#else + + #error "Multiple pin_settings.h files included somehow..." + +#endif //keyer_pin_settings_h + diff --git a/k3ng_keyer/keyer_pin_settings_test.h b/k3ng_keyer/keyer_pin_settings_test.h new file mode 100755 index 0000000..c2eaff3 --- /dev/null +++ b/k3ng_keyer/keyer_pin_settings_test.h @@ -0,0 +1,185 @@ + +// ######## ######## ###### ######## +// ## ## ## ## ## +// ## ## ## ## +// ## ###### ###### ## +// ## ## ## ## +// ## ## ## ## ## +// ## ######## ###### ## + + +#ifndef keyer_pin_settings_h +#define keyer_pin_settings_h + +#define paddle_left 2 +#define paddle_right 5 +#define tx_key_line_1 11 // (high = key down/tx on) +#define tx_key_line_2 13 +#define tx_key_line_3 0 +#define tx_key_line_4 0 +#define tx_key_line_5 0 +#define tx_key_line_6 0 +#if !defined(FEATURE_ETHERNET) + #define sidetone_line 4 // connect a speaker for sidetone (pin 4 is used by the Ethernet shield!) +#else + #define sidetone_line 12 +#endif +#define potentiometer A0 // Speed potentiometer (0 to 5 V) Use pot from 1k to 10k +#define ptt_tx_1 13 // PTT ("push to talk") lines +#define ptt_tx_2 0 // Can be used for keying fox transmitter, T/R switch, or keying slow boatanchors +#define ptt_tx_3 0 // These are optional - set to 0 if unused +#define ptt_tx_4 0 +#define ptt_tx_5 0 +#define ptt_tx_6 0 +#define tx_key_dit 0 // if defined, goes active for dit (any transmitter) - customized with tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state +#define tx_key_dah 0 // if defined, goes active for dah (any transmitter) - customized with tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state + +#define potentiometer_enable_pin 0 // if defined, the potentiometer will be enabled only when this pin is held low; set to 0 to ignore this pin + +#ifdef FEATURE_BUTTONS + #define analog_buttons_pin A1 + #define command_mode_active_led 0 +#endif //FEATURE_BUTTONS + + +//lcd pins +#if defined(FEATURE_LCD_4BIT) || defined(FEATURE_LCD_8BIT) + #define lcd_rs A2 + #define lcd_enable 10 // pin 10 is used by Ethernet shield and will conflict with that + #define lcd_d4 6 + #define lcd_d5 7 + #define lcd_d6 8 + #define lcd_d7 9 +#endif //FEATURE_LCD_4BIT || defined(FEATURE_LCD_8BIT) + +#if defined(FEATURE_LCD_8BIT) // addition four data lines for 8 bit LCD control + #define lcd_d0 20 + #define lcd_d1 21 + #define lcd_d2 22 + #define lcd_d3 23 +#endif //FEATURE_LCD_4BIT || defined(FEATURE_LCD_8BIT) + +#ifdef FEATURE_LCD1602_N07DH + #define lcd_rs 8 + #define lcd_enable 9 + #define lcd_d4 4 // pin 4 is used by Ethernet shield and will conflict with that + #define lcd_d5 5 + #define lcd_d6 6 + #define lcd_d7 7 +#endif //FEATURE_LCD1602_N07DH + +//ps2 keyboard pins +#ifdef FEATURE_PS2_KEYBOARD + #define ps2_keyboard_data A3 + #define ps2_keyboard_clock 3 // this must be on an interrupt capable pin! +#endif //FEATURE_PS2_KEYBOARD + +// rotary encoder pins and options - rotary encoder code from Jim Balls M0CKE +#ifdef FEATURE_ROTARY_ENCODER + #define OPTION_ENCODER_HALF_STEP_MODE // Half-step mode? + #define rotary_pin1 0 // CW Encoder Pin + #define rotary_pin2 0 // CCW Encoder Pin + #define OPTION_ENCODER_ENABLE_PULLUPS // define to enable weak pullups. +#endif //FEATURE_ROTARY_ENCODER + +#ifdef FEATURE_LED_RING + #define led_ring_sdi A10 //2 //Data + #define led_ring_clk A9 //3 //Clock + #define led_ring_le A8 //4 //Latch +#endif //FEATURE_LED_RING + +#define correct_answer_led 0 +#define wrong_answer_led 0 + +#ifdef FEATURE_PTT_INTERLOCK + #define ptt_interlock 0 // this pin disables PTT and TX KEY +#endif //FEATURE_PTT_INTERLOCK + +#ifdef FEATURE_STRAIGHT_KEY + #define pin_straight_key A5 //52 // pin 52 doesn't work right when FEATURE_WEB_SERVER is active. don't know why 2016-04-26 +#endif //FEATURE_STRAIGHT_KEY + +// FEATURE_CW_DECODER & OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR +// See https://github.com/k3ng/k3ng_cw_keyer/wiki/385-Feature:-CW-Decoder for details +#define cw_decoder_pin 0 // This is for use with external decoding hardware +#define cw_decoder_audio_input_pin 0 // This is for audio detection decoding using OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR; this must be an analog pin! +#define cw_decoder_indicator 0 // Output - goes HIGH when cw tone is detected by OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR + + +#if defined(FEATURE_COMPETITION_COMPRESSION_DETECTION) + #define compression_detection_pin 13 +#endif //FEATURE_COMPETITION_COMPRESSION_DETECTION + +#if defined(FEATURE_SLEEP) + #define keyer_awake 13 // Goes active when keyer is awake, inactive when in sleep mode; change active and inactive states in keyer_settings file +#endif + +#if defined(FEATURE_LCD_BACKLIGHT_AUTO_DIM) + #define keyer_power_led 0 // must be a PWM-capable pin +#endif + +#if defined(FEATURE_CAPACITIVE_PADDLE_PINS) + #define capactive_paddle_pin_inhibit_pin 0 // if this pin is defined and is set high, the capacitive paddle pins will switch to normal (non-capacitive) sensing mode +#endif + +/* +FEATURE_SIDETONE_SWITCH + Enabling this feature and an external toggle switch adds switch control for playing cw sidetone. + ST Switch status is displayed in the status command. This feature will override the software control of the sidetone (\o). + Arduino pin is assigned by SIDETONE_SWITCH +*/ + +#ifdef FEATURE_SIDETONE_SWITCH + #define SIDETONE_SWITCH 8 +#endif //FEATURE_SIDETONE_SWITCH + +#ifdef FEATURE_4x4_KEYPAD + #define Row3 33 + #define Row2 32 + #define Row1 31 + #define Row0 30 + #define Col3 37 + #define Col2 36 + #define Col1 35 + #define Col0 34 +#endif + +#ifdef FEATURE_3x4_KEYPAD + #define Row3 33 + #define Row2 32 + #define Row1 31 + #define Row0 30 + #define Col2 36 + #define Col1 35 + #define Col0 34 +#endif + +#ifdef FEATURE_SEQUENCER + #define sequencer_1_pin 40 + #define sequencer_2_pin 41 + #define sequencer_3_pin 42 + #define sequencer_4_pin 43 + #define sequencer_5_pin 44 +#endif //FEATURE_SEQUENCER + +#define ptt_input_pin 0 + +#define tx_inhibit_pin 0 +#define tx_pause_pin 0 + + +#else + + #error "Multiple pin_settings.h files included somehow..." + +#endif //keyer_pin_settings_h + + +// ######## ######## ###### ######## +// ## ## ## ## ## +// ## ## ## ## +// ## ###### ###### ## +// ## ## ## ## +// ## ## ## ## ## +// ## ######## ###### ## + diff --git a/k3ng_keyer/keyer_pin_settings_test_everything.h b/k3ng_keyer/keyer_pin_settings_test_everything.h new file mode 100755 index 0000000..48211a7 --- /dev/null +++ b/k3ng_keyer/keyer_pin_settings_test_everything.h @@ -0,0 +1,199 @@ + +// ######## ######## ###### ######## +// ## ## ## ## ## +// ## ## ## ## +// ## ###### ###### ## +// ## ## ## ## +// ## ## ## ## ## +// ## ######## ###### ## + + +#ifndef keyer_pin_settings_h +#define keyer_pin_settings_h + +#define paddle_left 2 +#define paddle_right 5 +#define tx_key_line_1 11 // (high = key down/tx on) +#define tx_key_line_2 12 +#define tx_key_line_3 0 +#define tx_key_line_4 0 +#define tx_key_line_5 0 +#define tx_key_line_6 0 +#if !defined(FEATURE_ETHERNET) + #define sidetone_line 4 // connect a speaker for sidetone +#else + #define sidetone_line 31 // pin 4 is used by Ethernet shield so we'll use pin 31 on the test jig... +#endif +#define potentiometer A0 // Speed potentiometer (0 to 5 V) Use pot from 1k to 10k +#define ptt_tx_1 13 // PTT ("push to talk") lines +#define ptt_tx_2 0 // Can be used for keying fox transmitter, T/R switch, or keying slow boatanchors +#define ptt_tx_3 0 // These are optional - set to 0 if unused +#define ptt_tx_4 0 +#define ptt_tx_5 0 +#define ptt_tx_6 0 +#define tx_key_dit 0 // if defined, goes active for dit (any transmitter) - customized with tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state +#define tx_key_dah 0 // if defined, goes active for dah (any transmitter) - customized with tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state + +#define so2r_tx_1 0 // Radio 1 transmitter selected (optional, set to zero if not used) +#define so2r_tx_2 11 // Radio 2 transmitter selected (optional, set to zero if not used) +#define so2r_rx_1 0 // Radio 1 receiver selected (optional, set to zero if not used) +#define so2r_rx_2 0 // Radio 2 receiver selected (optional, set to zero if not used) +#define so2r_rx_1s 0 // Radio 1 receiver or stereo selected (optional, set to zero if not used) +#define so2r_rx_2s 9 // Radio 2 receiver or stereo selected (optional, set to zero if not used) +#define so2r_rx_s 10 // Stereo receive selected (optional, set to zero if not used) + +#ifdef FEATURE_SO2R_SWITCHES + #define so2r_tx_switch A5 // TX switch, low if TX1, high if TX2 + #define so2r_rx1_switch A4 // RX 1 switch, low if RX1, high if RX2 or stereo + #define so2r_rx2_switch A3 // RX 2 switch, low if RX2, high if RX1 or stereo +#endif + +#define potentiometer_enable_pin 0 // if defined, the potentiometer will be enabled only when this pin is held low; set to 0 to ignore this pin + +#ifdef FEATURE_BUTTONS + #define analog_buttons_pin A1 + #define command_mode_active_led 0 +#endif //FEATURE_BUTTONS + + +//lcd pins +#if defined(FEATURE_LCD_4BIT) || defined(FEATURE_LCD_8BIT) + #define lcd_rs A2 + #define lcd_enable 10 // pin 10 is used by Ethernet shield and will conflict with that + #define lcd_d4 6 + #define lcd_d5 7 + #define lcd_d6 8 + #define lcd_d7 9 +#endif //FEATURE_LCD_4BIT || defined(FEATURE_LCD_8BIT) + +#if defined(FEATURE_LCD_8BIT) // addition four data lines for 8 bit LCD control + #define lcd_d0 20 + #define lcd_d1 21 + #define lcd_d2 22 + #define lcd_d3 23 +#endif //FEATURE_LCD_4BIT || defined(FEATURE_LCD_8BIT) + +#ifdef FEATURE_LCD1602_N07DH + #define lcd_rs 8 + #define lcd_enable 9 + #define lcd_d4 4 // pin 4 is used by Ethernet shield and will conflict with that + #define lcd_d5 5 + #define lcd_d6 6 + #define lcd_d7 7 +#endif //FEATURE_LCD1602_N07DH + +//ps2 keyboard pins +#ifdef FEATURE_PS2_KEYBOARD + #define ps2_keyboard_data A3 + #define ps2_keyboard_clock 3 // this must be on an interrupt capable pin! +#endif //FEATURE_PS2_KEYBOARD + +// rotary encoder pins and options - rotary encoder code from Jim Balls M0CKE +#ifdef FEATURE_ROTARY_ENCODER + #define OPTION_ENCODER_HALF_STEP_MODE // Half-step mode? + #define rotary_pin1 0 // CW Encoder Pin + #define rotary_pin2 0 // CCW Encoder Pin + #define OPTION_ENCODER_ENABLE_PULLUPS // define to enable weak pullups. +#endif //FEATURE_ROTARY_ENCODER + +#ifdef FEATURE_LED_RING + #define led_ring_sdi A10 //2 //Data + #define led_ring_clk A9 //3 //Clock + #define led_ring_le A8 //4 //Latch +#endif //FEATURE_LED_RING + +#define correct_answer_led 0 +#define wrong_answer_led 0 + +#ifdef FEATURE_PTT_INTERLOCK + #define ptt_interlock 0 // this pin disables PTT and TX KEY +#endif //FEATURE_PTT_INTERLOCK + +#ifdef FEATURE_STRAIGHT_KEY + #define pin_straight_key A5 //52 // pin 52 doesn't work right when FEATURE_WEB_SERVER is active. don't know why 2016-04-26 +#endif //FEATURE_STRAIGHT_KEY + +// FEATURE_CW_DECODER & OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR +// See https://github.com/k3ng/k3ng_cw_keyer/wiki/385-Feature:-CW-Decoder for details +#define cw_decoder_pin 0 // This is for use with external decoding hardware +#define cw_decoder_audio_input_pin 0 // This is for audio detection decoding using OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR; this must be an analog pin! +#define cw_decoder_indicator 0 // Output - goes HIGH when cw tone is detected by OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR + + +#if defined(FEATURE_COMPETITION_COMPRESSION_DETECTION) + #define compression_detection_pin 13 +#endif //FEATURE_COMPETITION_COMPRESSION_DETECTION + +#if defined(FEATURE_SLEEP) + #define keyer_awake 13 // Goes active when keyer is awake, inactive when in sleep mode; change active and inactive states in keyer_settings file +#endif + +#if defined(FEATURE_LCD_BACKLIGHT_AUTO_DIM) + #define keyer_power_led 0 // must be a PWM-capable pin +#endif + +#if defined(FEATURE_CAPACITIVE_PADDLE_PINS) + #define capactive_paddle_pin_inhibit_pin 0 // if this pin is defined and is set high, the capacitive paddle pins will switch to normal (non-capacitive) sensing mode +#endif + +/* +FEATURE_SIDETONE_SWITCH + Enabling this feature and an external toggle switch adds switch control for playing cw sidetone. + ST Switch status is displayed in the status command. This feature will override the software control of the sidetone (\o). + Arduino pin is assigned by SIDETONE_SWITCH +*/ + +#ifdef FEATURE_SIDETONE_SWITCH + #define SIDETONE_SWITCH 8 +#endif //FEATURE_SIDETONE_SWITCH + +#ifdef FEATURE_4x4_KEYPAD + #define Row3 33 + #define Row2 32 + #define Row1 31 + #define Row0 30 + #define Col3 37 + #define Col2 36 + #define Col1 35 + #define Col0 34 +#endif + +#ifdef FEATURE_3x4_KEYPAD + #define Row3 33 + #define Row2 32 + #define Row1 31 + #define Row0 30 + #define Col2 36 + #define Col1 35 + #define Col0 34 +#endif + +#ifdef FEATURE_SEQUENCER + #define sequencer_1_pin 40 + #define sequencer_2_pin 41 + #define sequencer_3_pin 42 + #define sequencer_4_pin 43 + #define sequencer_5_pin 44 +#endif //FEATURE_SEQUENCER + +#define ptt_input_pin 0 + +#define tx_inhibit_pin 50 +#define tx_pause_pin 48 + + +#else + + #error "Multiple pin_settings.h files included somehow..." + +#endif //keyer_pin_settings_h + + +// ######## ######## ###### ######## +// ## ## ## ## ## +// ## ## ## ## +// ## ###### ###### ## +// ## ## ## ## +// ## ## ## ## ## +// ## ######## ###### ## + diff --git a/k3ng_keyer/keyer_pin_settings_tinykeyer.h b/k3ng_keyer/keyer_pin_settings_tinykeyer.h new file mode 100755 index 0000000..db963bb --- /dev/null +++ b/k3ng_keyer/keyer_pin_settings_tinykeyer.h @@ -0,0 +1,94 @@ +// features & options for TinyKeyer by OK1RR +#ifndef keyer_pin_settings_h +#define keyer_pin_settings_h + +#define paddle_left 2 // dit +#define paddle_right 5 // dah +#define tx_key_line_1 12 // tinykeyer keyed port (high = key down/tx on) +#define tx_key_line_2 0 +#define tx_key_line_3 0 +#define tx_key_line_4 0 +#define tx_key_line_5 0 +#define tx_key_line_6 0 +#define sidetone_line 4 // connect a speaker for sidetone +#define potentiometer 0 // tinykeyer no pot used +#define ptt_tx_1 0 // (put 13 instead 0 to activate) tinykeyer PTT port (high = PTT down/PTT on) +#define ptt_tx_2 0 // Can be used for keying fox transmitter, T/R switch, or keying slow boatanchors +#define ptt_tx_3 0 // These are optional - set to 0 if unused +#define ptt_tx_4 0 +#define ptt_tx_5 0 +#define ptt_tx_6 0 +#define analog_buttons_pin A1 // tinykeyer +#define command_mode_active_led 9 // tinykeyer - command mode LED indicator +#define tx_key_dit 0 // if defined, goes active for dit (any transmitter) - customized with tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state +#define tx_key_dah 0 // if defined, goes active for dah (any transmitter) - customized with tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state + +#define potentiometer_enable_pin 0 // if defined, the potentiometer will be enabled only when this pin is held low; set to 0 to ignore this pin + +// rotary encoder pins and options - rotary encoder code from Jim Balls M0CKE +#ifdef FEATURE_ROTARY_ENCODER + #define OPTION_ENCODER_HALF_STEP_MODE // Half-step mode? + #define rotary_pin1 11 // CW Encoder Pin + #define rotary_pin2 10 // CCW Encoder Pin + #define OPTION_ENCODER_ENABLE_PULLUPS // define to enable weak pullups. +#endif //FEATURE_ROTARY_ENCODER + +#if defined(FEATURE_SLEEP) + #define keyer_awake 8 // Goes active when keyer is awake, inactive when in sleep mode; change active and inactive states in keyer_settings file +#endif + +#if defined(FEATURE_LCD_BACKLIGHT_AUTO_DIM) + #define keyer_power_led 0 // must be a PWM-capable pin +#endif + +/* +FEATURE_SIDETONE_SWITCH + Enabling this feature and an external toggle switch adds switch control for playing cw sidetone. + ST Switch status is displayed in the status command. This feature will override the software control of the sidetone (\o). + Arduino pin is assigned by SIDETONE_SWITCH +*/ + +#ifdef FEATURE_SIDETONE_SWITCH + #define SIDETONE_SWITCH 8 +#endif //FEATURE_SIDETONE_SWITCH + +#ifdef FEATURE_4x4_KEYPAD + #define Row3 33 + #define Row2 32 + #define Row1 31 + #define Row0 30 + #define Col3 37 + #define Col2 36 + #define Col1 35 + #define Col0 34 +#endif + +#ifdef FEATURE_3x4_KEYPAD + #define Row3 33 + #define Row2 32 + #define Row1 31 + #define Row0 30 + #define Col2 36 + #define Col1 35 + #define Col0 34 +#endif + + +#ifdef FEATURE_SEQUENCER + #define sequencer_1_pin 0 + #define sequencer_2_pin 0 + #define sequencer_3_pin 0 + #define sequencer_4_pin 0 + #define sequencer_5_pin 0 +#endif //FEATURE_SEQUENCER + +#define ptt_input_pin 0 + +#define tx_inhibit_pin 0 +#define tx_pause_pin 0 + +#else + + #error "Multiple pin_settings.h files included somehow..." + +#endif //keyer_pin_settings_h diff --git a/k3ng_keyer/keyer_pin_settings_yaacwk.h b/k3ng_keyer/keyer_pin_settings_yaacwk.h new file mode 100755 index 0000000..3d97192 --- /dev/null +++ b/k3ng_keyer/keyer_pin_settings_yaacwk.h @@ -0,0 +1,143 @@ +#ifndef keyer_pin_settings_h +#define keyer_pin_settings_h + +#define paddle_left 21 +#define paddle_right 22 +#define tx_key_line_1 28 // (high = key down/tx on) +#define tx_key_line_2 31 +#define tx_key_line_3 0 +#define tx_key_line_4 0 +#define tx_key_line_5 0 +#define tx_key_line_6 0 +#define sidetone_line 23 // connect a speaker for sidetone +#define potentiometer A0 // Speed potentiometer (0 to 5 V) Use pot from 1k to 10k +#define ptt_tx_1 0 // PTT ("push to talk") lines +#define ptt_tx_2 0 // Can be used for keying fox transmitter, T/R switch, or keying slow boatanchors +#define ptt_tx_3 0 // These are optional - set to 0 if unused +#define ptt_tx_4 0 +#define ptt_tx_5 0 +#define ptt_tx_6 0 +#define tx_key_dit 0 // if defined, goes active for dit (any transmitter) - customized with tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state +#define tx_key_dah 0 // if defined, goes active for dah (any transmitter) - customized with tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state + +#define potentiometer_enable_pin 0 // if defined, the potentiometer will be enabled only when this pin is held low; set to 0 to ignore this pin + +#ifdef FEATURE_BUTTONS + #define analog_buttons_pin A1 + #define command_mode_active_led 0 +#endif //FEATURE_BUTTONS + +/* +FEATURE_SIDETONE_SWITCH + Enabling this feature and an external toggle switch adds switch control for playing cw sidetone. + ST Switch status is displayed in the status command. This feature will override the software control of the sidetone (\o). + Arduino pin is assigned by SIDETONE_SWITCH +*/ + +#ifdef FEATURE_SIDETONE_SWITCH + #define SIDETONE_SWITCH 0 +#endif //FEATURE_SIDETONE_SWITCH + + +//lcd pins +#ifdef FEATURE_LCD_4BIT + #define lcd_rs 26 // A2 + #define lcd_enable 20 + #define lcd_d4 12 + #define lcd_d5 13 + #define lcd_d6 14 + #define lcd_d7 15 +#endif //FEATURE_LCD_4BIT + +//ps2 keyboard pins +#ifdef FEATURE_PS2_KEYBOARD + #define ps2_keyboard_data A3 + #define ps2_keyboard_clock 2 // this must be on an interrupt capable pin! +#endif //FEATURE_PS2_KEYBOARD + +// rotary encoder pins and options - rotary encoder code from Jim Balls M0CKE +#ifdef FEATURE_ROTARY_ENCODER + #define OPTION_ENCODER_HALF_STEP_MODE // Half-step mode? + #define rotary_pin1 0 // CW Encoder Pin + #define rotary_pin2 0 // CCW Encoder Pin + #define OPTION_ENCODER_ENABLE_PULLUPS // define to enable weak pullups. +#endif //FEATURE_ROTARY_ENCODER + +#ifdef FEATURE_LED_RING + #define led_ring_sdi A10 //2 //Data + #define led_ring_clk A9 //3 //Clock + #define led_ring_le A8 //4 //Latch +#endif //FEATURE_LED_RING + +#define correct_answer_led 0 +#define wrong_answer_led 0 + +#ifdef FEATURE_PTT_INTERLOCK + #define ptt_interlock 0 // this pin disables PTT and TX KEY +#endif //FEATURE_PTT_INTERLOCK + +#ifdef FEATURE_STRAIGHT_KEY + #define pin_straight_key 0 +#endif //FEATURE_STRAIGHT_KEY + +// FEATURE_CW_DECODER & OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR +// See https://github.com/k3ng/k3ng_cw_keyer/wiki/385-Feature:-CW-Decoder for details +#define cw_decoder_pin 0 // This is for use with external decoding hardware +#define cw_decoder_audio_input_pin 0 // This is for audio detection decoding using OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR; this must be an analog pin! +#define cw_decoder_indicator 0 // Output - goes HIGH when cw tone is detected by OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR + +#if defined(FEATURE_COMPETITION_COMPRESSION_DETECTION) + #define compression_detection_pin 0 +#endif //FEATURE_COMPETITION_COMPRESSION_DETECTION + +#if defined(FEATURE_SLEEP) + #define keyer_awake 0 // Goes active when keyer is awake, inactive when in sleep mode; change active and inactive states in keyer_settings file +#endif + +#if defined(FEATURE_LCD_BACKLIGHT_AUTO_DIM) + #define keyer_power_led 0 // must be a PWM-capable pin +#endif + +#if defined(FEATURE_CAPACITIVE_PADDLE_PINS) + #define capactive_paddle_pin_inhibit_pin 0 // if this pin is defined and is set high, the capacitive paddle pins will switch to normal (non-capacitive) sensing mode +#endif + +#ifdef FEATURE_4x4_KEYPAD + #define Row3 33 + #define Row2 32 + #define Row1 31 + #define Row0 30 + #define Col3 37 + #define Col2 36 + #define Col1 35 + #define Col0 34 +#endif + +#ifdef FEATURE_3x4_KEYPAD + #define Row3 33 + #define Row2 32 + #define Row1 31 + #define Row0 30 + #define Col2 36 + #define Col1 35 + #define Col0 34 +#endif + +#ifdef FEATURE_SEQUENCER + #define sequencer_1_pin 0 + #define sequencer_2_pin 0 + #define sequencer_3_pin 0 + #define sequencer_4_pin 0 + #define sequencer_5_pin 0 +#endif //FEATURE_SEQUENCER + +#define ptt_input_pin 0 + +#define tx_inhibit_pin 0 +#define tx_pause_pin 0 + +#else + + #error "Multiple pin_settings.h files included somehow..." + +#endif //keyer_pin_settings_h diff --git a/k3ng_keyer/keyer_pin_settings_yccc_so2r_mini.h b/k3ng_keyer/keyer_pin_settings_yccc_so2r_mini.h new file mode 100755 index 0000000..771c52f --- /dev/null +++ b/k3ng_keyer/keyer_pin_settings_yccc_so2r_mini.h @@ -0,0 +1,176 @@ +/* Pins - you must review these and configure ! */ +#ifndef keyer_pin_settings_h +#define keyer_pin_settings_h + +#define paddle_left 6 +#define paddle_right 7 + +#define tx_key_line_1 5 // (high = key down/tx on) +#define tx_key_line_2 4 +#define tx_key_line_3 0 +#define tx_key_line_4 0 +#define tx_key_line_5 0 +#define tx_key_line_6 0 +#define sidetone_line 0 // connect a buzzer for sidetone (optional, set to zero if not used) +#define potentiometer A7 // Speed potentiometer (0 to 5 V) Use pot from 1k to 10k +#define ptt_tx_1 3 // Radio 1 PTT ("push to talk") line +#define ptt_tx_2 2 // Radio 2 PTT ("push to talk") line +#define ptt_tx_3 0 // These are optional - set to 0 if unused +#define ptt_tx_4 0 +#define ptt_tx_5 0 +#define ptt_tx_6 0 +#define tx_key_dit 0 // if defined, goes active for dit (any transmitter) - customized with tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state +#define tx_key_dah 0 // if defined, goes active for dah (any transmitter) - customized with tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state + +#define so2r_tx_1 0 // Radio 1 transmitter selected (optional, set to zero if not used) +#define so2r_tx_2 11 // Radio 2 transmitter selected (optional, set to zero if not used) +#define so2r_rx_1 0 // Radio 1 receiver selected (optional, set to zero if not used) +#define so2r_rx_2 0 // Radio 2 receiver selected (optional, set to zero if not used) +#define so2r_rx_1s 0 // Radio 1 receiver or stereo selected (optional, set to zero if not used) +#define so2r_rx_2s 9 // Radio 2 receiver or stereo selected (optional, set to zero if not used) +#define so2r_rx_s 10 // Stereo receive selected (optional, set to zero if not used) + +#ifdef FEATURE_SO2R_SWITCHES + #define so2r_tx_switch A5 // TX switch, low if TX1, high if TX2 + #define so2r_rx1_switch A4 // RX 1 switch, low if RX1, high if RX2 or stereo + #define so2r_rx2_switch A3 // RX 2 switch, low if RX2, high if RX1 or stereo +#endif + +#define potentiometer_enable_pin 0 // if defined, the potentiometer will be enabled only when this pin is held low; set to 0 to ignore this pin + +#ifdef FEATURE_BUTTONS + #define analog_buttons_pin A1 + #define command_mode_active_led 0 +#endif //FEATURE_BUTTONS + +/* +FEATURE_SIDETONE_SWITCH + Enabling this feature and an external toggle switch adds switch control for playing cw sidetone. + ST Switch status is displayed in the status command. This feature will override the software control of the sidetone (\o). + Arduino pin is assigned by SIDETONE_SWITCH +*/ + +#ifdef FEATURE_SIDETONE_SWITCH + #define SIDETONE_SWITCH 8 +#endif //FEATURE_SIDETONE_SWITCH + + +//lcd pins +#if defined(FEATURE_LCD_4BIT) || defined(FEATURE_LCD_8BIT) + #define lcd_rs A2 + #define lcd_enable 10 // pin 10 is used by Ethernet shield and will conflict with that + #define lcd_d4 6 + #define lcd_d5 7 + #define lcd_d6 8 + #define lcd_d7 9 +#endif //FEATURE_LCD_4BIT || defined(FEATURE_LCD_8BIT) + +#if defined(FEATURE_LCD_8BIT) // addition four data lines for 8 bit LCD control + #define lcd_d0 20 + #define lcd_d1 21 + #define lcd_d2 22 + #define lcd_d3 23 +#endif //FEATURE_LCD_4BIT || defined(FEATURE_LCD_8BIT) + +#ifdef FEATURE_LCD1602_N07DH + #define lcd_rs 8 + #define lcd_enable 9 + #define lcd_d4 4 + #define lcd_d5 5 + #define lcd_d6 6 + #define lcd_d7 7 +#endif //FEATURE_LCD1602_N07DH + +//ps2 keyboard pins +#ifdef FEATURE_PS2_KEYBOARD + #define ps2_keyboard_data A3 + #define ps2_keyboard_clock 3 // this must be on an interrupt capable pin! +#endif //FEATURE_PS2_KEYBOARD + +// rotary encoder pins and options - rotary encoder code from Jim Balls M0CKE +#ifdef FEATURE_ROTARY_ENCODER + #define OPTION_ENCODER_HALF_STEP_MODE // Half-step mode? + #define rotary_pin1 0 // CW Encoder Pin + #define rotary_pin2 0 // CCW Encoder Pin + #define OPTION_ENCODER_ENABLE_PULLUPS // define to enable weak pullups. +#endif //FEATURE_ROTARY_ENCODER + +#ifdef FEATURE_LED_RING + #define led_ring_sdi A10 //2 //Data + #define led_ring_clk A9 //3 //Clock + #define led_ring_le A8 //4 //Latch +#endif //FEATURE_LED_RING + +#define correct_answer_led 0 +#define wrong_answer_led 0 + +#ifdef FEATURE_PTT_INTERLOCK + #define ptt_interlock 0 // this pin disables PTT and TX KEY +#endif //FEATURE_PTT_INTERLOCK + +#ifdef FEATURE_STRAIGHT_KEY + #define pin_straight_key 52 +#endif //FEATURE_STRAIGHT_KEY + +// FEATURE_CW_DECODER & OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR +// See https://github.com/k3ng/k3ng_cw_keyer/wiki/385-Feature:-CW-Decoder for details +#define cw_decoder_pin 0 // This is for use with external decoding hardware +#define cw_decoder_audio_input_pin 0 // This is for audio detection decoding using OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR; this must be an analog pin! +#define cw_decoder_indicator 0 // Output - goes HIGH when cw tone is detected by OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR + + +#if defined(FEATURE_COMPETITION_COMPRESSION_DETECTION) + #define compression_detection_pin 13 +#endif //FEATURE_COMPETITION_COMPRESSION_DETECTION + +#if defined(FEATURE_SLEEP) + #define keyer_awake 0 +#endif + +#if defined(FEATURE_LCD_BACKLIGHT_AUTO_DIM) + #define keyer_power_led 0 // must be a PWM-capable pin +#endif + +#if defined(FEATURE_CAPACITIVE_PADDLE_PINS) + #define capactive_paddle_pin_inhibit_pin 0 // if this pin is defined and is set high, the capacitive paddle pins will switch to normal (non-capacitive) sensing mode +#endif + +#ifdef FEATURE_4x4_KEYPAD + #define Row3 33 + #define Row2 32 + #define Row1 31 + #define Row0 30 + #define Col3 37 + #define Col2 36 + #define Col1 35 + #define Col0 34 +#endif + +#ifdef FEATURE_3x4_KEYPAD + #define Row3 33 + #define Row2 32 + #define Row1 31 + #define Row0 30 + #define Col2 36 + #define Col1 35 + #define Col0 34 +#endif + +#ifdef FEATURE_SEQUENCER + #define sequencer_1_pin 0 + #define sequencer_2_pin 0 + #define sequencer_3_pin 0 + #define sequencer_4_pin 0 + #define sequencer_5_pin 0 +#endif //FEATURE_SEQUENCER + +#define ptt_input_pin 8 + +#define tx_inhibit_pin 0 +#define tx_pause_pin 0 + +#else + + #error "Multiple pin_settings.h files included somehow..." + +#endif //keyer_pin_settings_h diff --git a/k3ng_keyer/keyer_settings.h b/k3ng_keyer/keyer_settings.h new file mode 100755 index 0000000..fa9fde3 --- /dev/null +++ b/k3ng_keyer/keyer_settings.h @@ -0,0 +1,316 @@ +// Initial and hardcoded settings +#define initial_speed_wpm 26 // "factory default" keyer speed setting +#define initial_command_mode_speed_wpm 20 // "factory default" command mode speed setting +#define initial_sidetone_freq 600 // "factory default" sidetone frequency setting +#define sidetone_hz_limit_low 299 +#define sidetone_hz_limit_high 2001 +#define hz_high_beep 1500 // frequency in hertz of high beep +#define hz_low_beep 400 // frequency in hertz of low beep +#define initial_dah_to_dit_ratio 300 // 300 = 3 / normal 3:1 ratio +#define initial_ptt_lead_time_tx1 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx1 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx2 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx2 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx3 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx3 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx4 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx4 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx5 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx5 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx6 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx6 10 // PTT tail time in mS +#define initial_qrss_dit_length 1 // QRSS dit length in seconds +#define initial_pot_wpm_low_value 13 // Potentiometer WPM fully CCW +#define initial_pot_wpm_high_value 35 // Potentiometer WPM fully CW +#define wpm_limit_low 5 +#define wpm_limit_high 60 +#define potentiometer_change_threshold 1.0 // don't change the keyer speed until pot wpm has changed more than this +#define send_buffer_size 150 +#define default_length_letterspace 3 +#define default_length_wordspace 7 +#define default_keying_compensation 0 // number of milliseconds to extend all dits and dahs - for QSK on boatanchors +#define default_first_extension_time 0 // number of milliseconds to extend first sent dit or dah +#define default_pot_full_scale_reading 1023 +#define default_weighting 50 // 50 = weighting factor of 1 (normal) +#define default_ptt_hang_time_wordspace_units 1.0 +#define winkey_c0_wait_time 1 // the number of milliseconds to wait to send 0xc0 byte after send buffer has been sent +#define winkey_command_timeout_ms 5000 +#define winkey_discard_bytes_startup 3 // this is used if OPTION_WINKEY_DISCARD_BYTES_AT_STARTUP is enabled above +#define winkey_xoff_threshold 20 // the number of chars in the buffer when we begin sending XOFFs +#define winkey_xon_threshold 10 // the number of chars in the buffer below which we deactivate XOFF +#define default_memory_repeat_time 3000 // time in milliseconds +#define LCD_COLUMNS 20 +#define LCD_ROWS 4 +#define lcd_i2c_address_mathertel_PCF8574 0x27 // I2C address of display for FEATURE_LCD_MATHERTEL_PCF8574 +#define lcd_i2c_address_fdebrander_lcd 0x27 // I2C address of display for FEATURE_LCD_I2C_FDEBRABANDER +#define lcd_i2c_address_ydv1_lcd 0x27 // I2C address of display for FEATURE_LCD_YDv1 +//#define lcd_i2c_address_ydv1_lcd 0x38 // I2C address of display for FEATURE_LCD_YDv1 +#define lcd_i2c_address_sainsmart_lcd 0x27 // I2C address of display for FEATURE_LCD_SAINSMART_I2C +#define hell_pixel_microseconds 4025 +#define program_memory_limit_consec_spaces 1 +#define serial_leading_zeros 1 // set to 1 to activate leading zeros in serial numbers (i.e. #1 = 001) +#define serial_cut_numbers 0 // set to 1 to activate cut numbers in serial numbers (i.e. #10 = 1T, #19 = 1N) +#define go_to_sleep_inactivity_time 10 // minutes - FEATURE_SLEEP +#define dim_backlight_inactive_time 5 // minutes - FEATURE_LCD_BACKLIGHT_AUTO_DIM +#define default_cmos_super_keyer_iambic_b_timing_percent 33 // use with FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING; should be between 0 to 99 % (0% = true iambic b;100% = iambic a behavior) +#define default_cw_echo_timing_factor 1.75 // "factory default" setting +#define default_autospace_timing_factor 2.0 // "factory default" setting +#define winkey_paddle_echo_buffer_decode_timing_factor 0.25 +#define potentiometer_always_on 1 +#define ptt_interlock_check_every_ms 100 +#define ptt_interlock_active_state HIGH +#define unknown_cw_character '*' +#define cli_paddle_echo_on_at_boot 1 +#define cli_straight_key_echo_on_at_boot 1 +#define tx_key_dit_and_dah_pins_active_state HIGH +#define tx_key_dit_and_dah_pins_inactive_state LOW +#define potentiometer_check_interval_ms 150 +#define potentiometer_reading_threshold 1 +#define default_paddle_interruption_quiet_time_element_lengths 0 +#define default_wordsworth_wordspace 6 +#define default_wordsworth_repetition 1 +#define serial_program_memory_buffer_size 500 +#define eeprom_write_time_ms 30000 + +#ifdef FEATURE_BUTTONS + #define analog_buttons_number_of_buttons 6 // includes the command button (command button + 3 memory buttons = 4) + #define analog_buttons_r1 10 + #define analog_buttons_r2 1 +#endif + + +#if defined(FEATURE_BUTTONS) && !defined(FEATURE_PS2_KEYBOARD) && !defined(FEATURE_USB_KEYBOARD) && !defined(FEATURE_COMMAND_LINE_INTERFACE) && !defined(FEATURE_WINKEY_EMULATION) + #define number_of_memories byte(analog_buttons_number_of_buttons-1) +#else + #define number_of_memories byte(12) +#endif + +#ifdef FEATURE_CAPACITIVE_PADDLE_PINS + #define capacitance_threshold 2 +#endif //FEATURE_CAPACITIVE_PADDLE_PINS + +#ifdef FEATURE_LED_RING + #define led_ring_low_limit 10 + #define led_ring_high_limit 50 +#endif //FEATURE_LED_RING + +#ifdef FEATURE_QLF + #define qlf_dit_max 125 + #define qlf_dit_min 75 + #define qlf_dah_max 200 + #define qlf_dah_min 100 + #define qlf_on_by_default 0 +#endif //FEATURE_QLF + + +#ifdef FEATURE_WINKEY_EMULATION + #if defined(OPTION_WINKEY_UCXLOG_9600_BAUD) || defined(FEATURE_SO2R_BASE) + #define WINKEY_DEFAULT_BAUD 9600 + #else + #define WINKEY_DEFAULT_BAUD 1200 + #endif //OPTION_WINKEY_UCXLOG_9600_BAUD || FEATURE_SO2R_BASE +// alter these below to map alternate sidetones for Winkey interface protocol emulation +#ifdef OPTION_WINKEY_2_SUPPORT + #define WINKEY_SIDETONE_1 3759 + #define WINKEY_SIDETONE_2 1879 + #define WINKEY_SIDETONE_3 1252 + #define WINKEY_SIDETONE_4 940 + #define WINKEY_SIDETONE_5 752 + #define WINKEY_SIDETONE_6 625 + #define WINKEY_SIDETONE_7 535 + #define WINKEY_SIDETONE_8 469 + #define WINKEY_SIDETONE_9 417 + #define WINKEY_SIDETONE_10 375 +#else //OPTION_WINKEY_2_SUPPORT + #define WINKEY_SIDETONE_1 4000 + #define WINKEY_SIDETONE_2 2000 + #define WINKEY_SIDETONE_3 1333 + #define WINKEY_SIDETONE_4 1000 + #define WINKEY_SIDETONE_5 800 + #define WINKEY_SIDETONE_6 666 + #define WINKEY_SIDETONE_7 571 + #define WINKEY_SIDETONE_8 500 + #define WINKEY_SIDETONE_9 444 + #define WINKEY_SIDETONE_10 400 +#endif //OPTION_WINKEY_2_SUPPORT + +#define WINKEY_1_REPORT_VERSION_NUMBER 10 +#define WINKEY_2_REPORT_VERSION_NUMBER 23 + +// alter these to map to alternate hang time wordspace units +#define WINKEY_HANG_TIME_1_0 1.0 +#define WINKEY_HANG_TIME_1_33 1.33 +#define WINKEY_HANG_TIME_1_66 1.66 +#define WINKEY_HANG_TIME_2_0 2.0 + +#define WINKEY_RETURN_THIS_FOR_ADMIN_GET_CAL 0x16 +#define WINKEY_RETURN_THIS_FOR_ADMIN_PADDLE_A2D 0xEE +#define WINKEY_RETURN_THIS_FOR_ADMIN_SPEED_A2D 0x00 + +#endif //FEATURE_WINKEY_EMULATION + +#define PRIMARY_SERIAL_PORT &Serial +#define PRIMARY_SERIAL_PORT_BAUD 115200 // This applies only when the port is in Command Line Interface mode. In Winkey mode it will default to 1200. + +#ifdef FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + #define SECONDARY_SERIAL_PORT &Serial1 + #define SECONDARY_SERIAL_PORT_BAUD 115200 +#endif + + +#define CW_DECODER_SCREEN_COLUMNS 120 // word wrap at this many columns +#define CW_DECODER_SPACE_PRINT_THRESH 4.5 // print space if no tone for this many element lengths +#define CW_DECODER_SPACE_DECODE_THRESH 2.0 // invoke character decode if no tone for this many element lengths +#define CW_DECODER_NOISE_FILTER 20 // ignore elements shorter than this (mS) + +#define STRAIGHT_KEY_ACTIVE_STATE LOW + +#ifdef FEATURE_DYNAMIC_DAH_TO_DIT_RATIO + #define DYNAMIC_DAH_TO_DIT_RATIO_LOWER_LIMIT_WPM 30 + #define DYNAMIC_DAH_TO_DIT_RATIO_LOWER_LIMIT_RATIO 300 // 300 = 3:1 ratio + #define DYNAMIC_DAH_TO_DIT_RATIO_UPPER_LIMIT_WPM 70 + #define DYNAMIC_DAH_TO_DIT_RATIO_UPPER_LIMIT_RATIO 240 // 240 = 2.4:1 ratio +#endif //FEATURE_DYNAMIC_DAH_TO_DIT_RATIO + +#if defined(FEATURE_COMPETITION_COMPRESSION_DETECTION) + #define COMPETITION_COMPRESSION_DETECTION_ARRAY_SIZE 16 + #define COMPETITION_COMPRESSION_DETECTION_TIME_INTERCHAR_LOWER_LIMIT 1 + #define COMPETITION_COMPRESSION_DETECTION_TIME_INTERCHAR_UPPER_LIMIT 6.0 + #define COMPETITION_COMPRESSION_DETECTION_AVERAGE_ALARM_THRESHOLD 3.0 +#endif //FEATURE_COMPETITION_COMPRESSION_DETECTION + +#if defined(FEATURE_SLEEP) + #define KEYER_AWAKE_PIN_AWAKE_STATE HIGH + #define KEYER_AWAKE_PIN_ASLEEP_STATE LOW +#endif + +#if defined(FEATURE_LCD_BACKLIGHT_AUTO_DIM) + #define keyer_power_led_awake_duty 255 // PWM duty cycle. 0 is 0%, 255 is 100% + #define keyer_power_led_asleep_duty 25 // 25 is quite dim. Use 0 for off +#endif + +#if defined(FEATURE_ETHERNET) + // #define FEATURE_ETHERNET_IP {192,168,1,178} // default IP address ("192.168.1.178") + // #define FEATURE_ETHERNET_MAC {0xDE,0xAD,0xBE,0xEF,0xFE,0xED} + #define FEATURE_ETHERNET_IP {192,168,1,179} // default IP address ("192.168.1.179") + #define FEATURE_ETHERNET_MAC {0xDE,0xAD,0xBE,0xEF,0xFE,0xEE} + #define FEATURE_ETHERNET_DNS {8,8,8,8} + + #define FEATURE_ETHERNET_GATEWAY {192,168,1,1} // default gateway + #define FEATURE_ETHERNET_SUBNET_MASK {255,255,255,0} // default subnet mask + #define FEATURE_ETHERNET_WEB_LISTENER_PORT 80 + #define FEATURE_UDP_SEND_BUFFER_SIZE 128 + #define FEATURE_UDP_RECEIVE_BUFFER_SIZE 128 +#endif //FEATURE_ETHERNET + +#define WEB_SERVER_CONTROL_TX_KEY_TIME_LIMIT_SECS 10 + +#define FEATURE_INTERNET_LINK_MAX_LINKS 2 +#define FEATURE_INTERNET_LINK_DEFAULT_RCV_UDP_PORT 8888 +#define FEATURE_INTERNET_LINK_BUFFER_TIME_MS 500 +#define FEATURE_INTERNET_LINK_SVC_DURING_LOOP_TIME_MS 20 +#define FEATURE_INTERNET_LINK_KEY_DOWN_TIMEOUT_SECS 8 + +#if defined(FEATURE_4x4_KEYPAD) || defined(FEATURE_3x4_KEYPAD) + #define KEYPAD_ROWS 4 //KeyPad Rows + #if defined(FEATURE_4x4_KEYPAD) + #define KEYPAD_COLS 4 //keypad Columns + #else + #define KEYPAD_COLS 3 + #endif + #define mem1 0 //Memory numbers for Keypad: Actual memory numbers start with "0" + #define mem2 1 + #define mem3 2 + #define mem4 3 + #define mem5 4 + #define mem6 5 + #define mem7 6 + #define mem8 7 + #define mem9 8 + #define mem10 9 + #define mem11 10 + #define mem12 11 +#endif //#if defined(FEATURE_4x4_KEYPAD) || defined(FEATURE_3x4_KEYPAD) + +#define initial_sidetone_mode 1 // Sidetone mode, 0=OFF, 1=ON, 2=PADDLE_ONLY + +#define sd_card_spi_ss_line 4 + +#if defined(OPTION_DFROBOT_LCD_COMMAND_BUTTONS) + + // For V1.1 board use these values + #define dfrobot_btnRIGHT_analog 50 + #define dfrobot_btnUP_analog 250 + #define dfrobot_btnDOWN_analog 450 + #define dfrobot_btnLEFT_analog 650 + #define dfrobot_btnSELECT_analog 850 + + // For V1.0 board use these values + // #define dfrobot_btnRIGHT_analog 50 + // #define dfrobot_btnUP_analog 195 + // #define dfrobot_btnDOWN_analog 380 + // #define dfrobot_btnLEFT_analog 555 + // #define dfrobot_btnSELECT_analog 790 + + // button to memory mappings (0 = command button, 1 = memory 1, 2 = memory 2, etc.) + #define dfrobot_btnRIGHT 2 + #define dfrobot_btnUP 1 + #define dfrobot_btnDOWN 3 + #define dfrobot_btnLEFT 4 + #define dfrobot_btnSELECT 0 + #define dfrobot_btnNONE 255 // do not change + +#endif + +#define sequencer_pins_active_state HIGH +#define sequencer_pins_inactive_state LOW +#define ptt_line_active_state HIGH +#define ptt_line_inactive_state LOW +#define tx_key_line_active_state HIGH +#define tx_key_line_inactive_state LOW +#define ptt_input_pin_active_state LOW +#define ptt_input_pin_inactive_state HIGH +#define tx_inhibit_pin_active_state LOW +#define tx_inhibit_pin_inactive_state HIGH +#define tx_pause_pin_active_state LOW +#define tx_pause_pin_inactive_state HIGH +#define sidetone_line_active_state HIGH +#define sidetone_line_inactive_state LOW + +#if defined(ARDUINO_MAPLE_MINI) + #define button_value_factor 4095 //sp5iou contributed +#else + #define button_value_factor 1023 +#endif + +#define farnsworth_timing_calibration 1.15 + +#define sidetone_volume_low_limit 10 +#define sidetone_volume_high_limit 500 + +#define custom_startup_field "your custom text here" // an example could be callsign and name, eg. "AB1XYZ Bob", (or "Worlds best operator" which requires a 20 column display), string length shouldo be no more than the number of columns on the display + +#define command_mode_acknowledgement_character 'R' + +#if defined(FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT) + #define command_a_iambic_a "A" + #define command_b_iambic_b "B" + #define command_c_single_paddle "SINGLE" + #define command_d_ultimatic "ULT" + #define command_h_weight_dit_dah_ratio_default "R" + #define command_i_tx_on "TX ON" + #define command_i_tx_off "TX OFF" + #define command_k_dit_dah_buffers_on "ON" + #define command_k_dit_dah_buffers_off "OFF" + #define command_n_paddle_reverse "REV" + #define command_n_paddle_normal "NORM" + #define command_o_sidetone_off "ST OFF" + #define command_o_sidetone_paddle_only "ST PD ONLY" + #define command_o_sidetone_on "ST ON" + #define command_t_tune_mode "TUNE" + #define command_v_potentiometer_on "POT ON" + #define command_v_potentiometer_off "POT OFF" + + #define command_error "ERR" + +#endif //FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT diff --git a/k3ng_keyer/keyer_settings_fk_10.h b/k3ng_keyer/keyer_settings_fk_10.h new file mode 100755 index 0000000..8aca538 --- /dev/null +++ b/k3ng_keyer/keyer_settings_fk_10.h @@ -0,0 +1,338 @@ +// Initial and hardcoded settings + + +// ######## ## ## ## ## ## ## ######## ## ## ######## ######## +// ## ## ## ### ## ## ## ## ## ## ## ## ## +// ## ## ## #### ## ## ## ## #### ## ## ## +// ###### ## ## ## ## ## ##### ###### ## ###### ######## +// ## ## ## ## #### ## ## ## ## ## ## ## +// ## ## ## ## ### ## ## ## ## ## ## ## +// ## ####### ## ## ## ## ######## ## ######## ## ## + + +// ######## ## ## ## ##### +// ## ## ## #### ## ## +// ## ## ## ## ## ## +// ###### ##### ## ## ## +// ## ## ## ## ## ## +// ## ## ## ## ## ## +// ## ## ## ###### ##### + + + +#define initial_speed_wpm 26 // "factory default" keyer speed setting +#define initial_command_mode_speed_wpm 20 // "factory default" command mode speed setting +#define initial_sidetone_freq 600 // "factory default" sidetone frequency setting +#define sidetone_hz_limit_low 299 +#define sidetone_hz_limit_high 2001 +#define hz_high_beep 1500 // frequency in hertz of high beep +#define hz_low_beep 400 // frequency in hertz of low beep +#define initial_dah_to_dit_ratio 300 // 300 = 3 / normal 3:1 ratio +#define initial_ptt_lead_time_tx1 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx1 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx2 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx2 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx3 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx3 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx4 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx4 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx5 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx5 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx6 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx6 10 // PTT tail time in mS +#define initial_qrss_dit_length 1 // QRSS dit length in seconds +#define initial_pot_wpm_low_value 13 // Potentiometer WPM fully CCW +#define initial_pot_wpm_high_value 35 // Potentiometer WPM fully CW +#define wpm_limit_low 5 +#define wpm_limit_high 60 +#define potentiometer_change_threshold 0.9 // don't change the keyer speed until pot wpm has changed more than this +#define send_buffer_size 150 +#define default_length_letterspace 3 +#define default_length_wordspace 7 +#define default_keying_compensation 0 // number of milliseconds to extend all dits and dahs - for QSK on boatanchors +#define default_first_extension_time 0 // number of milliseconds to extend first sent dit or dah +#define default_pot_full_scale_reading 1023 +#define default_weighting 50 // 50 = weighting factor of 1 (normal) +#define default_ptt_hang_time_wordspace_units 0.0 +#define winkey_c0_wait_time 1 // the number of milliseconds to wait to send 0xc0 byte after send buffer has been sent +#define winkey_command_timeout_ms 5000 +#define winkey_discard_bytes_startup 3 // this is used if OPTION_WINKEY_DISCARD_BYTES_AT_STARTUP is enabled above +#define winkey_xoff_threshold 20 // the number of chars in the buffer when we begin sending XOFFs +#define winkey_xon_threshold 10 // the number of chars in the buffer below which we deactivate XOFF +#define default_memory_repeat_time 3000 // time in milliseconds +#define LCD_COLUMNS 16 +#define LCD_ROWS 2 +#define lcd_i2c_address_mathertel_PCF8574 0x27 // I2C address of display for FEATURE_LCD_MATHERTEL_PCF8574 +#define lcd_i2c_address_fdebrander_lcd 0x27 // I2C address of display for FEATURE_LCD_I2C_FDEBRABANDER +#define lcd_i2c_address_ydv1_lcd 0x27 // I2C address of display for FEATURE_LCD_YDv1 +//#define lcd_i2c_address_ydv1_lcd 0x38 // I2C address of display for FEATURE_LCD_YDv1 +#define lcd_i2c_address_sainsmart_lcd 0x27 // I2C address of display for FEATURE_LCD_SAINSMART_I2C +#define hell_pixel_microseconds 4025 +#define program_memory_limit_consec_spaces 1 +#define serial_leading_zeros 1 // set to 1 to activate leading zeros in serial numbers (i.e. #1 = 001) +#define serial_cut_numbers 0 // set to 1 to activate cut numbers in serial numbers (i.e. #10 = 1T, #19 = 1N) +#define go_to_sleep_inactivity_time 10 // minutes - FEATURE_SLEEP +#define dim_backlight_inactive_time 5 // minutes - FEATURE_LCD_BACKLIGHT_AUTO_DIM +#define default_cmos_super_keyer_iambic_b_timing_percent 33 // use with FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING; should be between 0 to 99 % (0% = true iambic b;100% = iambic a behavior) +#define default_cw_echo_timing_factor 1.75 // "factory default" setting +#define default_autospace_timing_factor 2.0 // "factory default" setting +#define winkey_paddle_echo_buffer_decode_timing_factor 0.25 +#define potentiometer_always_on 0 +#define ptt_interlock_check_every_ms 100 +#define ptt_interlock_active_state HIGH +#define unknown_cw_character '*' +#define cli_paddle_echo_on_at_boot 1 +#define cli_straight_key_echo_on_at_boot 1 +#define tx_key_dit_and_dah_pins_active_state HIGH +#define tx_key_dit_and_dah_pins_inactive_state LOW +#define potentiometer_check_interval_ms 150 +#define potentiometer_reading_threshold 1 +#define default_paddle_interruption_quiet_time_element_lengths 0 +#define default_wordsworth_wordspace 6 +#define default_wordsworth_repetition 1 +#define serial_program_memory_buffer_size 500 +#define eeprom_write_time_ms 30000 + +#ifdef FEATURE_BUTTONS + #define analog_buttons_number_of_buttons 4 + #define analog_buttons_r1 10 + #define analog_buttons_r2 1 +#endif + + +#if defined(FEATURE_BUTTONS) && !defined(FEATURE_PS2_KEYBOARD) && !defined(FEATURE_USB_KEYBOARD) && !defined(FEATURE_COMMAND_LINE_INTERFACE) && !defined(FEATURE_WINKEY_EMULATION) + #define number_of_memories byte(analog_buttons_number_of_buttons-1) +#else + #define number_of_memories byte(12) +#endif + +#ifdef FEATURE_CAPACITIVE_PADDLE_PINS + #define capacitance_threshold 2 +#endif //FEATURE_CAPACITIVE_PADDLE_PINS + +#ifdef FEATURE_LED_RING + #define led_ring_low_limit 10 + #define led_ring_high_limit 50 +#endif //FEATURE_LED_RING + +#ifdef FEATURE_QLF + #define qlf_dit_max 125 + #define qlf_dit_min 75 + #define qlf_dah_max 200 + #define qlf_dah_min 100 + #define qlf_on_by_default 0 +#endif //FEATURE_QLF + + +#ifdef FEATURE_WINKEY_EMULATION + #ifdef OPTION_WINKEY_UCXLOG_9600_BAUD + #define WINKEY_DEFAULT_BAUD 9600 + #else + #define WINKEY_DEFAULT_BAUD 1200 + #endif //OPTION_WINKEY_UCXLOG_9600_BAUD +// alter these below to map alternate sidetones for Winkey interface protocol emulation +#ifdef OPTION_WINKEY_2_SUPPORT + #define WINKEY_SIDETONE_1 3759 + #define WINKEY_SIDETONE_2 1879 + #define WINKEY_SIDETONE_3 1252 + #define WINKEY_SIDETONE_4 940 + #define WINKEY_SIDETONE_5 752 + #define WINKEY_SIDETONE_6 625 + #define WINKEY_SIDETONE_7 535 + #define WINKEY_SIDETONE_8 469 + #define WINKEY_SIDETONE_9 417 + #define WINKEY_SIDETONE_10 375 +#else //OPTION_WINKEY_2_SUPPORT + #define WINKEY_SIDETONE_1 4000 + #define WINKEY_SIDETONE_2 2000 + #define WINKEY_SIDETONE_3 1333 + #define WINKEY_SIDETONE_4 1000 + #define WINKEY_SIDETONE_5 800 + #define WINKEY_SIDETONE_6 666 + #define WINKEY_SIDETONE_7 571 + #define WINKEY_SIDETONE_8 500 + #define WINKEY_SIDETONE_9 444 + #define WINKEY_SIDETONE_10 400 +#endif //OPTION_WINKEY_2_SUPPORT + +#define WINKEY_1_REPORT_VERSION_NUMBER 10 +#define WINKEY_2_REPORT_VERSION_NUMBER 23 + +// alter these to map to alternate hang time wordspace units +#define WINKEY_HANG_TIME_1_0 1.0 +#define WINKEY_HANG_TIME_1_33 1.33 +#define WINKEY_HANG_TIME_1_66 1.66 +#define WINKEY_HANG_TIME_2_0 2.0 + +#define WINKEY_RETURN_THIS_FOR_ADMIN_GET_CAL 0x16 +#define WINKEY_RETURN_THIS_FOR_ADMIN_PADDLE_A2D 0xEE +#define WINKEY_RETURN_THIS_FOR_ADMIN_SPEED_A2D 0x00 + +#endif //FEATURE_WINKEY_EMULATION + + + +#define PRIMARY_SERIAL_PORT &Serial +#define PRIMARY_SERIAL_PORT_BAUD 115200 // This applies only when the port is in Command Line Interface mode. In Winkey mode it will default to 1200. + +#ifdef FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + #define SECONDARY_SERIAL_PORT &Serial1 + #define SECONDARY_SERIAL_PORT_BAUD 115200 +#endif + + +#define CW_DECODER_SCREEN_COLUMNS 120 // word wrap at this many columns +#define CW_DECODER_SPACE_PRINT_THRESH 4.5 // print space if no tone for this many element lengths +#define CW_DECODER_SPACE_DECODE_THRESH 2.0 // invoke character decode if no tone for this many element lengths +#define CW_DECODER_NOISE_FILTER 20 // ignore elements shorter than this (mS) + +#define STRAIGHT_KEY_ACTIVE_STATE LOW + +#ifdef FEATURE_DYNAMIC_DAH_TO_DIT_RATIO + #define DYNAMIC_DAH_TO_DIT_RATIO_LOWER_LIMIT_WPM 30 + #define DYNAMIC_DAH_TO_DIT_RATIO_LOWER_LIMIT_RATIO 300 // 300 = 3:1 ratio + #define DYNAMIC_DAH_TO_DIT_RATIO_UPPER_LIMIT_WPM 70 + #define DYNAMIC_DAH_TO_DIT_RATIO_UPPER_LIMIT_RATIO 240 // 240 = 2.4:1 ratio +#endif //FEATURE_DYNAMIC_DAH_TO_DIT_RATIO + +#if defined(FEATURE_COMPETITION_COMPRESSION_DETECTION) + #define COMPETITION_COMPRESSION_DETECTION_ARRAY_SIZE 16 + #define COMPETITION_COMPRESSION_DETECTION_TIME_INTERCHAR_LOWER_LIMIT 1 + #define COMPETITION_COMPRESSION_DETECTION_TIME_INTERCHAR_UPPER_LIMIT 6.0 + #define COMPETITION_COMPRESSION_DETECTION_AVERAGE_ALARM_THRESHOLD 3.0 +#endif //FEATURE_COMPETITION_COMPRESSION_DETECTION + +#if defined(FEATURE_SLEEP) + #define KEYER_AWAKE_PIN_AWAKE_STATE HIGH + #define KEYER_AWAKE_PIN_ASLEEP_STATE LOW +#endif + +#if defined(FEATURE_LCD_BACKLIGHT_AUTO_DIM) + #define keyer_power_led_awake_duty 255 // PWM duty cycle. 0 is 0%, 255 is 100% + #define keyer_power_led_asleep_duty 25 // 25 is quite dim. Use 0 for off +#endif + +#if defined(FEATURE_ETHERNET) + // #define FEATURE_ETHERNET_IP {192,168,1,178} // default IP address ("192.168.1.178") + // #define FEATURE_ETHERNET_MAC {0xDE,0xAD,0xBE,0xEF,0xFE,0xED} + #define FEATURE_ETHERNET_IP {192,168,1,179} // default IP address ("192.168.1.178") + #define FEATURE_ETHERNET_MAC {0xDE,0xAD,0xBE,0xEF,0xFE,0xEE} + #define FEATURE_ETHERNET_DNS {8,8,8,8} + + #define FEATURE_ETHERNET_GATEWAY {192,168,1,1} // default gateway + #define FEATURE_ETHERNET_SUBNET_MASK {255,255,255,0} // default subnet mask + #define FEATURE_ETHERNET_WEB_LISTENER_PORT 80 + #define FEATURE_UDP_SEND_BUFFER_SIZE 128 + #define FEATURE_UDP_RECEIVE_BUFFER_SIZE 128 +#endif //FEATURE_ETHERNET + +#define WEB_SERVER_CONTROL_TX_KEY_TIME_LIMIT_SECS 10 + +#define FEATURE_INTERNET_LINK_MAX_LINKS 2 +#define FEATURE_INTERNET_LINK_DEFAULT_RCV_UDP_PORT 8888 +#define FEATURE_INTERNET_LINK_BUFFER_TIME_MS 500 + +#if defined(FEATURE_4x4_KEYPAD)|| defined (FEATURE_3x4_KEYPAD) + #define KEYPAD_ROWS 4 //KeyPad Rows + #if defined(FEATURE_4x4_KEYPAD) + #define KEYPAD_COLS 4 //keypad Columns + #else + #define KEYPAD_COLS 3 + #endif + #define mem1 0 //Memory numbers for Keypad: Actual memory numbers start with "0" + #define mem2 1 + #define mem3 2 + #define mem4 3 + #define mem5 4 + #define mem6 5 + #define mem7 6 + #define mem8 7 + #define mem9 8 + #define mem10 9 + #define mem11 10 + #define mem12 11 +#endif //#if defined(FEATURE_4x4_KEYPAD)|| defined (FEATURE_3x4_KEYPAD) + +#define initial_sidetone_mode 1 // Sidetone mode, 0=OFF, 1=ON, 2=PADDLE_ONLY + +#define sd_card_spi_ss_line 4 + +#if defined(OPTION_DFROBOT_LCD_COMMAND_BUTTONS) + + // For V1.1 board use these values + #define dfrobot_btnRIGHT_analog 50 + #define dfrobot_btnUP_analog 250 + #define dfrobot_btnDOWN_analog 450 + #define dfrobot_btnLEFT_analog 650 + #define dfrobot_btnSELECT_analog 850 + + // For V1.0 board use these values + // #define dfrobot_btnRIGHT_analog 50 + // #define dfrobot_btnUP_analog 195 + // #define dfrobot_btnDOWN_analog 380 + // #define dfrobot_btnLEFT_analog 555 + // #define dfrobot_btnSELECT_analog 790 + + // button to memory mappings (0 = command button, 1 = memory 1, 2 = memory 2, etc.) + #define dfrobot_btnRIGHT 2 + #define dfrobot_btnUP 1 + #define dfrobot_btnDOWN 3 + #define dfrobot_btnLEFT 4 + #define dfrobot_btnSELECT 0 + #define dfrobot_btnNONE 255 // do not change + +#endif + +#define sequencer_pins_active_state HIGH +#define sequencer_pins_inactive_state LOW +#define ptt_line_active_state HIGH +#define ptt_line_inactive_state LOW +#define tx_key_line_active_state HIGH +#define tx_key_line_inactive_state LOW +#define ptt_input_pin_active_state LOW +#define ptt_input_pin_inactive_state HIGH +#define tx_inhibit_pin_active_state LOW +#define tx_inhibit_pin_inactive_state HIGH +#define tx_pause_pin_active_state LOW +#define tx_pause_pin_inactive_state HIGH +#define sidetone_line_active_state HIGH +#define sidetone_line_inactive_state LOW + +#if defined(ARDUINO_MAPLE_MINI) + #define button_value_factor 4095 //sp5iou contributed +#else + #define button_value_factor 1023 +#endif + +#define farnsworth_timing_calibration 1.15 + +#define sidetone_volume_low_limit 10 +#define sidetone_volume_high_limit 500 + +#define custom_startup_field "your custom text here" // an example could be callsign and name, eg. "AB1XYZ Bob", (or "Worlds best operator" which requires a 20 column display), string length shouldo be no more than the number of columns on the display + +#define command_mode_acknowledgement_character 'E' + +#if defined(FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT) + #define command_a_iambic_a "A" + #define command_b_iambic_b "B" + #define command_c_single_paddle "SINGLE" + #define command_d_ultimatic "ULT" + #define command_h_weight_dit_dah_ratio_default "R" + #define command_i_tx_on "TX ON" + #define command_i_tx_off "TX OFF" + #define command_k_dit_dah_buffers_on "ON" + #define command_k_dit_dah_buffers_off "OFF" + #define command_n_paddle_reverse "REV" + #define command_n_paddle_normal "NORM" + #define command_o_sidetone_off "ST OFF" + #define command_o_sidetone_paddle_only "ST PD ONLY" + #define command_o_sidetone_on "ST ON" + #define command_t_tune_mode "TUNE" + #define command_v_potentiometer_on "POT ON" + #define command_v_potentiometer_off "POT OFF" + + #define command_error "ERR" + +#endif //FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT + diff --git a/k3ng_keyer/keyer_settings_fk_11.h b/k3ng_keyer/keyer_settings_fk_11.h new file mode 100755 index 0000000..dcdba63 --- /dev/null +++ b/k3ng_keyer/keyer_settings_fk_11.h @@ -0,0 +1,318 @@ +// Funtronics FK-11 + +// Initial and hardcoded settings +#define initial_speed_wpm 26 // "factory default" keyer speed setting +#define initial_command_mode_speed_wpm 20 // "factory default" command mode speed setting +#define initial_sidetone_freq 600 // "factory default" sidetone frequency setting +#define sidetone_hz_limit_low 299 +#define sidetone_hz_limit_high 2001 +#define hz_high_beep 1500 // frequency in hertz of high beep +#define hz_low_beep 400 // frequency in hertz of low beep +#define initial_dah_to_dit_ratio 300 // 300 = 3 / normal 3:1 ratio +#define initial_ptt_lead_time_tx1 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx1 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx2 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx2 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx3 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx3 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx4 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx4 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx5 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx5 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx6 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx6 10 // PTT tail time in mS +#define initial_qrss_dit_length 1 // QRSS dit length in seconds +#define initial_pot_wpm_low_value 13 // Potentiometer WPM fully CCW +#define initial_pot_wpm_high_value 35 // Potentiometer WPM fully CW +#define wpm_limit_low 5 +#define wpm_limit_high 60 +#define potentiometer_change_threshold 0.9 // don't change the keyer speed until pot wpm has changed more than this +#define send_buffer_size 150 +#define default_length_letterspace 3 +#define default_length_wordspace 7 +#define default_keying_compensation 0 // number of milliseconds to extend all dits and dahs - for QSK on boatanchors +#define default_first_extension_time 0 // number of milliseconds to extend first sent dit or dah +#define default_pot_full_scale_reading 1023 +#define default_weighting 50 // 50 = weighting factor of 1 (normal) +#define default_ptt_hang_time_wordspace_units 0.0 +#define winkey_c0_wait_time 1 // the number of milliseconds to wait to send 0xc0 byte after send buffer has been sent +#define winkey_command_timeout_ms 5000 +#define winkey_discard_bytes_startup 3 // this is used if OPTION_WINKEY_DISCARD_BYTES_AT_STARTUP is enabled above +#define winkey_xoff_threshold 20 // the number of chars in the buffer when we begin sending XOFFs +#define winkey_xon_threshold 10 // the number of chars in the buffer below which we deactivate XOFF +#define default_memory_repeat_time 3000 // time in milliseconds +#define LCD_COLUMNS 16 +#define LCD_ROWS 2 +#define lcd_i2c_address_mathertel_PCF8574 0x27 // I2C address of display for FEATURE_LCD_MATHERTEL_PCF8574 +#define lcd_i2c_address_fdebrander_lcd 0x27 // I2C address of display for FEATURE_LCD_I2C_FDEBRABANDER +#define lcd_i2c_address_ydv1_lcd 0x27 // I2C address of display for FEATURE_LCD_YDv1 +//#define lcd_i2c_address_ydv1_lcd 0x38 // I2C address of display for FEATURE_LCD_YDv1 +#define lcd_i2c_address_sainsmart_lcd 0x27 // I2C address of display for FEATURE_LCD_SAINSMART_I2C +#define hell_pixel_microseconds 4025 +#define program_memory_limit_consec_spaces 1 +#define serial_leading_zeros 1 // set to 1 to activate leading zeros in serial numbers (i.e. #1 = 001) +#define serial_cut_numbers 0 // set to 1 to activate cut numbers in serial numbers (i.e. #10 = 1T, #19 = 1N) +#define go_to_sleep_inactivity_time 10 // minutes - FEATURE_SLEEP +#define dim_backlight_inactive_time 5 // minutes - FEATURE_LCD_BACKLIGHT_AUTO_DIM +#define default_cmos_super_keyer_iambic_b_timing_percent 33 // use with FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING; should be between 0 to 99 % (0% = true iambic b;100% = iambic a behavior) +#define default_cw_echo_timing_factor 1.75 // "factory default" setting +#define default_autospace_timing_factor 2.0 // "factory default" setting +#define winkey_paddle_echo_buffer_decode_timing_factor 0.25 +#define potentiometer_always_on 0 +#define ptt_interlock_check_every_ms 100 +#define ptt_interlock_active_state HIGH +#define unknown_cw_character '*' +#define cli_paddle_echo_on_at_boot 1 +#define cli_straight_key_echo_on_at_boot 1 +#define tx_key_dit_and_dah_pins_active_state HIGH +#define tx_key_dit_and_dah_pins_inactive_state LOW +#define potentiometer_check_interval_ms 150 +#define potentiometer_reading_threshold 1 +#define default_paddle_interruption_quiet_time_element_lengths 0 +#define default_wordsworth_wordspace 6 +#define default_wordsworth_repetition 1 +#define serial_program_memory_buffer_size 500 +#define eeprom_write_time_ms 30000 + +#ifdef FEATURE_BUTTONS + #define analog_buttons_number_of_buttons 5 // includes the command button (command button + 3 memory buttons = 4) + #define analog_buttons_r1 10 + #define analog_buttons_r2 1 +#endif + + +#if defined(FEATURE_BUTTONS) && !defined(FEATURE_PS2_KEYBOARD) && !defined(FEATURE_USB_KEYBOARD) && !defined(FEATURE_COMMAND_LINE_INTERFACE) && !defined(FEATURE_WINKEY_EMULATION) + #define number_of_memories byte(analog_buttons_number_of_buttons-1) +#else + #define number_of_memories byte(12) +#endif + +#ifdef FEATURE_CAPACITIVE_PADDLE_PINS + #define capacitance_threshold 2 +#endif //FEATURE_CAPACITIVE_PADDLE_PINS + +#ifdef FEATURE_LED_RING + #define led_ring_low_limit 10 + #define led_ring_high_limit 50 +#endif //FEATURE_LED_RING + +#ifdef FEATURE_QLF + #define qlf_dit_max 125 + #define qlf_dit_min 75 + #define qlf_dah_max 200 + #define qlf_dah_min 100 + #define qlf_on_by_default 0 +#endif //FEATURE_QLF + + +#ifdef FEATURE_WINKEY_EMULATION + #ifdef OPTION_WINKEY_UCXLOG_9600_BAUD || defined(FEATURE_SO2R_BASE) + #define WINKEY_DEFAULT_BAUD 9600 + #else + #define WINKEY_DEFAULT_BAUD 1200 + #endif //OPTION_WINKEY_UCXLOG_9600_BAUD || FEATURE_SO2R_BASE +// alter these below to map alternate sidetones for Winkey interface protocol emulation +#ifdef OPTION_WINKEY_2_SUPPORT + #define WINKEY_SIDETONE_1 3759 + #define WINKEY_SIDETONE_2 1879 + #define WINKEY_SIDETONE_3 1252 + #define WINKEY_SIDETONE_4 940 + #define WINKEY_SIDETONE_5 752 + #define WINKEY_SIDETONE_6 625 + #define WINKEY_SIDETONE_7 535 + #define WINKEY_SIDETONE_8 469 + #define WINKEY_SIDETONE_9 417 + #define WINKEY_SIDETONE_10 375 +#else //OPTION_WINKEY_2_SUPPORT + #define WINKEY_SIDETONE_1 4000 + #define WINKEY_SIDETONE_2 2000 + #define WINKEY_SIDETONE_3 1333 + #define WINKEY_SIDETONE_4 1000 + #define WINKEY_SIDETONE_5 800 + #define WINKEY_SIDETONE_6 666 + #define WINKEY_SIDETONE_7 571 + #define WINKEY_SIDETONE_8 500 + #define WINKEY_SIDETONE_9 444 + #define WINKEY_SIDETONE_10 400 +#endif //OPTION_WINKEY_2_SUPPORT + +#define WINKEY_1_REPORT_VERSION_NUMBER 10 +#define WINKEY_2_REPORT_VERSION_NUMBER 23 + +// alter these to map to alternate hang time wordspace units +#define WINKEY_HANG_TIME_1_0 1.0 +#define WINKEY_HANG_TIME_1_33 1.33 +#define WINKEY_HANG_TIME_1_66 1.66 +#define WINKEY_HANG_TIME_2_0 2.0 + +#define WINKEY_RETURN_THIS_FOR_ADMIN_GET_CAL 0x16 +#define WINKEY_RETURN_THIS_FOR_ADMIN_PADDLE_A2D 0xEE +#define WINKEY_RETURN_THIS_FOR_ADMIN_SPEED_A2D 0x00 + +#endif //FEATURE_WINKEY_EMULATION + + + +#define PRIMARY_SERIAL_PORT &Serial +#define PRIMARY_SERIAL_PORT_BAUD 115200 // This applies only when the port is in Command Line Interface mode. In Winkey mode it will default to 1200. + +#ifdef FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + #define SECONDARY_SERIAL_PORT &Serial1 + #define SECONDARY_SERIAL_PORT_BAUD 115200 +#endif + + +#define CW_DECODER_SCREEN_COLUMNS 120 // word wrap at this many columns +#define CW_DECODER_SPACE_PRINT_THRESH 4.5 // print space if no tone for this many element lengths +#define CW_DECODER_SPACE_DECODE_THRESH 2.0 // invoke character decode if no tone for this many element lengths +#define CW_DECODER_NOISE_FILTER 20 // ignore elements shorter than this (mS) + +#define STRAIGHT_KEY_ACTIVE_STATE LOW + +#ifdef FEATURE_DYNAMIC_DAH_TO_DIT_RATIO + #define DYNAMIC_DAH_TO_DIT_RATIO_LOWER_LIMIT_WPM 30 + #define DYNAMIC_DAH_TO_DIT_RATIO_LOWER_LIMIT_RATIO 300 // 300 = 3:1 ratio + #define DYNAMIC_DAH_TO_DIT_RATIO_UPPER_LIMIT_WPM 70 + #define DYNAMIC_DAH_TO_DIT_RATIO_UPPER_LIMIT_RATIO 240 // 240 = 2.4:1 ratio +#endif //FEATURE_DYNAMIC_DAH_TO_DIT_RATIO + +#if defined(FEATURE_COMPETITION_COMPRESSION_DETECTION) + #define COMPETITION_COMPRESSION_DETECTION_ARRAY_SIZE 16 + #define COMPETITION_COMPRESSION_DETECTION_TIME_INTERCHAR_LOWER_LIMIT 1 + #define COMPETITION_COMPRESSION_DETECTION_TIME_INTERCHAR_UPPER_LIMIT 6.0 + #define COMPETITION_COMPRESSION_DETECTION_AVERAGE_ALARM_THRESHOLD 3.0 +#endif //FEATURE_COMPETITION_COMPRESSION_DETECTION + +#if defined(FEATURE_SLEEP) + #define KEYER_AWAKE_PIN_AWAKE_STATE HIGH + #define KEYER_AWAKE_PIN_ASLEEP_STATE LOW +#endif + +#if defined(FEATURE_LCD_BACKLIGHT_AUTO_DIM) + #define keyer_power_led_awake_duty 255 // PWM duty cycle. 0 is 0%, 255 is 100% + #define keyer_power_led_asleep_duty 25 // 25 is quite dim. Use 0 for off +#endif + +#if defined(FEATURE_ETHERNET) + // #define FEATURE_ETHERNET_IP {192,168,1,178} // default IP address ("192.168.1.178") + // #define FEATURE_ETHERNET_MAC {0xDE,0xAD,0xBE,0xEF,0xFE,0xED} + #define FEATURE_ETHERNET_IP {192,168,1,179} // default IP address ("192.168.1.179") + #define FEATURE_ETHERNET_MAC {0xDE,0xAD,0xBE,0xEF,0xFE,0xEE} + #define FEATURE_ETHERNET_DNS {8,8,8,8} + + #define FEATURE_ETHERNET_GATEWAY {192,168,1,1} // default gateway + #define FEATURE_ETHERNET_SUBNET_MASK {255,255,255,0} // default subnet mask + #define FEATURE_ETHERNET_WEB_LISTENER_PORT 80 + #define FEATURE_UDP_SEND_BUFFER_SIZE 128 + #define FEATURE_UDP_RECEIVE_BUFFER_SIZE 128 +#endif //FEATURE_ETHERNET + +#define WEB_SERVER_CONTROL_TX_KEY_TIME_LIMIT_SECS 10 + +#define FEATURE_INTERNET_LINK_MAX_LINKS 2 +#define FEATURE_INTERNET_LINK_DEFAULT_RCV_UDP_PORT 8888 +#define FEATURE_INTERNET_LINK_BUFFER_TIME_MS 500 + +#if defined(FEATURE_4x4_KEYPAD)|| defined (FEATURE_3x4_KEYPAD) + #define KEYPAD_ROWS 4 //KeyPad Rows + #if defined(FEATURE_4x4_KEYPAD) + #define KEYPAD_COLS 4 //keypad Columns + #else + #define KEYPAD_COLS 3 + #endif + #define mem1 0 //Memory numbers for Keypad: Actual memory numbers start with "0" + #define mem2 1 + #define mem3 2 + #define mem4 3 + #define mem5 4 + #define mem6 5 + #define mem7 6 + #define mem8 7 + #define mem9 8 + #define mem10 9 + #define mem11 10 + #define mem12 11 +#endif //#if defined(FEATURE_4x4_KEYPAD)|| defined (FEATURE_3x4_KEYPAD) + +#define initial_sidetone_mode 1 // Sidetone mode, 0=OFF, 1=ON, 2=PADDLE_ONLY + +#define sd_card_spi_ss_line 4 + +#if defined(OPTION_DFROBOT_LCD_COMMAND_BUTTONS) + + // For V1.1 board use these values + #define dfrobot_btnRIGHT_analog 50 + #define dfrobot_btnUP_analog 250 + #define dfrobot_btnDOWN_analog 450 + #define dfrobot_btnLEFT_analog 650 + #define dfrobot_btnSELECT_analog 850 + + // For V1.0 board use these values + // #define dfrobot_btnRIGHT_analog 50 + // #define dfrobot_btnUP_analog 195 + // #define dfrobot_btnDOWN_analog 380 + // #define dfrobot_btnLEFT_analog 555 + // #define dfrobot_btnSELECT_analog 790 + + // button to memory mappings (0 = command button, 1 = memory 1, 2 = memory 2, etc.) + #define dfrobot_btnRIGHT 2 + #define dfrobot_btnUP 1 + #define dfrobot_btnDOWN 3 + #define dfrobot_btnLEFT 4 + #define dfrobot_btnSELECT 0 + #define dfrobot_btnNONE 255 // do not change + +#endif + +#define sequencer_pins_active_state HIGH +#define sequencer_pins_inactive_state LOW +#define ptt_line_active_state HIGH +#define ptt_line_inactive_state LOW +#define tx_key_line_active_state HIGH +#define tx_key_line_inactive_state LOW +#define ptt_input_pin_active_state LOW +#define ptt_input_pin_inactive_state HIGH +#define tx_inhibit_pin_active_state LOW +#define tx_inhibit_pin_inactive_state HIGH +#define tx_pause_pin_active_state LOW +#define tx_pause_pin_inactive_state HIGH +#define sidetone_line_active_state HIGH +#define sidetone_line_inactive_state LOW + +#if defined(ARDUINO_MAPLE_MINI) + #define button_value_factor 4095 //sp5iou contributed +#else + #define button_value_factor 1023 +#endif + +#define farnsworth_timing_calibration 1.15 + +#define sidetone_volume_low_limit 10 +#define sidetone_volume_high_limit 500 + +#define custom_startup_field "your custom text here" // an example could be callsign and name, eg. "AB1XYZ Bob", (or "Worlds best operator" which requires a 20 column display), string length shouldo be no more than the number of columns on the display + +#define command_mode_acknowledgement_character 'E' + +#if defined(FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT) + #define command_a_iambic_a "A" + #define command_b_iambic_b "B" + #define command_c_single_paddle "SINGLE" + #define command_d_ultimatic "ULT" + #define command_h_weight_dit_dah_ratio_default "R" + #define command_i_tx_on "TX ON" + #define command_i_tx_off "TX OFF" + #define command_k_dit_dah_buffers_on "ON" + #define command_k_dit_dah_buffers_off "OFF" + #define command_n_paddle_reverse "REV" + #define command_n_paddle_normal "NORM" + #define command_o_sidetone_off "ST OFF" + #define command_o_sidetone_paddle_only "ST PD ONLY" + #define command_o_sidetone_on "ST ON" + #define command_t_tune_mode "TUNE" + #define command_v_potentiometer_on "POT ON" + #define command_v_potentiometer_off "POT OFF" + + #define command_error "ERR" + +#endif //FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT diff --git a/k3ng_keyer/keyer_settings_generic_STM32F103C.h b/k3ng_keyer/keyer_settings_generic_STM32F103C.h new file mode 100755 index 0000000..9e1c129 --- /dev/null +++ b/k3ng_keyer/keyer_settings_generic_STM32F103C.h @@ -0,0 +1,324 @@ +/* +GENERIC STM32F103C + + +*/ + +// Initial and hardcoded settings +#define initial_speed_wpm 26 // "factory default" keyer speed setting +#define initial_command_mode_speed_wpm 20 // "factory default" command mode speed setting +#define initial_sidetone_freq 600 // "factory default" sidetone frequency setting +#define sidetone_hz_limit_low 299 +#define sidetone_hz_limit_high 2001 +#define hz_high_beep 1500 // frequency in hertz of high beep +#define hz_low_beep 400 // frequency in hertz of low beep +#define initial_dah_to_dit_ratio 300 // 300 = 3 / normal 3:1 ratio +#define initial_ptt_lead_time_tx1 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx1 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx2 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx2 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx3 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx3 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx4 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx4 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx5 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx5 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx6 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx6 10 // PTT tail time in mS +#define initial_qrss_dit_length 1 // QRSS dit length in seconds +#define initial_pot_wpm_low_value 13 // Potentiometer WPM fully CCW +#define initial_pot_wpm_high_value 35 // Potentiometer WPM fully CW +#define wpm_limit_low 5 +#define wpm_limit_high 60 +#define potentiometer_change_threshold 0.9 // don't change the keyer speed until pot wpm has changed more than this +#define send_buffer_size 150 +#define default_length_letterspace 3 +#define default_length_wordspace 7 +#define default_keying_compensation 0 // number of milliseconds to extend all dits and dahs - for QSK on boatanchors +#define default_first_extension_time 0 // number of milliseconds to extend first sent dit or dah +//#define default_pot_full_scale_reading 1023 //For AVR boards +#define default_pot_full_scale_reading 4095 //SP5IOU 20180329 FOR stm32 Boards. +#define default_weighting 50 // 50 = weighting factor of 1 (normal) +#define default_ptt_hang_time_wordspace_units 0.0 +#define winkey_c0_wait_time 1 // the number of milliseconds to wait to send 0xc0 byte after send buffer has been sent +#define winkey_command_timeout_ms 5000 +#define winkey_discard_bytes_startup 3 // this is used if OPTION_WINKEY_DISCARD_BYTES_AT_STARTUP is enabled above +#define winkey_xoff_threshold 20 // the number of chars in the buffer when we begin sending XOFFs +#define winkey_xon_threshold 10 // the number of chars in the buffer below which we deactivate XOFF +#define default_memory_repeat_time 3000 // time in milliseconds +#define LCD_COLUMNS 16 +#define LCD_ROWS 2 +#define lcd_i2c_address_mathertel_PCF8574 0x27 // I2C address of display for FEATURE_LCD_MATHERTEL_PCF8574 +#define lcd_i2c_address_fdebrander_lcd 0x27 // I2C address of display for FEATURE_LCD_I2C_FDEBRABANDER +#define lcd_i2c_address_ydv1_lcd 0x27 // I2C address of display for FEATURE_LCD_YDv1 +//#define lcd_i2c_address_ydv1_lcd 0x38 // I2C address of display for FEATURE_LCD_YDv1 +#define lcd_i2c_address_sainsmart_lcd 0x27 // I2C address of display for FEATURE_LCD_SAINSMART_I2C +#define hell_pixel_microseconds 4025 +#define program_memory_limit_consec_spaces 1 +#define serial_leading_zeros 1 // set to 1 to activate leading zeros in serial numbers (i.e. #1 = 001) +#define serial_cut_numbers 0 // set to 1 to activate cut numbers in serial numbers (i.e. #10 = 1T, #19 = 1N) +#define go_to_sleep_inactivity_time 10 // minutes - FEATURE_SLEEP +#define dim_backlight_inactive_time 5 // minutes - FEATURE_LCD_BACKLIGHT_AUTO_DIM +#define default_cmos_super_keyer_iambic_b_timing_percent 33 // use with FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING; should be between 0 to 99 % (0% = true iambic b;100% = iambic a behavior) +#define default_cw_echo_timing_factor 1.75 // "factory default" setting +#define default_autospace_timing_factor 2.0 // "factory default" setting +#define winkey_paddle_echo_buffer_decode_timing_factor 0.25 +#define potentiometer_always_on 0 +#define ptt_interlock_check_every_ms 100 +#define ptt_interlock_active_state HIGH +#define unknown_cw_character '*' +#define cli_paddle_echo_on_at_boot 1 +#define cli_straight_key_echo_on_at_boot 1 +#define tx_key_dit_and_dah_pins_active_state HIGH +#define tx_key_dit_and_dah_pins_inactive_state LOW +#define potentiometer_check_interval_ms 150 +#define potentiometer_reading_threshold 1 +#define default_paddle_interruption_quiet_time_element_lengths 0 +#define default_wordsworth_wordspace 6 +#define default_wordsworth_repetition 1 +#define serial_program_memory_buffer_size 500 +#define eeprom_write_time_ms 30000 + +#ifdef FEATURE_BUTTONS + #define analog_buttons_number_of_buttons 1 // For Keypad only command butrton is neaded 6 //4 // includes the command button (command button + 3 memory buttons = 4) //sp5iou 20180319 + #define analog_buttons_r1 10 + #define analog_buttons_r2 1 +#endif + + +#if defined(FEATURE_BUTTONS) && !defined(FEATURE_PS2_KEYBOARD) && !defined(FEATURE_USB_KEYBOARD) && !defined(FEATURE_COMMAND_LINE_INTERFACE) && !defined(FEATURE_WINKEY_EMULATION) + #define number_of_memories byte(analog_buttons_number_of_buttons-1) +#else +// #define number_of_memories byte(12) + #define number_of_memories byte(10) //sp5iou 20180329 With many memories, be carefull to not put to much content. It caould disable memory programming and eeprom formatting is then necessary +#endif + +#ifdef FEATURE_CAPACITIVE_PADDLE_PINS + #define capacitance_threshold 2 +#endif //FEATURE_CAPACITIVE_PADDLE_PINS + +#ifdef FEATURE_LED_RING + #define led_ring_low_limit 10 + #define led_ring_high_limit 50 +#endif //FEATURE_LED_RING + +#ifdef FEATURE_QLF + #define qlf_dit_max 125 + #define qlf_dit_min 75 + #define qlf_dah_max 200 + #define qlf_dah_min 100 + #define qlf_on_by_default 0 +#endif //FEATURE_QLF + + +#ifdef FEATURE_WINKEY_EMULATION + #ifdef OPTION_WINKEY_UCXLOG_9600_BAUD + #define WINKEY_DEFAULT_BAUD 9600 + #else + #define WINKEY_DEFAULT_BAUD 1200 + #endif //OPTION_WINKEY_UCXLOG_9600_BAUD +// alter these below to map alternate sidetones for Winkey interface protocol emulation +#ifdef OPTION_WINKEY_2_SUPPORT + #define WINKEY_SIDETONE_1 3759 + #define WINKEY_SIDETONE_2 1879 + #define WINKEY_SIDETONE_3 1252 + #define WINKEY_SIDETONE_4 940 + #define WINKEY_SIDETONE_5 752 + #define WINKEY_SIDETONE_6 625 + #define WINKEY_SIDETONE_7 535 + #define WINKEY_SIDETONE_8 469 + #define WINKEY_SIDETONE_9 417 + #define WINKEY_SIDETONE_10 375 +#else //OPTION_WINKEY_2_SUPPORT + #define WINKEY_SIDETONE_1 4000 + #define WINKEY_SIDETONE_2 2000 + #define WINKEY_SIDETONE_3 1333 + #define WINKEY_SIDETONE_4 1000 + #define WINKEY_SIDETONE_5 800 + #define WINKEY_SIDETONE_6 666 + #define WINKEY_SIDETONE_7 571 + #define WINKEY_SIDETONE_8 500 + #define WINKEY_SIDETONE_9 444 + #define WINKEY_SIDETONE_10 400 +#endif //OPTION_WINKEY_2_SUPPORT + +#define WINKEY_1_REPORT_VERSION_NUMBER 10 +#define WINKEY_2_REPORT_VERSION_NUMBER 23 + +// alter these to map to alternate hang time wordspace units +#define WINKEY_HANG_TIME_1_0 1.0 +#define WINKEY_HANG_TIME_1_33 1.33 +#define WINKEY_HANG_TIME_1_66 1.66 +#define WINKEY_HANG_TIME_2_0 2.0 + +#define WINKEY_RETURN_THIS_FOR_ADMIN_GET_CAL 0x16 +#define WINKEY_RETURN_THIS_FOR_ADMIN_PADDLE_A2D 0xEE +#define WINKEY_RETURN_THIS_FOR_ADMIN_SPEED_A2D 0x00 + +#endif //FEATURE_WINKEY_EMULATION + + + +#define PRIMARY_SERIAL_PORT &Serial +#define PRIMARY_SERIAL_PORT_BAUD 115200 // This applies only when the port is in Command Line Interface mode. In Winkey mode it will default to 1200. + +#ifdef FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + #define SECONDARY_SERIAL_PORT &Serial1 + #define SECONDARY_SERIAL_PORT_BAUD 115200 +#endif + + +#define CW_DECODER_SCREEN_COLUMNS 120 // word wrap at this many columns +#define CW_DECODER_SPACE_PRINT_THRESH 4.5 // print space if no tone for this many element lengths +#define CW_DECODER_SPACE_DECODE_THRESH 2.0 // invoke character decode if no tone for this many element lengths +#define CW_DECODER_NOISE_FILTER 20 // ignore elements shorter than this (mS) + +#define STRAIGHT_KEY_ACTIVE_STATE LOW + +#ifdef FEATURE_DYNAMIC_DAH_TO_DIT_RATIO + #define DYNAMIC_DAH_TO_DIT_RATIO_LOWER_LIMIT_WPM 30 + #define DYNAMIC_DAH_TO_DIT_RATIO_LOWER_LIMIT_RATIO 300 // 300 = 3:1 ratio + #define DYNAMIC_DAH_TO_DIT_RATIO_UPPER_LIMIT_WPM 70 + #define DYNAMIC_DAH_TO_DIT_RATIO_UPPER_LIMIT_RATIO 240 // 240 = 2.4:1 ratio +#endif //FEATURE_DYNAMIC_DAH_TO_DIT_RATIO + +#if defined(FEATURE_COMPETITION_COMPRESSION_DETECTION) + #define COMPETITION_COMPRESSION_DETECTION_ARRAY_SIZE 16 + #define COMPETITION_COMPRESSION_DETECTION_TIME_INTERCHAR_LOWER_LIMIT 1 + #define COMPETITION_COMPRESSION_DETECTION_TIME_INTERCHAR_UPPER_LIMIT 6.0 + #define COMPETITION_COMPRESSION_DETECTION_AVERAGE_ALARM_THRESHOLD 3.0 +#endif //FEATURE_COMPETITION_COMPRESSION_DETECTION + +#if defined(FEATURE_SLEEP) + #define KEYER_AWAKE_PIN_AWAKE_STATE HIGH + #define KEYER_AWAKE_PIN_ASLEEP_STATE LOW +#endif + +#if defined(FEATURE_LCD_BACKLIGHT_AUTO_DIM) + #define keyer_power_led_awake_duty 255 // PWM duty cycle. 0 is 0%, 255 is 100% + #define keyer_power_led_asleep_duty 25 // 25 is quite dim. Use 0 for off +#endif + +#if defined(FEATURE_ETHERNET) + // #define FEATURE_ETHERNET_IP {192,168,1,178} // default IP address ("192.168.1.178") + // #define FEATURE_ETHERNET_MAC {0xDE,0xAD,0xBE,0xEF,0xFE,0xED} + #define FEATURE_ETHERNET_IP {192,168,1,179} // default IP address ("192.168.1.179") + #define FEATURE_ETHERNET_MAC {0xDE,0xAD,0xBE,0xEF,0xFE,0xEE} + #define FEATURE_ETHERNET_DNS {8,8,8,8} + + #define FEATURE_ETHERNET_GATEWAY {192,168,1,1} // default gateway + #define FEATURE_ETHERNET_SUBNET_MASK {255,255,255,0} // default subnet mask + #define FEATURE_ETHERNET_WEB_LISTENER_PORT 80 + #define FEATURE_UDP_SEND_BUFFER_SIZE 128 + #define FEATURE_UDP_RECEIVE_BUFFER_SIZE 128 +#endif //FEATURE_ETHERNET + +#define WEB_SERVER_CONTROL_TX_KEY_TIME_LIMIT_SECS 10 + +#define FEATURE_INTERNET_LINK_MAX_LINKS 2 +#define FEATURE_INTERNET_LINK_DEFAULT_RCV_UDP_PORT 8888 +#define FEATURE_INTERNET_LINK_BUFFER_TIME_MS 500 + +#if defined(FEATURE_4x4_KEYPAD)|| defined (FEATURE_3x4_KEYPAD) + #define KEYPAD_ROWS 4 //KeyPad Rows + #if defined(FEATURE_4x4_KEYPAD) + #define KEYPAD_COLS 4 //keypad Columns + #else + #define KEYPAD_COLS 3 + #endif + #define mem1 0 //Memory numbers for Keypad: Actual memory numbers start with "0" + #define mem2 1 + #define mem3 2 + #define mem4 3 + #define mem5 4 + #define mem6 5 + #define mem7 6 + #define mem8 7 + #define mem9 8 + #define mem10 9 + #define mem11 10 + #define mem12 11 +#endif //#if defined(FEATURE_4x4_KEYPAD)|| defined (FEATURE_3x4_KEYPAD) + +#define initial_sidetone_mode 1 // Sidetone mode, 0=OFF, 1=ON, 2=PADDLE_ONLY + +#define sd_card_spi_ss_line 4 + +#if defined(OPTION_DFROBOT_LCD_COMMAND_BUTTONS) + + // For V1.1 board use these values + #define dfrobot_btnRIGHT_analog 50 + #define dfrobot_btnUP_analog 250 + #define dfrobot_btnDOWN_analog 450 + #define dfrobot_btnLEFT_analog 650 + #define dfrobot_btnSELECT_analog 850 + + // For V1.0 board use these values + // #define dfrobot_btnRIGHT_analog 50 + // #define dfrobot_btnUP_analog 195 + // #define dfrobot_btnDOWN_analog 380 + // #define dfrobot_btnLEFT_analog 555 + // #define dfrobot_btnSELECT_analog 790 + + // button to memory mappings (0 = command button, 1 = memory 1, 2 = memory 2, etc.) + #define dfrobot_btnRIGHT 2 + #define dfrobot_btnUP 1 + #define dfrobot_btnDOWN 3 + #define dfrobot_btnLEFT 4 + #define dfrobot_btnSELECT 0 + #define dfrobot_btnNONE 255 // do not change + +#endif + +#define sequencer_pins_active_state HIGH +#define sequencer_pins_inactive_state LOW +#define ptt_line_active_state HIGH +#define ptt_line_inactive_state LOW +#define tx_key_line_active_state HIGH +#define tx_key_line_inactive_state LOW +#define ptt_input_pin_active_state LOW +#define ptt_input_pin_inactive_state HIGH +#define tx_inhibit_pin_active_state LOW +#define tx_inhibit_pin_inactive_state HIGH +#define tx_pause_pin_active_state LOW +#define tx_pause_pin_inactive_state HIGH +#define sidetone_line_active_state HIGH +#define sidetone_line_inactive_state LOW + +#if defined(ARDUINO_MAPLE_MINI) || defined(ARDUINO_GENERIC_STM32f103C) //sp5iou 20180329 + #define button_value_factor 4095 //sp5iou contributed +#else + #define button_value_factor 1023 +#endif + +#define farnsworth_timing_calibration 1.15 + +#define sidetone_volume_low_limit 10 +#define sidetone_volume_high_limit 500 + +#define custom_startup_field "your custom text here" // an example could be callsign and name, eg. "AB1XYZ Bob", (or "Worlds best operator" which requires a 20 column display), string length shouldo be no more than the number of columns on the display + +#define command_mode_acknowledgement_character 'E' + +#if defined(FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT) + #define command_a_iambic_a "A" + #define command_b_iambic_b "B" + #define command_c_single_paddle "SINGLE" + #define command_d_ultimatic "ULT" + #define command_h_weight_dit_dah_ratio_default "R" + #define command_i_tx_on "TX ON" + #define command_i_tx_off "TX OFF" + #define command_k_dit_dah_buffers_on "ON" + #define command_k_dit_dah_buffers_off "OFF" + #define command_n_paddle_reverse "REV" + #define command_n_paddle_normal "NORM" + #define command_o_sidetone_off "ST OFF" + #define command_o_sidetone_paddle_only "ST PD ONLY" + #define command_o_sidetone_on "ST ON" + #define command_t_tune_mode "TUNE" + #define command_v_potentiometer_on "POT ON" + #define command_v_potentiometer_off "POT OFF" + + #define command_error "ERR" + +#endif //FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT diff --git a/k3ng_keyer/keyer_settings_iz3gme.h b/k3ng_keyer/keyer_settings_iz3gme.h new file mode 100755 index 0000000..d5dba79 --- /dev/null +++ b/k3ng_keyer/keyer_settings_iz3gme.h @@ -0,0 +1,316 @@ +// Initial and hardcoded settings +#define initial_speed_wpm 26 // "factory default" keyer speed setting +#define initial_command_mode_speed_wpm 20 // "factory default" command mode speed setting +#define initial_sidetone_freq 600 // "factory default" sidetone frequency setting +#define sidetone_hz_limit_low 299 +#define sidetone_hz_limit_high 2001 +#define hz_high_beep 1500 // frequency in hertz of high beep +#define hz_low_beep 400 // frequency in hertz of low beep +#define initial_dah_to_dit_ratio 300 // 300 = 3 / normal 3:1 ratio +#define initial_ptt_lead_time_tx1 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx1 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx2 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx2 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx3 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx3 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx4 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx4 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx5 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx5 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx6 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx6 10 // PTT tail time in mS +#define initial_qrss_dit_length 1 // QRSS dit length in seconds +#define initial_pot_wpm_low_value 13 // Potentiometer WPM fully CCW +#define initial_pot_wpm_high_value 35 // Potentiometer WPM fully CW +#define wpm_limit_low 5 +#define wpm_limit_high 60 +#define potentiometer_change_threshold 0.9 // don't change the keyer speed until pot wpm has changed more than this +#define send_buffer_size 150 +#define default_length_letterspace 3 +#define default_length_wordspace 7 +#define default_keying_compensation 0 // number of milliseconds to extend all dits and dahs - for QSK on boatanchors +#define default_first_extension_time 0 // number of milliseconds to extend first sent dit or dah +#define default_pot_full_scale_reading 1023 +#define default_weighting 50 // 50 = weighting factor of 1 (normal) +#define default_ptt_hang_time_wordspace_units 0.0 +#define winkey_c0_wait_time 1 // the number of milliseconds to wait to send 0xc0 byte after send buffer has been sent +#define winkey_command_timeout_ms 5000 +#define winkey_discard_bytes_startup 3 // this is used if OPTION_WINKEY_DISCARD_BYTES_AT_STARTUP is enabled above +#define winkey_xoff_threshold 20 // the number of chars in the buffer when we begin sending XOFFs +#define winkey_xon_threshold 10 // the number of chars in the buffer below which we deactivate XOFF +#define default_memory_repeat_time 3000 // time in milliseconds +#define LCD_COLUMNS 16 +#define LCD_ROWS 2 +#define lcd_i2c_address_mathertel_PCF8574 0x27 // I2C address of display for FEATURE_LCD_MATHERTEL_PCF8574 +#define lcd_i2c_address_fdebrander_lcd 0x27 // I2C address of display for FEATURE_LCD_I2C_FDEBRABANDER +#define lcd_i2c_address_ydv1_lcd 0x27 // I2C address of display for FEATURE_LCD_YDv1 +//#define lcd_i2c_address_ydv1_lcd 0x38 // I2C address of display for FEATURE_LCD_YDv1 +#define lcd_i2c_address_sainsmart_lcd 0x27 // I2C address of display for FEATURE_LCD_SAINSMART_I2C +#define hell_pixel_microseconds 4025 +#define program_memory_limit_consec_spaces 1 +#define serial_leading_zeros 1 // set to 1 to activate leading zeros in serial numbers (i.e. #1 = 001) +#define serial_cut_numbers 0 // set to 1 to activate cut numbers in serial numbers (i.e. #10 = 1T, #19 = 1N) +#define go_to_sleep_inactivity_time 10 // minutes - FEATURE_SLEEP +#define dim_backlight_inactive_time 5 // minutes - FEATURE_LCD_BACKLIGHT_AUTO_DIM +#define default_cmos_super_keyer_iambic_b_timing_percent 33 // use with FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING; should be between 0 to 99 % (0% = true iambic b;100% = iambic a behavior) +#define default_cw_echo_timing_factor 1.75 // "factory default" setting +#define default_autospace_timing_factor 2.0 // "factory default" setting +#define winkey_paddle_echo_buffer_decode_timing_factor 0.25 +#define potentiometer_always_on 0 +#define ptt_interlock_check_every_ms 100 +#define ptt_interlock_active_state HIGH +#define unknown_cw_character '*' +#define cli_paddle_echo_on_at_boot 1 +#define cli_straight_key_echo_on_at_boot 1 +#define tx_key_dit_and_dah_pins_active_state HIGH +#define tx_key_dit_and_dah_pins_inactive_state LOW +#define potentiometer_check_interval_ms 150 +#define potentiometer_reading_threshold 1 +#define default_paddle_interruption_quiet_time_element_lengths 0 +#define default_wordsworth_wordspace 6 +#define default_wordsworth_repetition 1 +#define serial_program_memory_buffer_size 500 +#define eeprom_write_time_ms 30000 + +#ifdef FEATURE_BUTTONS + #define analog_buttons_number_of_buttons 5 // includes the command button (command button + 3 memory buttons = 4) + #define analog_buttons_r1 10 + #define analog_buttons_r2 1 +#endif + + +#if defined(FEATURE_BUTTONS) && !defined(FEATURE_PS2_KEYBOARD) && !defined(FEATURE_USB_KEYBOARD) && !defined(FEATURE_COMMAND_LINE_INTERFACE) && !defined(FEATURE_WINKEY_EMULATION) + #define number_of_memories byte(analog_buttons_number_of_buttons-1) +#else + #define number_of_memories byte(12) +#endif + +#ifdef FEATURE_CAPACITIVE_PADDLE_PINS + #define capacitance_threshold 2 +#endif //FEATURE_CAPACITIVE_PADDLE_PINS + +#ifdef FEATURE_LED_RING + #define led_ring_low_limit 10 + #define led_ring_high_limit 50 +#endif //FEATURE_LED_RING + +#ifdef FEATURE_QLF + #define qlf_dit_max 125 + #define qlf_dit_min 75 + #define qlf_dah_max 200 + #define qlf_dah_min 100 + #define qlf_on_by_default 0 +#endif //FEATURE_QLF + + +#ifdef FEATURE_WINKEY_EMULATION + #ifdef OPTION_WINKEY_UCXLOG_9600_BAUD + #define WINKEY_DEFAULT_BAUD 9600 + #else + #define WINKEY_DEFAULT_BAUD 1200 + #endif //OPTION_WINKEY_UCXLOG_9600_BAUD +// alter these below to map alternate sidetones for Winkey interface protocol emulation +#ifdef OPTION_WINKEY_2_SUPPORT + #define WINKEY_SIDETONE_1 3759 + #define WINKEY_SIDETONE_2 1879 + #define WINKEY_SIDETONE_3 1252 + #define WINKEY_SIDETONE_4 940 + #define WINKEY_SIDETONE_5 752 + #define WINKEY_SIDETONE_6 625 + #define WINKEY_SIDETONE_7 535 + #define WINKEY_SIDETONE_8 469 + #define WINKEY_SIDETONE_9 417 + #define WINKEY_SIDETONE_10 375 +#else //OPTION_WINKEY_2_SUPPORT + #define WINKEY_SIDETONE_1 4000 + #define WINKEY_SIDETONE_2 2000 + #define WINKEY_SIDETONE_3 1333 + #define WINKEY_SIDETONE_4 1000 + #define WINKEY_SIDETONE_5 800 + #define WINKEY_SIDETONE_6 666 + #define WINKEY_SIDETONE_7 571 + #define WINKEY_SIDETONE_8 500 + #define WINKEY_SIDETONE_9 444 + #define WINKEY_SIDETONE_10 400 +#endif //OPTION_WINKEY_2_SUPPORT + +#define WINKEY_1_REPORT_VERSION_NUMBER 10 +#define WINKEY_2_REPORT_VERSION_NUMBER 23 + +// alter these to map to alternate hang time wordspace units +#define WINKEY_HANG_TIME_1_0 1.0 +#define WINKEY_HANG_TIME_1_33 1.33 +#define WINKEY_HANG_TIME_1_66 1.66 +#define WINKEY_HANG_TIME_2_0 2.0 + +#define WINKEY_RETURN_THIS_FOR_ADMIN_GET_CAL 0x16 +#define WINKEY_RETURN_THIS_FOR_ADMIN_PADDLE_A2D 0xEE +#define WINKEY_RETURN_THIS_FOR_ADMIN_SPEED_A2D 0x00 + +#endif //FEATURE_WINKEY_EMULATION + + + +#define PRIMARY_SERIAL_PORT &Serial +#define PRIMARY_SERIAL_PORT_BAUD 115200 // This applies only when the port is in Command Line Interface mode. In Winkey mode it will default to 1200. + +#ifdef FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + #define SECONDARY_SERIAL_PORT &Serial1 + #define SECONDARY_SERIAL_PORT_BAUD 115200 +#endif + + +#define CW_DECODER_SCREEN_COLUMNS 120 // word wrap at this many columns +#define CW_DECODER_SPACE_PRINT_THRESH 4.5 // print space if no tone for this many element lengths +#define CW_DECODER_SPACE_DECODE_THRESH 2.0 // invoke character decode if no tone for this many element lengths +#define CW_DECODER_NOISE_FILTER 20 // ignore elements shorter than this (mS) + +#define STRAIGHT_KEY_ACTIVE_STATE LOW + +#ifdef FEATURE_DYNAMIC_DAH_TO_DIT_RATIO + #define DYNAMIC_DAH_TO_DIT_RATIO_LOWER_LIMIT_WPM 30 + #define DYNAMIC_DAH_TO_DIT_RATIO_LOWER_LIMIT_RATIO 300 // 300 = 3:1 ratio + #define DYNAMIC_DAH_TO_DIT_RATIO_UPPER_LIMIT_WPM 70 + #define DYNAMIC_DAH_TO_DIT_RATIO_UPPER_LIMIT_RATIO 240 // 240 = 2.4:1 ratio +#endif //FEATURE_DYNAMIC_DAH_TO_DIT_RATIO + +#if defined(FEATURE_COMPETITION_COMPRESSION_DETECTION) + #define COMPETITION_COMPRESSION_DETECTION_ARRAY_SIZE 16 + #define COMPETITION_COMPRESSION_DETECTION_TIME_INTERCHAR_LOWER_LIMIT 1 + #define COMPETITION_COMPRESSION_DETECTION_TIME_INTERCHAR_UPPER_LIMIT 6.0 + #define COMPETITION_COMPRESSION_DETECTION_AVERAGE_ALARM_THRESHOLD 3.0 +#endif //FEATURE_COMPETITION_COMPRESSION_DETECTION + +#if defined(FEATURE_SLEEP) + #define KEYER_AWAKE_PIN_AWAKE_STATE HIGH + #define KEYER_AWAKE_PIN_ASLEEP_STATE LOW +#endif + +#if defined(FEATURE_LCD_BACKLIGHT_AUTO_DIM) + #define keyer_power_led_awake_duty 255 // PWM duty cycle. 0 is 0%, 255 is 100% + #define keyer_power_led_asleep_duty 25 // 25 is quite dim. Use 0 for off +#endif + +#if defined(FEATURE_ETHERNET) + // #define FEATURE_ETHERNET_IP {192,168,1,178} // default IP address ("192.168.1.178") + // #define FEATURE_ETHERNET_MAC {0xDE,0xAD,0xBE,0xEF,0xFE,0xED} + #define FEATURE_ETHERNET_IP {192,168,1,179} // default IP address ("192.168.1.179") + #define FEATURE_ETHERNET_MAC {0xDE,0xAD,0xBE,0xEF,0xFE,0xEE} + #define FEATURE_ETHERNET_DNS {8,8,8,8} + + #define FEATURE_ETHERNET_GATEWAY {192,168,1,1} // default gateway + #define FEATURE_ETHERNET_SUBNET_MASK {255,255,255,0} // default subnet mask + #define FEATURE_ETHERNET_WEB_LISTENER_PORT 80 + #define FEATURE_UDP_SEND_BUFFER_SIZE 128 + #define FEATURE_UDP_RECEIVE_BUFFER_SIZE 128 +#endif //FEATURE_ETHERNET + +#define WEB_SERVER_CONTROL_TX_KEY_TIME_LIMIT_SECS 10 + +#define FEATURE_INTERNET_LINK_MAX_LINKS 2 +#define FEATURE_INTERNET_LINK_DEFAULT_RCV_UDP_PORT 8888 +#define FEATURE_INTERNET_LINK_BUFFER_TIME_MS 500 + +#if defined(FEATURE_4x4_KEYPAD)|| defined (FEATURE_3x4_KEYPAD) + #define KEYPAD_ROWS 4 //KeyPad Rows + #if defined(FEATURE_4x4_KEYPAD) + #define KEYPAD_COLS 4 //keypad Columns + #else + #define KEYPAD_COLS 3 + #endif + #define mem1 0 //Memory numbers for Keypad: Actual memory numbers start with "0" + #define mem2 1 + #define mem3 2 + #define mem4 3 + #define mem5 4 + #define mem6 5 + #define mem7 6 + #define mem8 7 + #define mem9 8 + #define mem10 9 + #define mem11 10 + #define mem12 11 +#endif //#if defined(FEATURE_4x4_KEYPAD)|| defined (FEATURE_3x4_KEYPAD) + +#define initial_sidetone_mode 1 // Sidetone mode, 0=OFF, 1=ON, 2=PADDLE_ONLY + +#define sd_card_spi_ss_line 4 + +#if defined(OPTION_DFROBOT_LCD_COMMAND_BUTTONS) + + // For V1.1 board use these values + #define dfrobot_btnRIGHT_analog 50 + #define dfrobot_btnUP_analog 250 + #define dfrobot_btnDOWN_analog 450 + #define dfrobot_btnLEFT_analog 650 + #define dfrobot_btnSELECT_analog 850 + + // For V1.0 board use these values + // #define dfrobot_btnRIGHT_analog 50 + // #define dfrobot_btnUP_analog 195 + // #define dfrobot_btnDOWN_analog 380 + // #define dfrobot_btnLEFT_analog 555 + // #define dfrobot_btnSELECT_analog 790 + + // button to memory mappings (0 = command button, 1 = memory 1, 2 = memory 2, etc.) + #define dfrobot_btnRIGHT 2 + #define dfrobot_btnUP 1 + #define dfrobot_btnDOWN 3 + #define dfrobot_btnLEFT 4 + #define dfrobot_btnSELECT 0 + #define dfrobot_btnNONE 255 // do not change + +#endif + +#define sequencer_pins_active_state HIGH +#define sequencer_pins_inactive_state LOW +#define ptt_line_active_state HIGH +#define ptt_line_inactive_state LOW +#define tx_key_line_active_state HIGH +#define tx_key_line_inactive_state LOW +#define ptt_input_pin_active_state LOW +#define ptt_input_pin_inactive_state HIGH +#define tx_inhibit_pin_active_state LOW +#define tx_inhibit_pin_inactive_state HIGH +#define tx_pause_pin_active_state LOW +#define tx_pause_pin_inactive_state HIGH +#define sidetone_line_active_state HIGH +#define sidetone_line_inactive_state LOW + +#if defined(ARDUINO_MAPLE_MINI) + #define button_value_factor 4095 //sp5iou contributed +#else + #define button_value_factor 1023 +#endif + +#define farnsworth_timing_calibration 1.15 + +#define sidetone_volume_low_limit 10 +#define sidetone_volume_high_limit 500 + +#define custom_startup_field "your custom text here" // an example could be callsign and name, eg. "AB1XYZ Bob", (or "Worlds best operator" which requires a 20 column display), string length shouldo be no more than the number of columns on the display + +#define command_mode_acknowledgement_character 'E' + +#if defined(FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT) + #define command_a_iambic_a "A" + #define command_b_iambic_b "B" + #define command_c_single_paddle "SINGLE" + #define command_d_ultimatic "ULT" + #define command_h_weight_dit_dah_ratio_default "R" + #define command_i_tx_on "TX ON" + #define command_i_tx_off "TX OFF" + #define command_k_dit_dah_buffers_on "ON" + #define command_k_dit_dah_buffers_off "OFF" + #define command_n_paddle_reverse "REV" + #define command_n_paddle_normal "NORM" + #define command_o_sidetone_off "ST OFF" + #define command_o_sidetone_paddle_only "ST PD ONLY" + #define command_o_sidetone_on "ST ON" + #define command_t_tune_mode "TUNE" + #define command_v_potentiometer_on "POT ON" + #define command_v_potentiometer_off "POT OFF" + + #define command_error "ERR" + +#endif //FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT diff --git a/k3ng_keyer/keyer_settings_k5bcq.h b/k3ng_keyer/keyer_settings_k5bcq.h new file mode 100755 index 0000000..b3c2ab7 --- /dev/null +++ b/k3ng_keyer/keyer_settings_k5bcq.h @@ -0,0 +1,315 @@ +// Initial and hardcoded settings +#define initial_speed_wpm 26 // "factory default" keyer speed setting +#define initial_command_mode_speed_wpm 20 // "factory default" command mode speed setting +#define initial_sidetone_freq 700 // "factory default" sidetone frequency setting +#define sidetone_hz_limit_low 299 +#define sidetone_hz_limit_high 2001 +#define hz_high_beep 1500 // frequency in hertz of high beep +#define hz_low_beep 400 // frequency in hertz of low beep +#define initial_dah_to_dit_ratio 300 // 300 = 3 / normal 3:1 ratio +#define initial_ptt_lead_time_tx1 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx1 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx2 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx2 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx3 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx3 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx4 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx4 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx5 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx5 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx6 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx6 10 // PTT tail time in mS +#define initial_qrss_dit_length 1 // QRSS dit length in seconds +#define initial_pot_wpm_low_value 13 // Potentiometer WPM fully CCW +#define initial_pot_wpm_high_value 35 // Potentiometer WPM fully CW +#define wpm_limit_low 5 +#define wpm_limit_high 60 +#define potentiometer_change_threshold 0.9 // don't change the keyer speed until pot wpm has changed more than this +#define send_buffer_size 150 +#define default_length_letterspace 3 +#define default_length_wordspace 7 +#define default_keying_compensation 0 // number of milliseconds to extend all dits and dahs - for QSK on boatanchors +#define default_first_extension_time 0 // number of milliseconds to extend first sent dit or dah +#define default_pot_full_scale_reading 1023 +#define default_weighting 50 // 50 = weighting factor of 1 (normal) +#define default_ptt_hang_time_wordspace_units 0.0 +#define winkey_c0_wait_time 1 // the number of milliseconds to wait to send 0xc0 byte after send buffer has been sent +#define winkey_command_timeout_ms 5000 +#define winkey_discard_bytes_startup 3 // this is used if OPTION_WINKEY_DISCARD_BYTES_AT_STARTUP is enabled above +#define winkey_xoff_threshold 20 // the number of chars in the buffer when we begin sending XOFFs +#define winkey_xon_threshold 10 // the number of chars in the buffer below which we deactivate XOFF +#define default_memory_repeat_time 3000 // time in milliseconds +#define LCD_COLUMNS 16 //edit for 1602 or 1604 LCD +#define LCD_ROWS 2 //edit for 1602 or 1604 LCD +#define lcd_i2c_address_mathertel_PCF8574 0x27 // I2C address of display for FEATURE_LCD_MATHERTEL_PCF8574 +#define lcd_i2c_address_fdebrander_lcd 0x27 // I2C address of display for FEATURE_LCD_I2C_FDEBRABANDER +#define lcd_i2c_address_ydv1_lcd 0x27 // I2C address of display for FEATURE_LCD_YDv1 +//#define lcd_i2c_address_ydv1_lcd 0x38 // I2C address of display for FEATURE_LCD_YDv1 +#define lcd_i2c_address_sainsmart_lcd 0x27 // I2C address of display for FEATURE_LCD_SAINSMART_I2C +#define hell_pixel_microseconds 4025 +#define program_memory_limit_consec_spaces 1 +#define serial_leading_zeros 1 // set to 1 to activate leading zeros in serial numbers (i.e. #1 = 001) +#define serial_cut_numbers 0 // set to 1 to activate cut numbers in serial numbers (i.e. #10 = 1T, #19 = 1N) +#define go_to_sleep_inactivity_time 10 // minutes - FEATURE_SLEEP +#define dim_backlight_inactive_time 5 // minutes - FEATURE_LCD_BACKLIGHT_AUTO_DIM +#define default_cmos_super_keyer_iambic_b_timing_percent 33 // use with FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING; should be between 0 to 99 % (0% = true iambic b;100% = iambic a behavior) +#define default_cw_echo_timing_factor 1.75 // "factory default" setting +#define default_autospace_timing_factor 2.0 // "factory default" setting +#define winkey_paddle_echo_buffer_decode_timing_factor 0.25 +#define potentiometer_always_on 0 +#define ptt_interlock_check_every_ms 100 +#define ptt_interlock_active_state HIGH +#define unknown_cw_character '*' +#define cli_paddle_echo_on_at_boot 1 +#define cli_straight_key_echo_on_at_boot 1 +#define tx_key_dit_and_dah_pins_active_state HIGH +#define tx_key_dit_and_dah_pins_inactive_state LOW +#define potentiometer_check_interval_ms 150 +#define potentiometer_reading_threshold 1 +#define default_paddle_interruption_quiet_time_element_lengths 0 +#define default_wordsworth_wordspace 6 +#define default_wordsworth_repetition 1 +#define serial_program_memory_buffer_size 500 +#define eeprom_write_time_ms 30000 + +#ifdef FEATURE_BUTTONS + #define analog_buttons_number_of_buttons 8 // includes the command button (command button + 3 memory buttons = 4) + #define analog_buttons_r1 10 + #define analog_buttons_r2 1 +#endif + + +#if defined(FEATURE_BUTTONS) && !defined(FEATURE_PS2_KEYBOARD) && !defined(FEATURE_USB_KEYBOARD) && !defined(FEATURE_COMMAND_LINE_INTERFACE) && !defined(FEATURE_WINKEY_EMULATION) + #define number_of_memories byte(analog_buttons_number_of_buttons-1) +#else + #define number_of_memories byte(12) +#endif + +#ifdef FEATURE_CAPACITIVE_PADDLE_PINS + #define capacitance_threshold 2 +#endif //FEATURE_CAPACITIVE_PADDLE_PINS + +#ifdef FEATURE_LED_RING + #define led_ring_low_limit 10 + #define led_ring_high_limit 50 +#endif //FEATURE_LED_RING + +#ifdef FEATURE_QLF + #define qlf_dit_max 125 + #define qlf_dit_min 75 + #define qlf_dah_max 200 + #define qlf_dah_min 100 + #define qlf_on_by_default 0 +#endif //FEATURE_QLF + + +#ifdef FEATURE_WINKEY_EMULATION + #ifdef OPTION_WINKEY_UCXLOG_9600_BAUD + #define WINKEY_DEFAULT_BAUD 9600 + #else + #define WINKEY_DEFAULT_BAUD 1200 + #endif //OPTION_WINKEY_UCXLOG_9600_BAUD +// alter these below to map alternate sidetones for Winkey interface protocol emulation +#ifdef OPTION_WINKEY_2_SUPPORT + #define WINKEY_SIDETONE_1 3759 + #define WINKEY_SIDETONE_2 1879 + #define WINKEY_SIDETONE_3 1252 + #define WINKEY_SIDETONE_4 940 + #define WINKEY_SIDETONE_5 752 + #define WINKEY_SIDETONE_6 625 + #define WINKEY_SIDETONE_7 535 + #define WINKEY_SIDETONE_8 469 + #define WINKEY_SIDETONE_9 417 + #define WINKEY_SIDETONE_10 375 +#else //OPTION_WINKEY_2_SUPPORT + #define WINKEY_SIDETONE_1 4000 + #define WINKEY_SIDETONE_2 2000 + #define WINKEY_SIDETONE_3 1333 + #define WINKEY_SIDETONE_4 1000 + #define WINKEY_SIDETONE_5 800 + #define WINKEY_SIDETONE_6 666 + #define WINKEY_SIDETONE_7 571 + #define WINKEY_SIDETONE_8 500 + #define WINKEY_SIDETONE_9 444 + #define WINKEY_SIDETONE_10 400 +#endif //OPTION_WINKEY_2_SUPPORT + +#define WINKEY_1_REPORT_VERSION_NUMBER 10 +#define WINKEY_2_REPORT_VERSION_NUMBER 23 + +// alter these to map to alternate hang time wordspace units +#define WINKEY_HANG_TIME_1_0 1.0 +#define WINKEY_HANG_TIME_1_33 1.33 +#define WINKEY_HANG_TIME_1_66 1.66 +#define WINKEY_HANG_TIME_2_0 2.0 + +#define WINKEY_RETURN_THIS_FOR_ADMIN_GET_CAL 0x16 +#define WINKEY_RETURN_THIS_FOR_ADMIN_PADDLE_A2D 0xEE +#define WINKEY_RETURN_THIS_FOR_ADMIN_SPEED_A2D 0x00 + +#endif //FEATURE_WINKEY_EMULATION + + + +#define PRIMARY_SERIAL_PORT &Serial +#define PRIMARY_SERIAL_PORT_BAUD 115200 // This applies only when the port is in Command Line Interface mode. In Winkey mode it will default to 1200. + +#ifdef FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + #define SECONDARY_SERIAL_PORT &Serial1 + #define SECONDARY_SERIAL_PORT_BAUD 115200 +#endif + + +#define CW_DECODER_SCREEN_COLUMNS 120 // word wrap at this many columns +#define CW_DECODER_SPACE_PRINT_THRESH 4.5 // print space if no tone for this many element lengths +#define CW_DECODER_SPACE_DECODE_THRESH 2.0 // invoke character decode if no tone for this many element lengths +#define CW_DECODER_NOISE_FILTER 20 // ignore elements shorter than this (mS) + +#define STRAIGHT_KEY_ACTIVE_STATE LOW + +#ifdef FEATURE_DYNAMIC_DAH_TO_DIT_RATIO + #define DYNAMIC_DAH_TO_DIT_RATIO_LOWER_LIMIT_WPM 30 + #define DYNAMIC_DAH_TO_DIT_RATIO_LOWER_LIMIT_RATIO 300 // 300 = 3:1 ratio + #define DYNAMIC_DAH_TO_DIT_RATIO_UPPER_LIMIT_WPM 70 + #define DYNAMIC_DAH_TO_DIT_RATIO_UPPER_LIMIT_RATIO 240 // 240 = 2.4:1 ratio +#endif //FEATURE_DYNAMIC_DAH_TO_DIT_RATIO + +#if defined(FEATURE_COMPETITION_COMPRESSION_DETECTION) + #define COMPETITION_COMPRESSION_DETECTION_ARRAY_SIZE 16 + #define COMPETITION_COMPRESSION_DETECTION_TIME_INTERCHAR_LOWER_LIMIT 1 + #define COMPETITION_COMPRESSION_DETECTION_TIME_INTERCHAR_UPPER_LIMIT 6.0 + #define COMPETITION_COMPRESSION_DETECTION_AVERAGE_ALARM_THRESHOLD 3.0 +#endif //FEATURE_COMPETITION_COMPRESSION_DETECTION + +#if defined(FEATURE_SLEEP) + #define KEYER_AWAKE_PIN_AWAKE_STATE HIGH + #define KEYER_AWAKE_PIN_ASLEEP_STATE LOW +#endif + +#if defined(FEATURE_LCD_BACKLIGHT_AUTO_DIM) + #define keyer_power_led_awake_duty 255 // PWM duty cycle. 0 is 0%, 255 is 100% + #define keyer_power_led_asleep_duty 25 // 25 is quite dim. Use 0 for off +#endif + +#if defined(FEATURE_ETHERNET) + // #define FEATURE_ETHERNET_IP {192,168,1,178} // default IP address ("192.168.1.178") + // #define FEATURE_ETHERNET_MAC {0xDE,0xAD,0xBE,0xEF,0xFE,0xED} + #define FEATURE_ETHERNET_IP {192,168,1,179} // default IP address ("192.168.1.179") + #define FEATURE_ETHERNET_MAC {0xDE,0xAD,0xBE,0xEF,0xFE,0xEE} + #define FEATURE_ETHERNET_DNS {8,8,8,8} + + #define FEATURE_ETHERNET_GATEWAY {192,168,1,1} // default gateway + #define FEATURE_ETHERNET_SUBNET_MASK {255,255,255,0} // default subnet mask + #define FEATURE_ETHERNET_WEB_LISTENER_PORT 80 + #define FEATURE_UDP_SEND_BUFFER_SIZE 128 + #define FEATURE_UDP_RECEIVE_BUFFER_SIZE 128 +#endif //FEATURE_ETHERNET + +#define WEB_SERVER_CONTROL_TX_KEY_TIME_LIMIT_SECS 10 + +#define FEATURE_INTERNET_LINK_MAX_LINKS 2 +#define FEATURE_INTERNET_LINK_DEFAULT_RCV_UDP_PORT 8888 +#define FEATURE_INTERNET_LINK_BUFFER_TIME_MS 500 + +#if defined(FEATURE_4x4_KEYPAD)|| defined (FEATURE_3x4_KEYPAD) + #define KEYPAD_ROWS 4 //KeyPad Rows + #if defined(FEATURE_4x4_KEYPAD) + #define KEYPAD_COLS 4 //keypad Columns + #else + #define KEYPAD_COLS 3 + #endif + #define mem1 0 //Memory numbers for Keypad: Actual memory numbers start with "0" + #define mem2 1 + #define mem3 2 + #define mem4 3 + #define mem5 4 + #define mem6 5 + #define mem7 6 + #define mem8 7 + #define mem9 8 + #define mem10 9 + #define mem11 10 + #define mem12 11 +#endif //#if defined(FEATURE_4x4_KEYPAD)|| defined (FEATURE_3x4_KEYPAD) + +#define initial_sidetone_mode 1 // Sidetone mode, 0=OFF, 1=ON, 2=PADDLE_ONLY + +#define sd_card_spi_ss_line 4 + +#if defined(OPTION_DFROBOT_LCD_COMMAND_BUTTONS) + + // For V1.1 board use these values + #define dfrobot_btnRIGHT_analog 50 + #define dfrobot_btnUP_analog 250 + #define dfrobot_btnDOWN_analog 450 + #define dfrobot_btnLEFT_analog 650 + #define dfrobot_btnSELECT_analog 850 + + // For V1.0 board use these values + // #define dfrobot_btnRIGHT_analog 50 + // #define dfrobot_btnUP_analog 195 + // #define dfrobot_btnDOWN_analog 380 + // #define dfrobot_btnLEFT_analog 555 + // #define dfrobot_btnSELECT_analog 790 + + // button to memory mappings (0 = command button, 1 = memory 1, 2 = memory 2, etc.) + #define dfrobot_btnRIGHT 2 + #define dfrobot_btnUP 1 + #define dfrobot_btnDOWN 3 + #define dfrobot_btnLEFT 4 + #define dfrobot_btnSELECT 0 + #define dfrobot_btnNONE 255 // do not change + +#endif + +#define sequencer_pins_active_state HIGH +#define sequencer_pins_inactive_state LOW +#define ptt_line_active_state HIGH +#define ptt_line_inactive_state LOW +#define tx_key_line_active_state HIGH +#define tx_key_line_inactive_state LOW +#define ptt_input_pin_active_state LOW +#define ptt_input_pin_inactive_state HIGH +#define tx_inhibit_pin_active_state LOW +#define tx_inhibit_pin_inactive_state HIGH +#define tx_pause_pin_active_state LOW +#define tx_pause_pin_inactive_state HIGH +#define sidetone_line_active_state HIGH +#define sidetone_line_inactive_state LOW + +#if defined(ARDUINO_MAPLE_MINI) + #define button_value_factor 4095 //sp5iou contributed +#else + #define button_value_factor 1023 +#endif + +#define farnsworth_timing_calibration 1.15 + +#define sidetone_volume_low_limit 10 +#define sidetone_volume_high_limit 500 + +#define custom_startup_field "your custom text here" // an example could be callsign and name, eg. "AB1XYZ Bob", (or "Worlds best operator" which requires a 20 column display), string length shouldo be no more than the number of columns on the display + +#define command_mode_acknowledgement_character 'E' +#if defined(FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT) + #define command_a_iambic_a "A" + #define command_b_iambic_b "B" + #define command_c_single_paddle "SINGLE" + #define command_d_ultimatic "ULT" + #define command_h_weight_dit_dah_ratio_default "R" + #define command_i_tx_on "TX ON" + #define command_i_tx_off "TX OFF" + #define command_k_dit_dah_buffers_on "ON" + #define command_k_dit_dah_buffers_off "OFF" + #define command_n_paddle_reverse "REV" + #define command_n_paddle_normal "NORM" + #define command_o_sidetone_off "ST OFF" + #define command_o_sidetone_paddle_only "ST PD ONLY" + #define command_o_sidetone_on "ST ON" + #define command_t_tune_mode "TUNE" + #define command_v_potentiometer_on "POT ON" + #define command_v_potentiometer_off "POT OFF" + + #define command_error "ERR" + +#endif //FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT diff --git a/k3ng_keyer/keyer_settings_maple_mini.h b/k3ng_keyer/keyer_settings_maple_mini.h new file mode 100755 index 0000000..72ecae7 --- /dev/null +++ b/k3ng_keyer/keyer_settings_maple_mini.h @@ -0,0 +1,329 @@ +/* + +# # ## ##### # ###### # # # # # # +## ## # # # # # # ## ## # ## # # +# ## # # # # # # ##### # ## # # # # # # +# # ###### ##### # # # # # # # # # +# # # # # # # # # # # ## # +# # # # # ###### ###### # # # # # # + + + +*/ + +// Initial and hardcoded settings +#define initial_speed_wpm 26 // "factory default" keyer speed setting +#define initial_command_mode_speed_wpm 20 // "factory default" command mode speed setting +#define initial_sidetone_freq 600 // "factory default" sidetone frequency setting +#define sidetone_hz_limit_low 299 +#define sidetone_hz_limit_high 2001 +#define hz_high_beep 1500 // frequency in hertz of high beep +#define hz_low_beep 400 // frequency in hertz of low beep +#define initial_dah_to_dit_ratio 300 // 300 = 3 / normal 3:1 ratio +#define initial_ptt_lead_time_tx1 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx1 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx2 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx2 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx3 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx3 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx4 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx4 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx5 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx5 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx6 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx6 10 // PTT tail time in mS +#define initial_qrss_dit_length 1 // QRSS dit length in seconds +#define initial_pot_wpm_low_value 13 // Potentiometer WPM fully CCW +#define initial_pot_wpm_high_value 35 // Potentiometer WPM fully CW +#define wpm_limit_low 5 +#define wpm_limit_high 60 +#define potentiometer_change_threshold 0.9 // don't change the keyer speed until pot wpm has changed more than this +#define send_buffer_size 150 +#define default_length_letterspace 3 +#define default_length_wordspace 7 +#define default_keying_compensation 0 // number of milliseconds to extend all dits and dahs - for QSK on boatanchors +#define default_first_extension_time 0 // number of milliseconds to extend first sent dit or dah +#define default_pot_full_scale_reading 1023 +#define default_weighting 50 // 50 = weighting factor of 1 (normal) +#define default_ptt_hang_time_wordspace_units 0.0 +#define winkey_c0_wait_time 1 // the number of milliseconds to wait to send 0xc0 byte after send buffer has been sent +#define winkey_command_timeout_ms 5000 +#define winkey_discard_bytes_startup 3 // this is used if OPTION_WINKEY_DISCARD_BYTES_AT_STARTUP is enabled above +#define winkey_xoff_threshold 20 // the number of chars in the buffer when we begin sending XOFFs +#define winkey_xon_threshold 10 // the number of chars in the buffer below which we deactivate XOFF +#define default_memory_repeat_time 3000 // time in milliseconds +#define LCD_COLUMNS 16 +#define LCD_ROWS 2 +#define lcd_i2c_address_mathertel_PCF8574 0x27 // I2C address of display for FEATURE_LCD_MATHERTEL_PCF8574 +#define lcd_i2c_address_fdebrander_lcd 0x27 // I2C address of display for FEATURE_LCD_I2C_FDEBRABANDER +#define lcd_i2c_address_ydv1_lcd 0x27 // I2C address of display for FEATURE_LCD_YDv1 +//#define lcd_i2c_address_ydv1_lcd 0x38 // I2C address of display for FEATURE_LCD_YDv1 +#define lcd_i2c_address_sainsmart_lcd 0x27 // I2C address of display for FEATURE_LCD_SAINSMART_I2C +#define hell_pixel_microseconds 4025 +#define program_memory_limit_consec_spaces 1 +#define serial_leading_zeros 1 // set to 1 to activate leading zeros in serial numbers (i.e. #1 = 001) +#define serial_cut_numbers 0 // set to 1 to activate cut numbers in serial numbers (i.e. #10 = 1T, #19 = 1N) +#define go_to_sleep_inactivity_time 10 // minutes - FEATURE_SLEEP +#define dim_backlight_inactive_time 5 // minutes - FEATURE_LCD_BACKLIGHT_AUTO_DIM +#define default_cmos_super_keyer_iambic_b_timing_percent 33 // use with FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING; should be between 0 to 99 % (0% = true iambic b;100% = iambic a behavior) +#define default_cw_echo_timing_factor 1.75 // "factory default" setting +#define default_autospace_timing_factor 2.0 // "factory default" setting +#define winkey_paddle_echo_buffer_decode_timing_factor 0.25 +#define potentiometer_always_on 0 +#define ptt_interlock_check_every_ms 100 +#define ptt_interlock_active_state HIGH +#define unknown_cw_character '*' +#define cli_paddle_echo_on_at_boot 1 +#define cli_straight_key_echo_on_at_boot 1 +#define tx_key_dit_and_dah_pins_active_state HIGH +#define tx_key_dit_and_dah_pins_inactive_state LOW +#define potentiometer_check_interval_ms 150 +#define potentiometer_reading_threshold 1 +#define default_paddle_interruption_quiet_time_element_lengths 0 +#define default_wordsworth_wordspace 6 +#define default_wordsworth_repetition 1 +#define serial_program_memory_buffer_size 500 +#define eeprom_write_time_ms 30000 + +#ifdef FEATURE_BUTTONS + #define analog_buttons_number_of_buttons 4 // includes the command button (command button + 3 memory buttons = 4) + #define analog_buttons_r1 10 + #define analog_buttons_r2 1 +#endif + + +#if defined(FEATURE_BUTTONS) && !defined(FEATURE_PS2_KEYBOARD) && !defined(FEATURE_USB_KEYBOARD) && !defined(FEATURE_COMMAND_LINE_INTERFACE) && !defined(FEATURE_WINKEY_EMULATION) + #define number_of_memories byte(analog_buttons_number_of_buttons-1) +#else + #define number_of_memories byte(12) +#endif + +#ifdef FEATURE_CAPACITIVE_PADDLE_PINS + #define capacitance_threshold 2 +#endif //FEATURE_CAPACITIVE_PADDLE_PINS + +#ifdef FEATURE_LED_RING + #define led_ring_low_limit 10 + #define led_ring_high_limit 50 +#endif //FEATURE_LED_RING + +#ifdef FEATURE_QLF + #define qlf_dit_max 125 + #define qlf_dit_min 75 + #define qlf_dah_max 200 + #define qlf_dah_min 100 + #define qlf_on_by_default 0 +#endif //FEATURE_QLF + + +#ifdef FEATURE_WINKEY_EMULATION + #ifdef OPTION_WINKEY_UCXLOG_9600_BAUD + #define WINKEY_DEFAULT_BAUD 9600 + #else + #define WINKEY_DEFAULT_BAUD 1200 + #endif //OPTION_WINKEY_UCXLOG_9600_BAUD +// alter these below to map alternate sidetones for Winkey interface protocol emulation +#ifdef OPTION_WINKEY_2_SUPPORT + #define WINKEY_SIDETONE_1 3759 + #define WINKEY_SIDETONE_2 1879 + #define WINKEY_SIDETONE_3 1252 + #define WINKEY_SIDETONE_4 940 + #define WINKEY_SIDETONE_5 752 + #define WINKEY_SIDETONE_6 625 + #define WINKEY_SIDETONE_7 535 + #define WINKEY_SIDETONE_8 469 + #define WINKEY_SIDETONE_9 417 + #define WINKEY_SIDETONE_10 375 +#else //OPTION_WINKEY_2_SUPPORT + #define WINKEY_SIDETONE_1 4000 + #define WINKEY_SIDETONE_2 2000 + #define WINKEY_SIDETONE_3 1333 + #define WINKEY_SIDETONE_4 1000 + #define WINKEY_SIDETONE_5 800 + #define WINKEY_SIDETONE_6 666 + #define WINKEY_SIDETONE_7 571 + #define WINKEY_SIDETONE_8 500 + #define WINKEY_SIDETONE_9 444 + #define WINKEY_SIDETONE_10 400 +#endif //OPTION_WINKEY_2_SUPPORT + +#define WINKEY_1_REPORT_VERSION_NUMBER 10 +#define WINKEY_2_REPORT_VERSION_NUMBER 23 + +// alter these to map to alternate hang time wordspace units +#define WINKEY_HANG_TIME_1_0 1.0 +#define WINKEY_HANG_TIME_1_33 1.33 +#define WINKEY_HANG_TIME_1_66 1.66 +#define WINKEY_HANG_TIME_2_0 2.0 + +#define WINKEY_RETURN_THIS_FOR_ADMIN_GET_CAL 0x16 +#define WINKEY_RETURN_THIS_FOR_ADMIN_PADDLE_A2D 0xEE +#define WINKEY_RETURN_THIS_FOR_ADMIN_SPEED_A2D 0x00 + +#endif //FEATURE_WINKEY_EMULATION + + + +#define PRIMARY_SERIAL_PORT &Serial +#define PRIMARY_SERIAL_PORT_BAUD 115200 // This applies only when the port is in Command Line Interface mode. In Winkey mode it will default to 1200. + +#ifdef FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + #define SECONDARY_SERIAL_PORT &Serial1 + #define SECONDARY_SERIAL_PORT_BAUD 115200 +#endif + + +#define CW_DECODER_SCREEN_COLUMNS 120 // word wrap at this many columns +#define CW_DECODER_SPACE_PRINT_THRESH 4.5 // print space if no tone for this many element lengths +#define CW_DECODER_SPACE_DECODE_THRESH 2.0 // invoke character decode if no tone for this many element lengths +#define CW_DECODER_NOISE_FILTER 20 // ignore elements shorter than this (mS) + +#define STRAIGHT_KEY_ACTIVE_STATE LOW + +#ifdef FEATURE_DYNAMIC_DAH_TO_DIT_RATIO + #define DYNAMIC_DAH_TO_DIT_RATIO_LOWER_LIMIT_WPM 30 + #define DYNAMIC_DAH_TO_DIT_RATIO_LOWER_LIMIT_RATIO 300 // 300 = 3:1 ratio + #define DYNAMIC_DAH_TO_DIT_RATIO_UPPER_LIMIT_WPM 70 + #define DYNAMIC_DAH_TO_DIT_RATIO_UPPER_LIMIT_RATIO 240 // 240 = 2.4:1 ratio +#endif //FEATURE_DYNAMIC_DAH_TO_DIT_RATIO + +#if defined(FEATURE_COMPETITION_COMPRESSION_DETECTION) + #define COMPETITION_COMPRESSION_DETECTION_ARRAY_SIZE 16 + #define COMPETITION_COMPRESSION_DETECTION_TIME_INTERCHAR_LOWER_LIMIT 1 + #define COMPETITION_COMPRESSION_DETECTION_TIME_INTERCHAR_UPPER_LIMIT 6.0 + #define COMPETITION_COMPRESSION_DETECTION_AVERAGE_ALARM_THRESHOLD 3.0 +#endif //FEATURE_COMPETITION_COMPRESSION_DETECTION + +#if defined(FEATURE_SLEEP) + #define KEYER_AWAKE_PIN_AWAKE_STATE HIGH + #define KEYER_AWAKE_PIN_ASLEEP_STATE LOW +#endif + +#if defined(FEATURE_LCD_BACKLIGHT_AUTO_DIM) + #define keyer_power_led_awake_duty 255 // PWM duty cycle. 0 is 0%, 255 is 100% + #define keyer_power_led_asleep_duty 25 // 25 is quite dim. Use 0 for off +#endif + +#if defined(FEATURE_ETHERNET) + // #define FEATURE_ETHERNET_IP {192,168,1,178} // default IP address ("192.168.1.178") + // #define FEATURE_ETHERNET_MAC {0xDE,0xAD,0xBE,0xEF,0xFE,0xED} + #define FEATURE_ETHERNET_IP {192,168,1,179} // default IP address ("192.168.1.179") + #define FEATURE_ETHERNET_MAC {0xDE,0xAD,0xBE,0xEF,0xFE,0xEE} + #define FEATURE_ETHERNET_DNS {8,8,8,8} + + #define FEATURE_ETHERNET_GATEWAY {192,168,1,1} // default gateway + #define FEATURE_ETHERNET_SUBNET_MASK {255,255,255,0} // default subnet mask + #define FEATURE_ETHERNET_WEB_LISTENER_PORT 80 + #define FEATURE_UDP_SEND_BUFFER_SIZE 128 + #define FEATURE_UDP_RECEIVE_BUFFER_SIZE 128 +#endif //FEATURE_ETHERNET + +#define WEB_SERVER_CONTROL_TX_KEY_TIME_LIMIT_SECS 10 + +#define FEATURE_INTERNET_LINK_MAX_LINKS 2 +#define FEATURE_INTERNET_LINK_DEFAULT_RCV_UDP_PORT 8888 +#define FEATURE_INTERNET_LINK_BUFFER_TIME_MS 500 + +#if defined(FEATURE_4x4_KEYPAD)|| defined (FEATURE_3x4_KEYPAD) + #define KEYPAD_ROWS 4 //KeyPad Rows + #if defined(FEATURE_4x4_KEYPAD) + #define KEYPAD_COLS 4 //keypad Columns + #else + #define KEYPAD_COLS 3 + #endif + #define mem1 0 //Memory numbers for Keypad: Actual memory numbers start with "0" + #define mem2 1 + #define mem3 2 + #define mem4 3 + #define mem5 4 + #define mem6 5 + #define mem7 6 + #define mem8 7 + #define mem9 8 + #define mem10 9 + #define mem11 10 + #define mem12 11 +#endif //#if defined(FEATURE_4x4_KEYPAD)|| defined (FEATURE_3x4_KEYPAD) + +#define initial_sidetone_mode 1 // Sidetone mode, 0=OFF, 1=ON, 2=PADDLE_ONLY + +#define sd_card_spi_ss_line 4 + +#if defined(OPTION_DFROBOT_LCD_COMMAND_BUTTONS) + + // For V1.1 board use these values + #define dfrobot_btnRIGHT_analog 50 + #define dfrobot_btnUP_analog 250 + #define dfrobot_btnDOWN_analog 450 + #define dfrobot_btnLEFT_analog 650 + #define dfrobot_btnSELECT_analog 850 + + // For V1.0 board use these values + // #define dfrobot_btnRIGHT_analog 50 + // #define dfrobot_btnUP_analog 195 + // #define dfrobot_btnDOWN_analog 380 + // #define dfrobot_btnLEFT_analog 555 + // #define dfrobot_btnSELECT_analog 790 + + // button to memory mappings (0 = command button, 1 = memory 1, 2 = memory 2, etc.) + #define dfrobot_btnRIGHT 2 + #define dfrobot_btnUP 1 + #define dfrobot_btnDOWN 3 + #define dfrobot_btnLEFT 4 + #define dfrobot_btnSELECT 0 + #define dfrobot_btnNONE 255 // do not change + +#endif + +#define sequencer_pins_active_state HIGH +#define sequencer_pins_inactive_state LOW +#define ptt_line_active_state HIGH +#define ptt_line_inactive_state LOW +#define tx_key_line_active_state HIGH +#define tx_key_line_inactive_state LOW +#define ptt_input_pin_active_state LOW +#define ptt_input_pin_inactive_state HIGH +#define tx_inhibit_pin_active_state LOW +#define tx_inhibit_pin_inactive_state HIGH +#define tx_pause_pin_active_state LOW +#define tx_pause_pin_inactive_state HIGH +#define sidetone_line_active_state HIGH +#define sidetone_line_inactive_state LOW + +#if defined(ARDUINO_MAPLE_MINI) + #define button_value_factor 4095 //sp5iou contributed +#else + #define button_value_factor 1023 +#endif + +#define farnsworth_timing_calibration 1.15 + +#define sidetone_volume_low_limit 10 +#define sidetone_volume_high_limit 500 + +#define custom_startup_field "your custom text here" // an example could be callsign and name, eg. "AB1XYZ Bob", (or "Worlds best operator" which requires a 20 column display), string length shouldo be no more than the number of columns on the display + +#define command_mode_acknowledgement_character 'E' + +#if defined(FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT) + #define command_a_iambic_a "A" + #define command_b_iambic_b "B" + #define command_c_single_paddle "SINGLE" + #define command_d_ultimatic "ULT" + #define command_h_weight_dit_dah_ratio_default "R" + #define command_i_tx_on "TX ON" + #define command_i_tx_off "TX OFF" + #define command_k_dit_dah_buffers_on "ON" + #define command_k_dit_dah_buffers_off "OFF" + #define command_n_paddle_reverse "REV" + #define command_n_paddle_normal "NORM" + #define command_o_sidetone_off "ST OFF" + #define command_o_sidetone_paddle_only "ST PD ONLY" + #define command_o_sidetone_on "ST ON" + #define command_t_tune_mode "TUNE" + #define command_v_potentiometer_on "POT ON" + #define command_v_potentiometer_off "POT OFF" + + #define command_error "ERR" + +#endif //FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT diff --git a/k3ng_keyer/keyer_settings_megakeyer.h b/k3ng_keyer/keyer_settings_megakeyer.h new file mode 100755 index 0000000..97ccfad --- /dev/null +++ b/k3ng_keyer/keyer_settings_megakeyer.h @@ -0,0 +1,321 @@ +// +// Initial setting for W6IPA megakeyer v1.1 (CC) BY-NC-SA +// Project files are available here https://github.com/w6ipa/megakeyer +// +#define initial_speed_wpm 20 // "factory default" keyer speed setting +#define initial_command_mode_speed_wpm 20 // "factory default" command mode speed setting +#define initial_sidetone_freq 600 // "factory default" sidetone frequency setting +#define sidetone_hz_limit_low 299 +#define sidetone_hz_limit_high 2001 +#define sidetone_hz_limit_low 299 +#define sidetone_hz_limit_high 2001 +#define hz_high_beep 1500 // frequency in hertz of high beep +#define hz_low_beep 400 // frequency in hertz of low beep +#define initial_dah_to_dit_ratio 300 // 300 = 3 / normal 3:1 ratio +#define initial_ptt_lead_time_tx1 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx1 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx2 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx2 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx3 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx3 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx4 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx4 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx5 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx5 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx6 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx6 10 // PTT tail time in mS +#define initial_qrss_dit_length 1 // QRSS dit length in seconds +#define initial_pot_wpm_low_value 13 // Potentiometer WPM fully CCW +#define initial_pot_wpm_high_value 35 // Potentiometer WPM fully CW +#define wpm_limit_low 5 +#define wpm_limit_high 60 +#define potentiometer_change_threshold 0.9 // don't change the keyer speed until pot wpm has changed more than this +#define send_buffer_size 150 +#define default_length_letterspace 3 +#define default_length_wordspace 7 +#define default_keying_compensation 0 // number of milliseconds to extend all dits and dahs - for QSK on boatanchors +#define default_first_extension_time 0 // number of milliseconds to extend first sent dit or dah +#define default_pot_full_scale_reading 1023 +#define default_weighting 50 // 50 = weighting factor of 1 (normal) +#define default_ptt_hang_time_wordspace_units 0.0 +#define winkey_c0_wait_time 1 // the number of milliseconds to wait to send 0xc0 byte after send buffer has been sent +#define winkey_command_timeout_ms 5000 +#define winkey_discard_bytes_startup 3 // this is used if OPTION_WINKEY_DISCARD_BYTES_AT_STARTUP is enabled above +#define winkey_xoff_threshold 20 // the number of chars in the buffer when we begin sending XOFFs +#define winkey_xon_threshold 10 // the number of chars in the buffer below which we deactivate XOFF +#define default_memory_repeat_time 3000 // time in milliseconds +#define LCD_COLUMNS 16 +#define LCD_ROWS 2 +#define lcd_i2c_address_mathertel_PCF8574 0x27 // I2C address of display for FEATURE_LCD_MATHERTEL_PCF8574 +#define lcd_i2c_address_fdebrander_lcd 0x27 // I2C address of display for FEATURE_LCD_I2C_FDEBRABANDER +#define lcd_i2c_address_ydv1_lcd 0x27 // I2C address of display for FEATURE_LCD_YDv1 +//#define lcd_i2c_address_ydv1_lcd 0x38 // I2C address of display for FEATURE_LCD_YDv1 +#define lcd_i2c_address_sainsmart_lcd 0x27 // I2C address of display for FEATURE_LCD_SAINSMART_I2C +#define hell_pixel_microseconds 4025 +#define program_memory_limit_consec_spaces 1 +#define serial_leading_zeros 1 // set to 1 to activate leading zeros in serial numbers (i.e. #1 = 001) +#define serial_cut_numbers 0 // set to 1 to activate cut numbers in serial numbers (i.e. #10 = 1T, #19 = 1N) +#define go_to_sleep_inactivity_time 10 // minutes - FEATURE_SLEEP +#define dim_backlight_inactive_time 5 // minutes - FEATURE_LCD_BACKLIGHT_AUTO_DIM +#define default_cmos_super_keyer_iambic_b_timing_percent 33 // use with FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING; should be between 0 to 99 % (0% = true iambic b;100% = iambic a behavior) +#define default_cw_echo_timing_factor 1.75 // "factory default" setting +#define default_autospace_timing_factor 2.0 // "factory default" setting +#define winkey_paddle_echo_buffer_decode_timing_factor 0.25 +#define potentiometer_always_on 0 +#define ptt_interlock_check_every_ms 100 +#define ptt_interlock_active_state HIGH +#define unknown_cw_character '*' +#define cli_paddle_echo_on_at_boot 1 +#define cli_straight_key_echo_on_at_boot 1 +#define tx_key_dit_and_dah_pins_active_state HIGH +#define tx_key_dit_and_dah_pins_inactive_state LOW +#define potentiometer_check_interval_ms 150 +#define potentiometer_reading_threshold 1 +#define default_paddle_interruption_quiet_time_element_lengths 0 +#define default_wordsworth_wordspace 6 +#define default_wordsworth_repetition 1 +#define serial_program_memory_buffer_size 500 +#define eeprom_write_time_ms 30000 + +#ifdef FEATURE_BUTTONS + #define analog_buttons_number_of_buttons 4 // includes the command button (command button + 3 memory buttons = 4) + #define analog_buttons_r1 10 + #define analog_buttons_r2 1 +#endif + + +#if defined(FEATURE_BUTTONS) && !defined(FEATURE_PS2_KEYBOARD) && !defined(FEATURE_USB_KEYBOARD) && !defined(FEATURE_COMMAND_LINE_INTERFACE) && !defined(FEATURE_WINKEY_EMULATION) + #define number_of_memories byte(analog_buttons_number_of_buttons-1) +#else + #define number_of_memories byte(12) +#endif + +#ifdef FEATURE_CAPACITIVE_PADDLE_PINS + #define capacitance_threshold 2 +#endif //FEATURE_CAPACITIVE_PADDLE_PINS + +#ifdef FEATURE_LED_RING + #define led_ring_low_limit 10 + #define led_ring_high_limit 50 +#endif //FEATURE_LED_RING + +#ifdef FEATURE_QLF + #define qlf_dit_max 125 + #define qlf_dit_min 75 + #define qlf_dah_max 200 + #define qlf_dah_min 100 + #define qlf_on_by_default 0 +#endif //FEATURE_QLF + + +#ifdef FEATURE_WINKEY_EMULATION + #ifdef OPTION_WINKEY_UCXLOG_9600_BAUD + #define WINKEY_DEFAULT_BAUD 9600 + #else + #define WINKEY_DEFAULT_BAUD 1200 + #endif //OPTION_WINKEY_UCXLOG_9600_BAUD +// alter these below to map alternate sidetones for Winkey interface protocol emulation +#ifdef OPTION_WINKEY_2_SUPPORT + #define WINKEY_SIDETONE_1 3759 + #define WINKEY_SIDETONE_2 1879 + #define WINKEY_SIDETONE_3 1252 + #define WINKEY_SIDETONE_4 940 + #define WINKEY_SIDETONE_5 752 + #define WINKEY_SIDETONE_6 625 + #define WINKEY_SIDETONE_7 535 + #define WINKEY_SIDETONE_8 469 + #define WINKEY_SIDETONE_9 417 + #define WINKEY_SIDETONE_10 375 +#else //OPTION_WINKEY_2_SUPPORT + #define WINKEY_SIDETONE_1 4000 + #define WINKEY_SIDETONE_2 2000 + #define WINKEY_SIDETONE_3 1333 + #define WINKEY_SIDETONE_4 1000 + #define WINKEY_SIDETONE_5 800 + #define WINKEY_SIDETONE_6 666 + #define WINKEY_SIDETONE_7 571 + #define WINKEY_SIDETONE_8 500 + #define WINKEY_SIDETONE_9 444 + #define WINKEY_SIDETONE_10 400 +#endif //OPTION_WINKEY_2_SUPPORT + +#define WINKEY_1_REPORT_VERSION_NUMBER 10 +#define WINKEY_2_REPORT_VERSION_NUMBER 23 + +// alter these to map to alternate hang time wordspace units +#define WINKEY_HANG_TIME_1_0 1.0 +#define WINKEY_HANG_TIME_1_33 1.33 +#define WINKEY_HANG_TIME_1_66 1.66 +#define WINKEY_HANG_TIME_2_0 2.0 + +#define WINKEY_RETURN_THIS_FOR_ADMIN_GET_CAL 0x16 +#define WINKEY_RETURN_THIS_FOR_ADMIN_PADDLE_A2D 0xEE +#define WINKEY_RETURN_THIS_FOR_ADMIN_SPEED_A2D 0x00 + +#endif //FEATURE_WINKEY_EMULATION + + + +#define PRIMARY_SERIAL_PORT &Serial +#define PRIMARY_SERIAL_PORT_BAUD 115200 // This applies only when the port is in Command Line Interface mode. In Winkey mode it will default to 1200. + +#ifdef FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + #define SECONDARY_SERIAL_PORT &Serial1 + #define SECONDARY_SERIAL_PORT_BAUD 115200 +#endif + + +#define CW_DECODER_SCREEN_COLUMNS 120 // word wrap at this many columns +#define CW_DECODER_SPACE_PRINT_THRESH 4.5 // print space if no tone for this many element lengths +#define CW_DECODER_SPACE_DECODE_THRESH 2.0 // invoke character decode if no tone for this many element lengths +#define CW_DECODER_NOISE_FILTER 20 // ignore elements shorter than this (mS) + +#define STRAIGHT_KEY_ACTIVE_STATE LOW + +#ifdef FEATURE_DYNAMIC_DAH_TO_DIT_RATIO + #define DYNAMIC_DAH_TO_DIT_RATIO_LOWER_LIMIT_WPM 30 + #define DYNAMIC_DAH_TO_DIT_RATIO_LOWER_LIMIT_RATIO 300 // 300 = 3:1 ratio + #define DYNAMIC_DAH_TO_DIT_RATIO_UPPER_LIMIT_WPM 70 + #define DYNAMIC_DAH_TO_DIT_RATIO_UPPER_LIMIT_RATIO 240 // 240 = 2.4:1 ratio +#endif //FEATURE_DYNAMIC_DAH_TO_DIT_RATIO + +#if defined(FEATURE_COMPETITION_COMPRESSION_DETECTION) + #define COMPETITION_COMPRESSION_DETECTION_ARRAY_SIZE 16 + #define COMPETITION_COMPRESSION_DETECTION_TIME_INTERCHAR_LOWER_LIMIT 1 + #define COMPETITION_COMPRESSION_DETECTION_TIME_INTERCHAR_UPPER_LIMIT 6.0 + #define COMPETITION_COMPRESSION_DETECTION_AVERAGE_ALARM_THRESHOLD 3.0 +#endif //FEATURE_COMPETITION_COMPRESSION_DETECTION + +#if defined(FEATURE_SLEEP) + #define KEYER_AWAKE_PIN_AWAKE_STATE HIGH + #define KEYER_AWAKE_PIN_ASLEEP_STATE LOW +#endif + +#if defined(FEATURE_LCD_BACKLIGHT_AUTO_DIM) + #define keyer_power_led_awake_duty 255 // PWM duty cycle. 0 is 0%, 255 is 100% + #define keyer_power_led_asleep_duty 25 // 25 is quite dim. Use 0 for off +#endif + +#if defined(FEATURE_ETHERNET) + // #define FEATURE_ETHERNET_IP {192,168,1,178} // default IP address ("192.168.1.178") + // #define FEATURE_ETHERNET_MAC {0xDE,0xAD,0xBE,0xEF,0xFE,0xED} + #define FEATURE_ETHERNET_IP {192,168,1,179} // default IP address ("192.168.1.179") + #define FEATURE_ETHERNET_MAC {0xDE,0xAD,0xBE,0xEF,0xFE,0xEE} + #define FEATURE_ETHERNET_DNS {8,8,8,8} + + #define FEATURE_ETHERNET_GATEWAY {192,168,1,1} // default gateway + #define FEATURE_ETHERNET_SUBNET_MASK {255,255,255,0} // default subnet mask + #define FEATURE_ETHERNET_WEB_LISTENER_PORT 80 + #define FEATURE_UDP_SEND_BUFFER_SIZE 128 + #define FEATURE_UDP_RECEIVE_BUFFER_SIZE 128 +#endif //FEATURE_ETHERNET + +#define WEB_SERVER_CONTROL_TX_KEY_TIME_LIMIT_SECS 10 + +#define FEATURE_INTERNET_LINK_MAX_LINKS 2 +#define FEATURE_INTERNET_LINK_DEFAULT_RCV_UDP_PORT 8888 +#define FEATURE_INTERNET_LINK_BUFFER_TIME_MS 500 + +#if defined(FEATURE_4x4_KEYPAD)|| defined (FEATURE_3x4_KEYPAD) + #define KEYPAD_ROWS 4 //KeyPad Rows + #if defined(FEATURE_4x4_KEYPAD) + #define KEYPAD_COLS 4 //keypad Columns + #else + #define KEYPAD_COLS 3 + #endif + #define mem1 0 //Memory numbers for Keypad: Actual memory numbers start with "0" + #define mem2 1 + #define mem3 2 + #define mem4 3 + #define mem5 4 + #define mem6 5 + #define mem7 6 + #define mem8 7 + #define mem9 8 + #define mem10 9 + #define mem11 10 + #define mem12 11 +#endif //#if defined(FEATURE_4x4_KEYPAD)|| defined (FEATURE_3x4_KEYPAD) + +#define initial_sidetone_mode 1 // Sidetone mode, 0=OFF, 1=ON, 2=PADDLE_ONLY + +#define sd_card_spi_ss_line 4 + +#if defined(OPTION_DFROBOT_LCD_COMMAND_BUTTONS) + + // For V1.1 board use these values + #define dfrobot_btnRIGHT_analog 50 + #define dfrobot_btnUP_analog 250 + #define dfrobot_btnDOWN_analog 450 + #define dfrobot_btnLEFT_analog 650 + #define dfrobot_btnSELECT_analog 850 + + // For V1.0 board use these values + // #define dfrobot_btnRIGHT_analog 50 + // #define dfrobot_btnUP_analog 195 + // #define dfrobot_btnDOWN_analog 380 + // #define dfrobot_btnLEFT_analog 555 + // #define dfrobot_btnSELECT_analog 790 + + // button to memory mappings (0 = command button, 1 = memory 1, 2 = memory 2, etc.) + #define dfrobot_btnRIGHT 2 + #define dfrobot_btnUP 1 + #define dfrobot_btnDOWN 3 + #define dfrobot_btnLEFT 4 + #define dfrobot_btnSELECT 0 + #define dfrobot_btnNONE 255 // do not change + +#endif + +#define sequencer_pins_active_state HIGH +#define sequencer_pins_inactive_state LOW +#define ptt_line_active_state HIGH +#define ptt_line_inactive_state LOW +#define tx_key_line_active_state HIGH +#define tx_key_line_inactive_state LOW +#define ptt_input_pin_active_state LOW +#define ptt_input_pin_inactive_state HIGH +#define tx_inhibit_pin_active_state LOW +#define tx_inhibit_pin_inactive_state HIGH +#define tx_pause_pin_active_state LOW +#define tx_pause_pin_inactive_state HIGH +#define sidetone_line_active_state HIGH +#define sidetone_line_inactive_state LOW + +#if defined(ARDUINO_MAPLE_MINI) + #define button_value_factor 4095 //sp5iou contributed +#else + #define button_value_factor 1023 +#endif + +#define farnsworth_timing_calibration 1.15 + +#define sidetone_volume_low_limit 10 +#define sidetone_volume_high_limit 500 + +#define custom_startup_field "your custom text here" // an example could be callsign and name, eg. "AB1XYZ Bob", (or "Worlds best operator" which requires a 20 column display), string length shouldo be no more than the number of columns on the display + +#define command_mode_acknowledgement_character 'E' + +#if defined(FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT) + #define command_a_iambic_a "A" + #define command_b_iambic_b "B" + #define command_c_single_paddle "SINGLE" + #define command_d_ultimatic "ULT" + #define command_h_weight_dit_dah_ratio_default "R" + #define command_i_tx_on "TX ON" + #define command_i_tx_off "TX OFF" + #define command_k_dit_dah_buffers_on "ON" + #define command_k_dit_dah_buffers_off "OFF" + #define command_n_paddle_reverse "REV" + #define command_n_paddle_normal "NORM" + #define command_o_sidetone_off "ST OFF" + #define command_o_sidetone_paddle_only "ST PD ONLY" + #define command_o_sidetone_on "ST ON" + #define command_t_tune_mode "TUNE" + #define command_v_potentiometer_on "POT ON" + #define command_v_potentiometer_off "POT OFF" + + #define command_error "ERR" + +#endif //FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT diff --git a/k3ng_keyer/keyer_settings_mortty.h b/k3ng_keyer/keyer_settings_mortty.h new file mode 100755 index 0000000..a63ac30 --- /dev/null +++ b/k3ng_keyer/keyer_settings_mortty.h @@ -0,0 +1,318 @@ +// Initial and hardcoded settings +#define initial_speed_wpm 26 // "factory default" keyer speed setting +#define initial_command_mode_speed_wpm 20 // "factory default" command mode speed setting +#define initial_sidetone_freq 600 // "factory default" sidetone frequency setting +#define sidetone_hz_limit_low 299 +#define sidetone_hz_limit_high 2001 +#define hz_high_beep 1500 // frequency in hertz of high beep +#define hz_low_beep 400 // frequency in hertz of low beep +#define initial_dah_to_dit_ratio 300 // 300 = 3 / normal 3:1 ratio +#define initial_ptt_lead_time_tx1 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx1 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx2 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx2 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx3 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx3 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx4 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx4 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx5 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx5 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx6 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx6 10 // PTT tail time in mS +#define initial_qrss_dit_length 1 // QRSS dit length in seconds +#define initial_pot_wpm_low_value 13 // Potentiometer WPM fully CCW +#define initial_pot_wpm_high_value 35 // Potentiometer WPM fully CW +#define wpm_limit_low 5 +#define wpm_limit_high 60 +#define potentiometer_change_threshold 0.9 // don't change the keyer speed until pot wpm has changed more than this +#define send_buffer_size 150 +#define default_length_letterspace 3 +#define default_length_wordspace 7 +#define default_keying_compensation 0 // number of milliseconds to extend all dits and dahs - for QSK on boatanchors +#define default_first_extension_time 0 // number of milliseconds to extend first sent dit or dah +#define default_pot_full_scale_reading 1023 +#define default_weighting 50 // 50 = weighting factor of 1 (normal) +#define default_ptt_hang_time_wordspace_units 0.0 +#define winkey_c0_wait_time 1 // the number of milliseconds to wait to send 0xc0 byte after send buffer has been sent +#define winkey_command_timeout_ms 5000 +#define winkey_discard_bytes_startup 3 // this is used if OPTION_WINKEY_DISCARD_BYTES_AT_STARTUP is enabled above +#define winkey_xoff_threshold 20 // the number of chars in the buffer when we begin sending XOFFs +#define winkey_xon_threshold 10 // the number of chars in the buffer below which we deactivate XOFF +#define default_memory_repeat_time 3000 // time in milliseconds +#define LCD_COLUMNS 16 +#define LCD_ROWS 2 +#define lcd_i2c_address_mathertel_PCF8574 0x27 // I2C address of display for FEATURE_LCD_MATHERTEL_PCF8574 +#define lcd_i2c_address_fdebrander_lcd 0x27 // I2C address of display for FEATURE_LCD_I2C_FDEBRABANDER +#define lcd_i2c_address_ydv1_lcd 0x27 // I2C address of display for FEATURE_LCD_YDv1 +//#define lcd_i2c_address_ydv1_lcd 0x38 // I2C address of display for FEATURE_LCD_YDv1 +#define lcd_i2c_address_sainsmart_lcd 0x27 // I2C address of display for FEATURE_LCD_SAINSMART_I2C +#define hell_pixel_microseconds 4025 +#define program_memory_limit_consec_spaces 1 +#define serial_leading_zeros 1 // set to 1 to activate leading zeros in serial numbers (i.e. #1 = 001) +#define serial_cut_numbers 0 // set to 1 to activate cut numbers in serial numbers (i.e. #10 = 1T, #19 = 1N) +#define go_to_sleep_inactivity_time 10 // minutes - FEATURE_SLEEP +#define dim_backlight_inactive_time 5 // minutes - FEATURE_LCD_BACKLIGHT_AUTO_DIM +#define default_cmos_super_keyer_iambic_b_timing_percent 33 // use with FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING; should be between 0 to 99 % (0% = true iambic b;100% = iambic a behavior) +#define default_cw_echo_timing_factor 1.75 // "factory default" setting +#define default_autospace_timing_factor 2.0 // "factory default" setting +#define winkey_paddle_echo_buffer_decode_timing_factor 0.25 +#define potentiometer_always_on 0 +#define ptt_interlock_check_every_ms 100 +#define ptt_interlock_active_state HIGH +#define unknown_cw_character '*' +#define cli_paddle_echo_on_at_boot 1 +#define cli_straight_key_echo_on_at_boot 1 +#define tx_key_dit_and_dah_pins_active_state HIGH +#define tx_key_dit_and_dah_pins_inactive_state LOW +#define potentiometer_check_interval_ms 0 //=====150 +#define potentiometer_reading_threshold 1 +#define default_paddle_interruption_quiet_time_element_lengths 0 +#define default_wordsworth_wordspace 6 +#define default_wordsworth_repetition 1 +#define serial_program_memory_buffer_size 500 +#define eeprom_write_time_ms 30000 + +#ifdef FEATURE_BUTTONS + #define analog_buttons_number_of_buttons 4 + #define analog_buttons_r1 10 + #define analog_buttons_r2 1 +#endif + + +#if defined(FEATURE_BUTTONS) && !defined(FEATURE_PS2_KEYBOARD) && !defined(FEATURE_USB_KEYBOARD) && !defined(FEATURE_COMMAND_LINE_INTERFACE) && !defined(FEATURE_WINKEY_EMULATION) + #define number_of_memories byte(analog_buttons_number_of_buttons-1) +#else + #define number_of_memories byte(12) +#endif + +#ifdef FEATURE_CAPACITIVE_PADDLE_PINS + #define capacitance_threshold 2 +#endif //FEATURE_CAPACITIVE_PADDLE_PINS + +#ifdef FEATURE_LED_RING + #define led_ring_low_limit 10 + #define led_ring_high_limit 50 +#endif //FEATURE_LED_RING + +#ifdef FEATURE_QLF + #define qlf_dit_max 125 + #define qlf_dit_min 75 + #define qlf_dah_max 200 + #define qlf_dah_min 100 + #define qlf_on_by_default 0 +#endif //FEATURE_QLF + + +#ifdef FEATURE_WINKEY_EMULATION + #ifdef OPTION_WINKEY_UCXLOG_9600_BAUD + #define WINKEY_DEFAULT_BAUD 9600 + #else + #define WINKEY_DEFAULT_BAUD 1200 + #endif //OPTION_WINKEY_UCXLOG_9600_BAUD +// alter these below to map alternate sidetones for Winkey interface protocol emulation +#ifdef OPTION_WINKEY_2_SUPPORT + #define WINKEY_SIDETONE_1 3759 + #define WINKEY_SIDETONE_2 1879 + #define WINKEY_SIDETONE_3 1252 + #define WINKEY_SIDETONE_4 940 + #define WINKEY_SIDETONE_5 752 + #define WINKEY_SIDETONE_6 625 + #define WINKEY_SIDETONE_7 535 + #define WINKEY_SIDETONE_8 469 + #define WINKEY_SIDETONE_9 417 + #define WINKEY_SIDETONE_10 375 +#else //OPTION_WINKEY_2_SUPPORT + #define WINKEY_SIDETONE_1 4000 + #define WINKEY_SIDETONE_2 2000 + #define WINKEY_SIDETONE_3 1333 + #define WINKEY_SIDETONE_4 1000 + #define WINKEY_SIDETONE_5 800 + #define WINKEY_SIDETONE_6 666 + #define WINKEY_SIDETONE_7 571 + #define WINKEY_SIDETONE_8 500 + #define WINKEY_SIDETONE_9 444 + #define WINKEY_SIDETONE_10 400 +#endif //OPTION_WINKEY_2_SUPPORT + +#define WINKEY_1_REPORT_VERSION_NUMBER 10 +#define WINKEY_2_REPORT_VERSION_NUMBER 23 + +// alter these to map to alternate hang time wordspace units +#define WINKEY_HANG_TIME_1_0 1.0 +#define WINKEY_HANG_TIME_1_33 1.33 +#define WINKEY_HANG_TIME_1_66 1.66 +#define WINKEY_HANG_TIME_2_0 2.0 + +#define WINKEY_RETURN_THIS_FOR_ADMIN_GET_CAL 0x16 +#define WINKEY_RETURN_THIS_FOR_ADMIN_PADDLE_A2D 0xEE +#define WINKEY_RETURN_THIS_FOR_ADMIN_SPEED_A2D 0x00 + +#endif //FEATURE_WINKEY_EMULATION + + + +#define PRIMARY_SERIAL_PORT &Serial +#define PRIMARY_SERIAL_PORT_BAUD 115200 // This applies only when the port is in Command Line Interface mode. In Winkey mode it will default to 1200. + +#ifdef FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + #define SECONDARY_SERIAL_PORT &Serial1 + #define SECONDARY_SERIAL_PORT_BAUD 115200 +#endif + + +#define CW_DECODER_SCREEN_COLUMNS 120 // word wrap at this many columns +#define CW_DECODER_SPACE_PRINT_THRESH 4.5 // print space if no tone for this many element lengths +#define CW_DECODER_SPACE_DECODE_THRESH 2.0 // invoke character decode if no tone for this many element lengths +#define CW_DECODER_NOISE_FILTER 20 // ignore elements shorter than this (mS) + +#define STRAIGHT_KEY_ACTIVE_STATE LOW + +#ifdef FEATURE_DYNAMIC_DAH_TO_DIT_RATIO + #define DYNAMIC_DAH_TO_DIT_RATIO_LOWER_LIMIT_WPM 30 + #define DYNAMIC_DAH_TO_DIT_RATIO_LOWER_LIMIT_RATIO 300 // 300 = 3:1 ratio + #define DYNAMIC_DAH_TO_DIT_RATIO_UPPER_LIMIT_WPM 70 + #define DYNAMIC_DAH_TO_DIT_RATIO_UPPER_LIMIT_RATIO 240 // 240 = 2.4:1 ratio +#endif //FEATURE_DYNAMIC_DAH_TO_DIT_RATIO + +#if defined(FEATURE_COMPETITION_COMPRESSION_DETECTION) + #define COMPETITION_COMPRESSION_DETECTION_ARRAY_SIZE 16 + #define COMPETITION_COMPRESSION_DETECTION_TIME_INTERCHAR_LOWER_LIMIT 1 + #define COMPETITION_COMPRESSION_DETECTION_TIME_INTERCHAR_UPPER_LIMIT 6.0 + #define COMPETITION_COMPRESSION_DETECTION_AVERAGE_ALARM_THRESHOLD 3.0 +#endif //FEATURE_COMPETITION_COMPRESSION_DETECTION + +#if defined(FEATURE_SLEEP) + #define KEYER_AWAKE_PIN_AWAKE_STATE HIGH + #define KEYER_AWAKE_PIN_ASLEEP_STATE LOW +#endif + +#if defined(FEATURE_LCD_BACKLIGHT_AUTO_DIM) + #define keyer_power_led_awake_duty 255 // PWM duty cycle. 0 is 0%, 255 is 100% + #define keyer_power_led_asleep_duty 25 // 25 is quite dim. Use 0 for off +#endif + +#if defined(FEATURE_ETHERNET) + // #define FEATURE_ETHERNET_IP {192,168,1,178} // default IP address ("192.168.1.178") + // #define FEATURE_ETHERNET_MAC {0xDE,0xAD,0xBE,0xEF,0xFE,0xED} + #define FEATURE_ETHERNET_IP {192,168,1,179} // default IP address ("192.168.1.179") + #define FEATURE_ETHERNET_MAC {0xDE,0xAD,0xBE,0xEF,0xFE,0xEE} + #define FEATURE_ETHERNET_DNS {8,8,8,8} + + #define FEATURE_ETHERNET_GATEWAY {192,168,1,1} // default gateway + #define FEATURE_ETHERNET_SUBNET_MASK {255,255,255,0} // default subnet mask + #define FEATURE_ETHERNET_WEB_LISTENER_PORT 80 + #define FEATURE_UDP_SEND_BUFFER_SIZE 128 + #define FEATURE_UDP_RECEIVE_BUFFER_SIZE 128 +#endif //FEATURE_ETHERNET + +#define WEB_SERVER_CONTROL_TX_KEY_TIME_LIMIT_SECS 10 + +#define FEATURE_INTERNET_LINK_MAX_LINKS 2 +#define FEATURE_INTERNET_LINK_DEFAULT_RCV_UDP_PORT 8888 +#define FEATURE_INTERNET_LINK_BUFFER_TIME_MS 500 + +#if defined(FEATURE_4x4_KEYPAD)|| defined (FEATURE_3x4_KEYPAD) + #define KEYPAD_ROWS 4 //KeyPad Rows + #if defined(FEATURE_4x4_KEYPAD) + #define KEYPAD_COLS 4 //keypad Columns + #else + #define KEYPAD_COLS 3 + #endif + #define mem1 0 //Memory numbers for Keypad: Actual memory numbers start with "0" + #define mem2 1 + #define mem3 2 + #define mem4 3 + #define mem5 4 + #define mem6 5 + #define mem7 6 + #define mem8 7 + #define mem9 8 + #define mem10 9 + #define mem11 10 + #define mem12 11 +#endif //#if defined(FEATURE_4x4_KEYPAD)|| defined (FEATURE_3x4_KEYPAD) + +#define initial_sidetone_mode 1 // Sidetone mode, 0=OFF, 1=ON, 2=PADDLE_ONLY + +#define sd_card_spi_ss_line 4 + + +#if defined(OPTION_DFROBOT_LCD_COMMAND_BUTTONS) + + // For V1.1 board use these values + #define dfrobot_btnRIGHT_analog 50 + #define dfrobot_btnUP_analog 250 + #define dfrobot_btnDOWN_analog 450 + #define dfrobot_btnLEFT_analog 650 + #define dfrobot_btnSELECT_analog 850 + + // For V1.0 board use these values + // #define dfrobot_btnRIGHT_analog 50 + // #define dfrobot_btnUP_analog 195 + // #define dfrobot_btnDOWN_analog 380 + // #define dfrobot_btnLEFT_analog 555 + // #define dfrobot_btnSELECT_analog 790 + + // button to memory mappings (0 = command button, 1 = memory 1, 2 = memory 2, etc.) + #define dfrobot_btnRIGHT 2 + #define dfrobot_btnUP 1 + #define dfrobot_btnDOWN 3 + #define dfrobot_btnLEFT 4 + #define dfrobot_btnSELECT 0 + #define dfrobot_btnNONE 255 // do not change + +#endif + + +#define sequencer_pins_active_state HIGH +#define sequencer_pins_inactive_state LOW +#define ptt_line_active_state HIGH +#define ptt_line_inactive_state LOW +#define tx_key_line_active_state HIGH +#define tx_key_line_inactive_state LOW +#define ptt_input_pin_active_state LOW +#define ptt_input_pin_inactive_state HIGH +#define tx_inhibit_pin_active_state LOW +#define tx_inhibit_pin_inactive_state HIGH +#define tx_pause_pin_active_state LOW +#define tx_pause_pin_inactive_state HIGH +#define sidetone_line_active_state HIGH +#define sidetone_line_inactive_state LOW + +#if defined(ARDUINO_MAPLE_MINI) + #define button_value_factor 4095 +#else + #define button_value_factor 1023 +#endif + +#define farnsworth_timing_calibration 1.15 + +#define sidetone_volume_low_limit 10 +#define sidetone_volume_high_limit 500 + +#define custom_startup_field "your custom text here" // an example could be callsign and name, eg. "AB1XYZ Bob", (or "Worlds best operator" which requires a 20 column display), string length shouldo be no more than the number of columns on the display + +#define command_mode_acknowledgement_character 'E' + +#if defined(FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT) + #define command_a_iambic_a "A" + #define command_b_iambic_b "B" + #define command_c_single_paddle "SINGLE" + #define command_d_ultimatic "ULT" + #define command_h_weight_dit_dah_ratio_default "R" + #define command_i_tx_on "TX ON" + #define command_i_tx_off "TX OFF" + #define command_k_dit_dah_buffers_on "ON" + #define command_k_dit_dah_buffers_off "OFF" + #define command_n_paddle_reverse "REV" + #define command_n_paddle_normal "NORM" + #define command_o_sidetone_off "ST OFF" + #define command_o_sidetone_paddle_only "ST PD ONLY" + #define command_o_sidetone_on "ST ON" + #define command_t_tune_mode "TUNE" + #define command_v_potentiometer_on "POT ON" + #define command_v_potentiometer_off "POT OFF" + + #define command_error "ERR" + +#endif //FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT diff --git a/k3ng_keyer/keyer_settings_mortty_regular.h b/k3ng_keyer/keyer_settings_mortty_regular.h new file mode 100755 index 0000000..a63ac30 --- /dev/null +++ b/k3ng_keyer/keyer_settings_mortty_regular.h @@ -0,0 +1,318 @@ +// Initial and hardcoded settings +#define initial_speed_wpm 26 // "factory default" keyer speed setting +#define initial_command_mode_speed_wpm 20 // "factory default" command mode speed setting +#define initial_sidetone_freq 600 // "factory default" sidetone frequency setting +#define sidetone_hz_limit_low 299 +#define sidetone_hz_limit_high 2001 +#define hz_high_beep 1500 // frequency in hertz of high beep +#define hz_low_beep 400 // frequency in hertz of low beep +#define initial_dah_to_dit_ratio 300 // 300 = 3 / normal 3:1 ratio +#define initial_ptt_lead_time_tx1 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx1 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx2 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx2 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx3 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx3 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx4 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx4 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx5 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx5 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx6 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx6 10 // PTT tail time in mS +#define initial_qrss_dit_length 1 // QRSS dit length in seconds +#define initial_pot_wpm_low_value 13 // Potentiometer WPM fully CCW +#define initial_pot_wpm_high_value 35 // Potentiometer WPM fully CW +#define wpm_limit_low 5 +#define wpm_limit_high 60 +#define potentiometer_change_threshold 0.9 // don't change the keyer speed until pot wpm has changed more than this +#define send_buffer_size 150 +#define default_length_letterspace 3 +#define default_length_wordspace 7 +#define default_keying_compensation 0 // number of milliseconds to extend all dits and dahs - for QSK on boatanchors +#define default_first_extension_time 0 // number of milliseconds to extend first sent dit or dah +#define default_pot_full_scale_reading 1023 +#define default_weighting 50 // 50 = weighting factor of 1 (normal) +#define default_ptt_hang_time_wordspace_units 0.0 +#define winkey_c0_wait_time 1 // the number of milliseconds to wait to send 0xc0 byte after send buffer has been sent +#define winkey_command_timeout_ms 5000 +#define winkey_discard_bytes_startup 3 // this is used if OPTION_WINKEY_DISCARD_BYTES_AT_STARTUP is enabled above +#define winkey_xoff_threshold 20 // the number of chars in the buffer when we begin sending XOFFs +#define winkey_xon_threshold 10 // the number of chars in the buffer below which we deactivate XOFF +#define default_memory_repeat_time 3000 // time in milliseconds +#define LCD_COLUMNS 16 +#define LCD_ROWS 2 +#define lcd_i2c_address_mathertel_PCF8574 0x27 // I2C address of display for FEATURE_LCD_MATHERTEL_PCF8574 +#define lcd_i2c_address_fdebrander_lcd 0x27 // I2C address of display for FEATURE_LCD_I2C_FDEBRABANDER +#define lcd_i2c_address_ydv1_lcd 0x27 // I2C address of display for FEATURE_LCD_YDv1 +//#define lcd_i2c_address_ydv1_lcd 0x38 // I2C address of display for FEATURE_LCD_YDv1 +#define lcd_i2c_address_sainsmart_lcd 0x27 // I2C address of display for FEATURE_LCD_SAINSMART_I2C +#define hell_pixel_microseconds 4025 +#define program_memory_limit_consec_spaces 1 +#define serial_leading_zeros 1 // set to 1 to activate leading zeros in serial numbers (i.e. #1 = 001) +#define serial_cut_numbers 0 // set to 1 to activate cut numbers in serial numbers (i.e. #10 = 1T, #19 = 1N) +#define go_to_sleep_inactivity_time 10 // minutes - FEATURE_SLEEP +#define dim_backlight_inactive_time 5 // minutes - FEATURE_LCD_BACKLIGHT_AUTO_DIM +#define default_cmos_super_keyer_iambic_b_timing_percent 33 // use with FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING; should be between 0 to 99 % (0% = true iambic b;100% = iambic a behavior) +#define default_cw_echo_timing_factor 1.75 // "factory default" setting +#define default_autospace_timing_factor 2.0 // "factory default" setting +#define winkey_paddle_echo_buffer_decode_timing_factor 0.25 +#define potentiometer_always_on 0 +#define ptt_interlock_check_every_ms 100 +#define ptt_interlock_active_state HIGH +#define unknown_cw_character '*' +#define cli_paddle_echo_on_at_boot 1 +#define cli_straight_key_echo_on_at_boot 1 +#define tx_key_dit_and_dah_pins_active_state HIGH +#define tx_key_dit_and_dah_pins_inactive_state LOW +#define potentiometer_check_interval_ms 0 //=====150 +#define potentiometer_reading_threshold 1 +#define default_paddle_interruption_quiet_time_element_lengths 0 +#define default_wordsworth_wordspace 6 +#define default_wordsworth_repetition 1 +#define serial_program_memory_buffer_size 500 +#define eeprom_write_time_ms 30000 + +#ifdef FEATURE_BUTTONS + #define analog_buttons_number_of_buttons 4 + #define analog_buttons_r1 10 + #define analog_buttons_r2 1 +#endif + + +#if defined(FEATURE_BUTTONS) && !defined(FEATURE_PS2_KEYBOARD) && !defined(FEATURE_USB_KEYBOARD) && !defined(FEATURE_COMMAND_LINE_INTERFACE) && !defined(FEATURE_WINKEY_EMULATION) + #define number_of_memories byte(analog_buttons_number_of_buttons-1) +#else + #define number_of_memories byte(12) +#endif + +#ifdef FEATURE_CAPACITIVE_PADDLE_PINS + #define capacitance_threshold 2 +#endif //FEATURE_CAPACITIVE_PADDLE_PINS + +#ifdef FEATURE_LED_RING + #define led_ring_low_limit 10 + #define led_ring_high_limit 50 +#endif //FEATURE_LED_RING + +#ifdef FEATURE_QLF + #define qlf_dit_max 125 + #define qlf_dit_min 75 + #define qlf_dah_max 200 + #define qlf_dah_min 100 + #define qlf_on_by_default 0 +#endif //FEATURE_QLF + + +#ifdef FEATURE_WINKEY_EMULATION + #ifdef OPTION_WINKEY_UCXLOG_9600_BAUD + #define WINKEY_DEFAULT_BAUD 9600 + #else + #define WINKEY_DEFAULT_BAUD 1200 + #endif //OPTION_WINKEY_UCXLOG_9600_BAUD +// alter these below to map alternate sidetones for Winkey interface protocol emulation +#ifdef OPTION_WINKEY_2_SUPPORT + #define WINKEY_SIDETONE_1 3759 + #define WINKEY_SIDETONE_2 1879 + #define WINKEY_SIDETONE_3 1252 + #define WINKEY_SIDETONE_4 940 + #define WINKEY_SIDETONE_5 752 + #define WINKEY_SIDETONE_6 625 + #define WINKEY_SIDETONE_7 535 + #define WINKEY_SIDETONE_8 469 + #define WINKEY_SIDETONE_9 417 + #define WINKEY_SIDETONE_10 375 +#else //OPTION_WINKEY_2_SUPPORT + #define WINKEY_SIDETONE_1 4000 + #define WINKEY_SIDETONE_2 2000 + #define WINKEY_SIDETONE_3 1333 + #define WINKEY_SIDETONE_4 1000 + #define WINKEY_SIDETONE_5 800 + #define WINKEY_SIDETONE_6 666 + #define WINKEY_SIDETONE_7 571 + #define WINKEY_SIDETONE_8 500 + #define WINKEY_SIDETONE_9 444 + #define WINKEY_SIDETONE_10 400 +#endif //OPTION_WINKEY_2_SUPPORT + +#define WINKEY_1_REPORT_VERSION_NUMBER 10 +#define WINKEY_2_REPORT_VERSION_NUMBER 23 + +// alter these to map to alternate hang time wordspace units +#define WINKEY_HANG_TIME_1_0 1.0 +#define WINKEY_HANG_TIME_1_33 1.33 +#define WINKEY_HANG_TIME_1_66 1.66 +#define WINKEY_HANG_TIME_2_0 2.0 + +#define WINKEY_RETURN_THIS_FOR_ADMIN_GET_CAL 0x16 +#define WINKEY_RETURN_THIS_FOR_ADMIN_PADDLE_A2D 0xEE +#define WINKEY_RETURN_THIS_FOR_ADMIN_SPEED_A2D 0x00 + +#endif //FEATURE_WINKEY_EMULATION + + + +#define PRIMARY_SERIAL_PORT &Serial +#define PRIMARY_SERIAL_PORT_BAUD 115200 // This applies only when the port is in Command Line Interface mode. In Winkey mode it will default to 1200. + +#ifdef FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + #define SECONDARY_SERIAL_PORT &Serial1 + #define SECONDARY_SERIAL_PORT_BAUD 115200 +#endif + + +#define CW_DECODER_SCREEN_COLUMNS 120 // word wrap at this many columns +#define CW_DECODER_SPACE_PRINT_THRESH 4.5 // print space if no tone for this many element lengths +#define CW_DECODER_SPACE_DECODE_THRESH 2.0 // invoke character decode if no tone for this many element lengths +#define CW_DECODER_NOISE_FILTER 20 // ignore elements shorter than this (mS) + +#define STRAIGHT_KEY_ACTIVE_STATE LOW + +#ifdef FEATURE_DYNAMIC_DAH_TO_DIT_RATIO + #define DYNAMIC_DAH_TO_DIT_RATIO_LOWER_LIMIT_WPM 30 + #define DYNAMIC_DAH_TO_DIT_RATIO_LOWER_LIMIT_RATIO 300 // 300 = 3:1 ratio + #define DYNAMIC_DAH_TO_DIT_RATIO_UPPER_LIMIT_WPM 70 + #define DYNAMIC_DAH_TO_DIT_RATIO_UPPER_LIMIT_RATIO 240 // 240 = 2.4:1 ratio +#endif //FEATURE_DYNAMIC_DAH_TO_DIT_RATIO + +#if defined(FEATURE_COMPETITION_COMPRESSION_DETECTION) + #define COMPETITION_COMPRESSION_DETECTION_ARRAY_SIZE 16 + #define COMPETITION_COMPRESSION_DETECTION_TIME_INTERCHAR_LOWER_LIMIT 1 + #define COMPETITION_COMPRESSION_DETECTION_TIME_INTERCHAR_UPPER_LIMIT 6.0 + #define COMPETITION_COMPRESSION_DETECTION_AVERAGE_ALARM_THRESHOLD 3.0 +#endif //FEATURE_COMPETITION_COMPRESSION_DETECTION + +#if defined(FEATURE_SLEEP) + #define KEYER_AWAKE_PIN_AWAKE_STATE HIGH + #define KEYER_AWAKE_PIN_ASLEEP_STATE LOW +#endif + +#if defined(FEATURE_LCD_BACKLIGHT_AUTO_DIM) + #define keyer_power_led_awake_duty 255 // PWM duty cycle. 0 is 0%, 255 is 100% + #define keyer_power_led_asleep_duty 25 // 25 is quite dim. Use 0 for off +#endif + +#if defined(FEATURE_ETHERNET) + // #define FEATURE_ETHERNET_IP {192,168,1,178} // default IP address ("192.168.1.178") + // #define FEATURE_ETHERNET_MAC {0xDE,0xAD,0xBE,0xEF,0xFE,0xED} + #define FEATURE_ETHERNET_IP {192,168,1,179} // default IP address ("192.168.1.179") + #define FEATURE_ETHERNET_MAC {0xDE,0xAD,0xBE,0xEF,0xFE,0xEE} + #define FEATURE_ETHERNET_DNS {8,8,8,8} + + #define FEATURE_ETHERNET_GATEWAY {192,168,1,1} // default gateway + #define FEATURE_ETHERNET_SUBNET_MASK {255,255,255,0} // default subnet mask + #define FEATURE_ETHERNET_WEB_LISTENER_PORT 80 + #define FEATURE_UDP_SEND_BUFFER_SIZE 128 + #define FEATURE_UDP_RECEIVE_BUFFER_SIZE 128 +#endif //FEATURE_ETHERNET + +#define WEB_SERVER_CONTROL_TX_KEY_TIME_LIMIT_SECS 10 + +#define FEATURE_INTERNET_LINK_MAX_LINKS 2 +#define FEATURE_INTERNET_LINK_DEFAULT_RCV_UDP_PORT 8888 +#define FEATURE_INTERNET_LINK_BUFFER_TIME_MS 500 + +#if defined(FEATURE_4x4_KEYPAD)|| defined (FEATURE_3x4_KEYPAD) + #define KEYPAD_ROWS 4 //KeyPad Rows + #if defined(FEATURE_4x4_KEYPAD) + #define KEYPAD_COLS 4 //keypad Columns + #else + #define KEYPAD_COLS 3 + #endif + #define mem1 0 //Memory numbers for Keypad: Actual memory numbers start with "0" + #define mem2 1 + #define mem3 2 + #define mem4 3 + #define mem5 4 + #define mem6 5 + #define mem7 6 + #define mem8 7 + #define mem9 8 + #define mem10 9 + #define mem11 10 + #define mem12 11 +#endif //#if defined(FEATURE_4x4_KEYPAD)|| defined (FEATURE_3x4_KEYPAD) + +#define initial_sidetone_mode 1 // Sidetone mode, 0=OFF, 1=ON, 2=PADDLE_ONLY + +#define sd_card_spi_ss_line 4 + + +#if defined(OPTION_DFROBOT_LCD_COMMAND_BUTTONS) + + // For V1.1 board use these values + #define dfrobot_btnRIGHT_analog 50 + #define dfrobot_btnUP_analog 250 + #define dfrobot_btnDOWN_analog 450 + #define dfrobot_btnLEFT_analog 650 + #define dfrobot_btnSELECT_analog 850 + + // For V1.0 board use these values + // #define dfrobot_btnRIGHT_analog 50 + // #define dfrobot_btnUP_analog 195 + // #define dfrobot_btnDOWN_analog 380 + // #define dfrobot_btnLEFT_analog 555 + // #define dfrobot_btnSELECT_analog 790 + + // button to memory mappings (0 = command button, 1 = memory 1, 2 = memory 2, etc.) + #define dfrobot_btnRIGHT 2 + #define dfrobot_btnUP 1 + #define dfrobot_btnDOWN 3 + #define dfrobot_btnLEFT 4 + #define dfrobot_btnSELECT 0 + #define dfrobot_btnNONE 255 // do not change + +#endif + + +#define sequencer_pins_active_state HIGH +#define sequencer_pins_inactive_state LOW +#define ptt_line_active_state HIGH +#define ptt_line_inactive_state LOW +#define tx_key_line_active_state HIGH +#define tx_key_line_inactive_state LOW +#define ptt_input_pin_active_state LOW +#define ptt_input_pin_inactive_state HIGH +#define tx_inhibit_pin_active_state LOW +#define tx_inhibit_pin_inactive_state HIGH +#define tx_pause_pin_active_state LOW +#define tx_pause_pin_inactive_state HIGH +#define sidetone_line_active_state HIGH +#define sidetone_line_inactive_state LOW + +#if defined(ARDUINO_MAPLE_MINI) + #define button_value_factor 4095 +#else + #define button_value_factor 1023 +#endif + +#define farnsworth_timing_calibration 1.15 + +#define sidetone_volume_low_limit 10 +#define sidetone_volume_high_limit 500 + +#define custom_startup_field "your custom text here" // an example could be callsign and name, eg. "AB1XYZ Bob", (or "Worlds best operator" which requires a 20 column display), string length shouldo be no more than the number of columns on the display + +#define command_mode_acknowledgement_character 'E' + +#if defined(FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT) + #define command_a_iambic_a "A" + #define command_b_iambic_b "B" + #define command_c_single_paddle "SINGLE" + #define command_d_ultimatic "ULT" + #define command_h_weight_dit_dah_ratio_default "R" + #define command_i_tx_on "TX ON" + #define command_i_tx_off "TX OFF" + #define command_k_dit_dah_buffers_on "ON" + #define command_k_dit_dah_buffers_off "OFF" + #define command_n_paddle_reverse "REV" + #define command_n_paddle_normal "NORM" + #define command_o_sidetone_off "ST OFF" + #define command_o_sidetone_paddle_only "ST PD ONLY" + #define command_o_sidetone_on "ST ON" + #define command_t_tune_mode "TUNE" + #define command_v_potentiometer_on "POT ON" + #define command_v_potentiometer_off "POT OFF" + + #define command_error "ERR" + +#endif //FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT diff --git a/k3ng_keyer/keyer_settings_mortty_regular_with_potentiometer.h b/k3ng_keyer/keyer_settings_mortty_regular_with_potentiometer.h new file mode 100755 index 0000000..a63ac30 --- /dev/null +++ b/k3ng_keyer/keyer_settings_mortty_regular_with_potentiometer.h @@ -0,0 +1,318 @@ +// Initial and hardcoded settings +#define initial_speed_wpm 26 // "factory default" keyer speed setting +#define initial_command_mode_speed_wpm 20 // "factory default" command mode speed setting +#define initial_sidetone_freq 600 // "factory default" sidetone frequency setting +#define sidetone_hz_limit_low 299 +#define sidetone_hz_limit_high 2001 +#define hz_high_beep 1500 // frequency in hertz of high beep +#define hz_low_beep 400 // frequency in hertz of low beep +#define initial_dah_to_dit_ratio 300 // 300 = 3 / normal 3:1 ratio +#define initial_ptt_lead_time_tx1 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx1 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx2 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx2 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx3 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx3 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx4 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx4 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx5 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx5 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx6 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx6 10 // PTT tail time in mS +#define initial_qrss_dit_length 1 // QRSS dit length in seconds +#define initial_pot_wpm_low_value 13 // Potentiometer WPM fully CCW +#define initial_pot_wpm_high_value 35 // Potentiometer WPM fully CW +#define wpm_limit_low 5 +#define wpm_limit_high 60 +#define potentiometer_change_threshold 0.9 // don't change the keyer speed until pot wpm has changed more than this +#define send_buffer_size 150 +#define default_length_letterspace 3 +#define default_length_wordspace 7 +#define default_keying_compensation 0 // number of milliseconds to extend all dits and dahs - for QSK on boatanchors +#define default_first_extension_time 0 // number of milliseconds to extend first sent dit or dah +#define default_pot_full_scale_reading 1023 +#define default_weighting 50 // 50 = weighting factor of 1 (normal) +#define default_ptt_hang_time_wordspace_units 0.0 +#define winkey_c0_wait_time 1 // the number of milliseconds to wait to send 0xc0 byte after send buffer has been sent +#define winkey_command_timeout_ms 5000 +#define winkey_discard_bytes_startup 3 // this is used if OPTION_WINKEY_DISCARD_BYTES_AT_STARTUP is enabled above +#define winkey_xoff_threshold 20 // the number of chars in the buffer when we begin sending XOFFs +#define winkey_xon_threshold 10 // the number of chars in the buffer below which we deactivate XOFF +#define default_memory_repeat_time 3000 // time in milliseconds +#define LCD_COLUMNS 16 +#define LCD_ROWS 2 +#define lcd_i2c_address_mathertel_PCF8574 0x27 // I2C address of display for FEATURE_LCD_MATHERTEL_PCF8574 +#define lcd_i2c_address_fdebrander_lcd 0x27 // I2C address of display for FEATURE_LCD_I2C_FDEBRABANDER +#define lcd_i2c_address_ydv1_lcd 0x27 // I2C address of display for FEATURE_LCD_YDv1 +//#define lcd_i2c_address_ydv1_lcd 0x38 // I2C address of display for FEATURE_LCD_YDv1 +#define lcd_i2c_address_sainsmart_lcd 0x27 // I2C address of display for FEATURE_LCD_SAINSMART_I2C +#define hell_pixel_microseconds 4025 +#define program_memory_limit_consec_spaces 1 +#define serial_leading_zeros 1 // set to 1 to activate leading zeros in serial numbers (i.e. #1 = 001) +#define serial_cut_numbers 0 // set to 1 to activate cut numbers in serial numbers (i.e. #10 = 1T, #19 = 1N) +#define go_to_sleep_inactivity_time 10 // minutes - FEATURE_SLEEP +#define dim_backlight_inactive_time 5 // minutes - FEATURE_LCD_BACKLIGHT_AUTO_DIM +#define default_cmos_super_keyer_iambic_b_timing_percent 33 // use with FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING; should be between 0 to 99 % (0% = true iambic b;100% = iambic a behavior) +#define default_cw_echo_timing_factor 1.75 // "factory default" setting +#define default_autospace_timing_factor 2.0 // "factory default" setting +#define winkey_paddle_echo_buffer_decode_timing_factor 0.25 +#define potentiometer_always_on 0 +#define ptt_interlock_check_every_ms 100 +#define ptt_interlock_active_state HIGH +#define unknown_cw_character '*' +#define cli_paddle_echo_on_at_boot 1 +#define cli_straight_key_echo_on_at_boot 1 +#define tx_key_dit_and_dah_pins_active_state HIGH +#define tx_key_dit_and_dah_pins_inactive_state LOW +#define potentiometer_check_interval_ms 0 //=====150 +#define potentiometer_reading_threshold 1 +#define default_paddle_interruption_quiet_time_element_lengths 0 +#define default_wordsworth_wordspace 6 +#define default_wordsworth_repetition 1 +#define serial_program_memory_buffer_size 500 +#define eeprom_write_time_ms 30000 + +#ifdef FEATURE_BUTTONS + #define analog_buttons_number_of_buttons 4 + #define analog_buttons_r1 10 + #define analog_buttons_r2 1 +#endif + + +#if defined(FEATURE_BUTTONS) && !defined(FEATURE_PS2_KEYBOARD) && !defined(FEATURE_USB_KEYBOARD) && !defined(FEATURE_COMMAND_LINE_INTERFACE) && !defined(FEATURE_WINKEY_EMULATION) + #define number_of_memories byte(analog_buttons_number_of_buttons-1) +#else + #define number_of_memories byte(12) +#endif + +#ifdef FEATURE_CAPACITIVE_PADDLE_PINS + #define capacitance_threshold 2 +#endif //FEATURE_CAPACITIVE_PADDLE_PINS + +#ifdef FEATURE_LED_RING + #define led_ring_low_limit 10 + #define led_ring_high_limit 50 +#endif //FEATURE_LED_RING + +#ifdef FEATURE_QLF + #define qlf_dit_max 125 + #define qlf_dit_min 75 + #define qlf_dah_max 200 + #define qlf_dah_min 100 + #define qlf_on_by_default 0 +#endif //FEATURE_QLF + + +#ifdef FEATURE_WINKEY_EMULATION + #ifdef OPTION_WINKEY_UCXLOG_9600_BAUD + #define WINKEY_DEFAULT_BAUD 9600 + #else + #define WINKEY_DEFAULT_BAUD 1200 + #endif //OPTION_WINKEY_UCXLOG_9600_BAUD +// alter these below to map alternate sidetones for Winkey interface protocol emulation +#ifdef OPTION_WINKEY_2_SUPPORT + #define WINKEY_SIDETONE_1 3759 + #define WINKEY_SIDETONE_2 1879 + #define WINKEY_SIDETONE_3 1252 + #define WINKEY_SIDETONE_4 940 + #define WINKEY_SIDETONE_5 752 + #define WINKEY_SIDETONE_6 625 + #define WINKEY_SIDETONE_7 535 + #define WINKEY_SIDETONE_8 469 + #define WINKEY_SIDETONE_9 417 + #define WINKEY_SIDETONE_10 375 +#else //OPTION_WINKEY_2_SUPPORT + #define WINKEY_SIDETONE_1 4000 + #define WINKEY_SIDETONE_2 2000 + #define WINKEY_SIDETONE_3 1333 + #define WINKEY_SIDETONE_4 1000 + #define WINKEY_SIDETONE_5 800 + #define WINKEY_SIDETONE_6 666 + #define WINKEY_SIDETONE_7 571 + #define WINKEY_SIDETONE_8 500 + #define WINKEY_SIDETONE_9 444 + #define WINKEY_SIDETONE_10 400 +#endif //OPTION_WINKEY_2_SUPPORT + +#define WINKEY_1_REPORT_VERSION_NUMBER 10 +#define WINKEY_2_REPORT_VERSION_NUMBER 23 + +// alter these to map to alternate hang time wordspace units +#define WINKEY_HANG_TIME_1_0 1.0 +#define WINKEY_HANG_TIME_1_33 1.33 +#define WINKEY_HANG_TIME_1_66 1.66 +#define WINKEY_HANG_TIME_2_0 2.0 + +#define WINKEY_RETURN_THIS_FOR_ADMIN_GET_CAL 0x16 +#define WINKEY_RETURN_THIS_FOR_ADMIN_PADDLE_A2D 0xEE +#define WINKEY_RETURN_THIS_FOR_ADMIN_SPEED_A2D 0x00 + +#endif //FEATURE_WINKEY_EMULATION + + + +#define PRIMARY_SERIAL_PORT &Serial +#define PRIMARY_SERIAL_PORT_BAUD 115200 // This applies only when the port is in Command Line Interface mode. In Winkey mode it will default to 1200. + +#ifdef FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + #define SECONDARY_SERIAL_PORT &Serial1 + #define SECONDARY_SERIAL_PORT_BAUD 115200 +#endif + + +#define CW_DECODER_SCREEN_COLUMNS 120 // word wrap at this many columns +#define CW_DECODER_SPACE_PRINT_THRESH 4.5 // print space if no tone for this many element lengths +#define CW_DECODER_SPACE_DECODE_THRESH 2.0 // invoke character decode if no tone for this many element lengths +#define CW_DECODER_NOISE_FILTER 20 // ignore elements shorter than this (mS) + +#define STRAIGHT_KEY_ACTIVE_STATE LOW + +#ifdef FEATURE_DYNAMIC_DAH_TO_DIT_RATIO + #define DYNAMIC_DAH_TO_DIT_RATIO_LOWER_LIMIT_WPM 30 + #define DYNAMIC_DAH_TO_DIT_RATIO_LOWER_LIMIT_RATIO 300 // 300 = 3:1 ratio + #define DYNAMIC_DAH_TO_DIT_RATIO_UPPER_LIMIT_WPM 70 + #define DYNAMIC_DAH_TO_DIT_RATIO_UPPER_LIMIT_RATIO 240 // 240 = 2.4:1 ratio +#endif //FEATURE_DYNAMIC_DAH_TO_DIT_RATIO + +#if defined(FEATURE_COMPETITION_COMPRESSION_DETECTION) + #define COMPETITION_COMPRESSION_DETECTION_ARRAY_SIZE 16 + #define COMPETITION_COMPRESSION_DETECTION_TIME_INTERCHAR_LOWER_LIMIT 1 + #define COMPETITION_COMPRESSION_DETECTION_TIME_INTERCHAR_UPPER_LIMIT 6.0 + #define COMPETITION_COMPRESSION_DETECTION_AVERAGE_ALARM_THRESHOLD 3.0 +#endif //FEATURE_COMPETITION_COMPRESSION_DETECTION + +#if defined(FEATURE_SLEEP) + #define KEYER_AWAKE_PIN_AWAKE_STATE HIGH + #define KEYER_AWAKE_PIN_ASLEEP_STATE LOW +#endif + +#if defined(FEATURE_LCD_BACKLIGHT_AUTO_DIM) + #define keyer_power_led_awake_duty 255 // PWM duty cycle. 0 is 0%, 255 is 100% + #define keyer_power_led_asleep_duty 25 // 25 is quite dim. Use 0 for off +#endif + +#if defined(FEATURE_ETHERNET) + // #define FEATURE_ETHERNET_IP {192,168,1,178} // default IP address ("192.168.1.178") + // #define FEATURE_ETHERNET_MAC {0xDE,0xAD,0xBE,0xEF,0xFE,0xED} + #define FEATURE_ETHERNET_IP {192,168,1,179} // default IP address ("192.168.1.179") + #define FEATURE_ETHERNET_MAC {0xDE,0xAD,0xBE,0xEF,0xFE,0xEE} + #define FEATURE_ETHERNET_DNS {8,8,8,8} + + #define FEATURE_ETHERNET_GATEWAY {192,168,1,1} // default gateway + #define FEATURE_ETHERNET_SUBNET_MASK {255,255,255,0} // default subnet mask + #define FEATURE_ETHERNET_WEB_LISTENER_PORT 80 + #define FEATURE_UDP_SEND_BUFFER_SIZE 128 + #define FEATURE_UDP_RECEIVE_BUFFER_SIZE 128 +#endif //FEATURE_ETHERNET + +#define WEB_SERVER_CONTROL_TX_KEY_TIME_LIMIT_SECS 10 + +#define FEATURE_INTERNET_LINK_MAX_LINKS 2 +#define FEATURE_INTERNET_LINK_DEFAULT_RCV_UDP_PORT 8888 +#define FEATURE_INTERNET_LINK_BUFFER_TIME_MS 500 + +#if defined(FEATURE_4x4_KEYPAD)|| defined (FEATURE_3x4_KEYPAD) + #define KEYPAD_ROWS 4 //KeyPad Rows + #if defined(FEATURE_4x4_KEYPAD) + #define KEYPAD_COLS 4 //keypad Columns + #else + #define KEYPAD_COLS 3 + #endif + #define mem1 0 //Memory numbers for Keypad: Actual memory numbers start with "0" + #define mem2 1 + #define mem3 2 + #define mem4 3 + #define mem5 4 + #define mem6 5 + #define mem7 6 + #define mem8 7 + #define mem9 8 + #define mem10 9 + #define mem11 10 + #define mem12 11 +#endif //#if defined(FEATURE_4x4_KEYPAD)|| defined (FEATURE_3x4_KEYPAD) + +#define initial_sidetone_mode 1 // Sidetone mode, 0=OFF, 1=ON, 2=PADDLE_ONLY + +#define sd_card_spi_ss_line 4 + + +#if defined(OPTION_DFROBOT_LCD_COMMAND_BUTTONS) + + // For V1.1 board use these values + #define dfrobot_btnRIGHT_analog 50 + #define dfrobot_btnUP_analog 250 + #define dfrobot_btnDOWN_analog 450 + #define dfrobot_btnLEFT_analog 650 + #define dfrobot_btnSELECT_analog 850 + + // For V1.0 board use these values + // #define dfrobot_btnRIGHT_analog 50 + // #define dfrobot_btnUP_analog 195 + // #define dfrobot_btnDOWN_analog 380 + // #define dfrobot_btnLEFT_analog 555 + // #define dfrobot_btnSELECT_analog 790 + + // button to memory mappings (0 = command button, 1 = memory 1, 2 = memory 2, etc.) + #define dfrobot_btnRIGHT 2 + #define dfrobot_btnUP 1 + #define dfrobot_btnDOWN 3 + #define dfrobot_btnLEFT 4 + #define dfrobot_btnSELECT 0 + #define dfrobot_btnNONE 255 // do not change + +#endif + + +#define sequencer_pins_active_state HIGH +#define sequencer_pins_inactive_state LOW +#define ptt_line_active_state HIGH +#define ptt_line_inactive_state LOW +#define tx_key_line_active_state HIGH +#define tx_key_line_inactive_state LOW +#define ptt_input_pin_active_state LOW +#define ptt_input_pin_inactive_state HIGH +#define tx_inhibit_pin_active_state LOW +#define tx_inhibit_pin_inactive_state HIGH +#define tx_pause_pin_active_state LOW +#define tx_pause_pin_inactive_state HIGH +#define sidetone_line_active_state HIGH +#define sidetone_line_inactive_state LOW + +#if defined(ARDUINO_MAPLE_MINI) + #define button_value_factor 4095 +#else + #define button_value_factor 1023 +#endif + +#define farnsworth_timing_calibration 1.15 + +#define sidetone_volume_low_limit 10 +#define sidetone_volume_high_limit 500 + +#define custom_startup_field "your custom text here" // an example could be callsign and name, eg. "AB1XYZ Bob", (or "Worlds best operator" which requires a 20 column display), string length shouldo be no more than the number of columns on the display + +#define command_mode_acknowledgement_character 'E' + +#if defined(FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT) + #define command_a_iambic_a "A" + #define command_b_iambic_b "B" + #define command_c_single_paddle "SINGLE" + #define command_d_ultimatic "ULT" + #define command_h_weight_dit_dah_ratio_default "R" + #define command_i_tx_on "TX ON" + #define command_i_tx_off "TX OFF" + #define command_k_dit_dah_buffers_on "ON" + #define command_k_dit_dah_buffers_off "OFF" + #define command_n_paddle_reverse "REV" + #define command_n_paddle_normal "NORM" + #define command_o_sidetone_off "ST OFF" + #define command_o_sidetone_paddle_only "ST PD ONLY" + #define command_o_sidetone_on "ST ON" + #define command_t_tune_mode "TUNE" + #define command_v_potentiometer_on "POT ON" + #define command_v_potentiometer_off "POT OFF" + + #define command_error "ERR" + +#endif //FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT diff --git a/k3ng_keyer/keyer_settings_mortty_so2r.h b/k3ng_keyer/keyer_settings_mortty_so2r.h new file mode 100755 index 0000000..a63ac30 --- /dev/null +++ b/k3ng_keyer/keyer_settings_mortty_so2r.h @@ -0,0 +1,318 @@ +// Initial and hardcoded settings +#define initial_speed_wpm 26 // "factory default" keyer speed setting +#define initial_command_mode_speed_wpm 20 // "factory default" command mode speed setting +#define initial_sidetone_freq 600 // "factory default" sidetone frequency setting +#define sidetone_hz_limit_low 299 +#define sidetone_hz_limit_high 2001 +#define hz_high_beep 1500 // frequency in hertz of high beep +#define hz_low_beep 400 // frequency in hertz of low beep +#define initial_dah_to_dit_ratio 300 // 300 = 3 / normal 3:1 ratio +#define initial_ptt_lead_time_tx1 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx1 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx2 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx2 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx3 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx3 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx4 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx4 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx5 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx5 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx6 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx6 10 // PTT tail time in mS +#define initial_qrss_dit_length 1 // QRSS dit length in seconds +#define initial_pot_wpm_low_value 13 // Potentiometer WPM fully CCW +#define initial_pot_wpm_high_value 35 // Potentiometer WPM fully CW +#define wpm_limit_low 5 +#define wpm_limit_high 60 +#define potentiometer_change_threshold 0.9 // don't change the keyer speed until pot wpm has changed more than this +#define send_buffer_size 150 +#define default_length_letterspace 3 +#define default_length_wordspace 7 +#define default_keying_compensation 0 // number of milliseconds to extend all dits and dahs - for QSK on boatanchors +#define default_first_extension_time 0 // number of milliseconds to extend first sent dit or dah +#define default_pot_full_scale_reading 1023 +#define default_weighting 50 // 50 = weighting factor of 1 (normal) +#define default_ptt_hang_time_wordspace_units 0.0 +#define winkey_c0_wait_time 1 // the number of milliseconds to wait to send 0xc0 byte after send buffer has been sent +#define winkey_command_timeout_ms 5000 +#define winkey_discard_bytes_startup 3 // this is used if OPTION_WINKEY_DISCARD_BYTES_AT_STARTUP is enabled above +#define winkey_xoff_threshold 20 // the number of chars in the buffer when we begin sending XOFFs +#define winkey_xon_threshold 10 // the number of chars in the buffer below which we deactivate XOFF +#define default_memory_repeat_time 3000 // time in milliseconds +#define LCD_COLUMNS 16 +#define LCD_ROWS 2 +#define lcd_i2c_address_mathertel_PCF8574 0x27 // I2C address of display for FEATURE_LCD_MATHERTEL_PCF8574 +#define lcd_i2c_address_fdebrander_lcd 0x27 // I2C address of display for FEATURE_LCD_I2C_FDEBRABANDER +#define lcd_i2c_address_ydv1_lcd 0x27 // I2C address of display for FEATURE_LCD_YDv1 +//#define lcd_i2c_address_ydv1_lcd 0x38 // I2C address of display for FEATURE_LCD_YDv1 +#define lcd_i2c_address_sainsmart_lcd 0x27 // I2C address of display for FEATURE_LCD_SAINSMART_I2C +#define hell_pixel_microseconds 4025 +#define program_memory_limit_consec_spaces 1 +#define serial_leading_zeros 1 // set to 1 to activate leading zeros in serial numbers (i.e. #1 = 001) +#define serial_cut_numbers 0 // set to 1 to activate cut numbers in serial numbers (i.e. #10 = 1T, #19 = 1N) +#define go_to_sleep_inactivity_time 10 // minutes - FEATURE_SLEEP +#define dim_backlight_inactive_time 5 // minutes - FEATURE_LCD_BACKLIGHT_AUTO_DIM +#define default_cmos_super_keyer_iambic_b_timing_percent 33 // use with FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING; should be between 0 to 99 % (0% = true iambic b;100% = iambic a behavior) +#define default_cw_echo_timing_factor 1.75 // "factory default" setting +#define default_autospace_timing_factor 2.0 // "factory default" setting +#define winkey_paddle_echo_buffer_decode_timing_factor 0.25 +#define potentiometer_always_on 0 +#define ptt_interlock_check_every_ms 100 +#define ptt_interlock_active_state HIGH +#define unknown_cw_character '*' +#define cli_paddle_echo_on_at_boot 1 +#define cli_straight_key_echo_on_at_boot 1 +#define tx_key_dit_and_dah_pins_active_state HIGH +#define tx_key_dit_and_dah_pins_inactive_state LOW +#define potentiometer_check_interval_ms 0 //=====150 +#define potentiometer_reading_threshold 1 +#define default_paddle_interruption_quiet_time_element_lengths 0 +#define default_wordsworth_wordspace 6 +#define default_wordsworth_repetition 1 +#define serial_program_memory_buffer_size 500 +#define eeprom_write_time_ms 30000 + +#ifdef FEATURE_BUTTONS + #define analog_buttons_number_of_buttons 4 + #define analog_buttons_r1 10 + #define analog_buttons_r2 1 +#endif + + +#if defined(FEATURE_BUTTONS) && !defined(FEATURE_PS2_KEYBOARD) && !defined(FEATURE_USB_KEYBOARD) && !defined(FEATURE_COMMAND_LINE_INTERFACE) && !defined(FEATURE_WINKEY_EMULATION) + #define number_of_memories byte(analog_buttons_number_of_buttons-1) +#else + #define number_of_memories byte(12) +#endif + +#ifdef FEATURE_CAPACITIVE_PADDLE_PINS + #define capacitance_threshold 2 +#endif //FEATURE_CAPACITIVE_PADDLE_PINS + +#ifdef FEATURE_LED_RING + #define led_ring_low_limit 10 + #define led_ring_high_limit 50 +#endif //FEATURE_LED_RING + +#ifdef FEATURE_QLF + #define qlf_dit_max 125 + #define qlf_dit_min 75 + #define qlf_dah_max 200 + #define qlf_dah_min 100 + #define qlf_on_by_default 0 +#endif //FEATURE_QLF + + +#ifdef FEATURE_WINKEY_EMULATION + #ifdef OPTION_WINKEY_UCXLOG_9600_BAUD + #define WINKEY_DEFAULT_BAUD 9600 + #else + #define WINKEY_DEFAULT_BAUD 1200 + #endif //OPTION_WINKEY_UCXLOG_9600_BAUD +// alter these below to map alternate sidetones for Winkey interface protocol emulation +#ifdef OPTION_WINKEY_2_SUPPORT + #define WINKEY_SIDETONE_1 3759 + #define WINKEY_SIDETONE_2 1879 + #define WINKEY_SIDETONE_3 1252 + #define WINKEY_SIDETONE_4 940 + #define WINKEY_SIDETONE_5 752 + #define WINKEY_SIDETONE_6 625 + #define WINKEY_SIDETONE_7 535 + #define WINKEY_SIDETONE_8 469 + #define WINKEY_SIDETONE_9 417 + #define WINKEY_SIDETONE_10 375 +#else //OPTION_WINKEY_2_SUPPORT + #define WINKEY_SIDETONE_1 4000 + #define WINKEY_SIDETONE_2 2000 + #define WINKEY_SIDETONE_3 1333 + #define WINKEY_SIDETONE_4 1000 + #define WINKEY_SIDETONE_5 800 + #define WINKEY_SIDETONE_6 666 + #define WINKEY_SIDETONE_7 571 + #define WINKEY_SIDETONE_8 500 + #define WINKEY_SIDETONE_9 444 + #define WINKEY_SIDETONE_10 400 +#endif //OPTION_WINKEY_2_SUPPORT + +#define WINKEY_1_REPORT_VERSION_NUMBER 10 +#define WINKEY_2_REPORT_VERSION_NUMBER 23 + +// alter these to map to alternate hang time wordspace units +#define WINKEY_HANG_TIME_1_0 1.0 +#define WINKEY_HANG_TIME_1_33 1.33 +#define WINKEY_HANG_TIME_1_66 1.66 +#define WINKEY_HANG_TIME_2_0 2.0 + +#define WINKEY_RETURN_THIS_FOR_ADMIN_GET_CAL 0x16 +#define WINKEY_RETURN_THIS_FOR_ADMIN_PADDLE_A2D 0xEE +#define WINKEY_RETURN_THIS_FOR_ADMIN_SPEED_A2D 0x00 + +#endif //FEATURE_WINKEY_EMULATION + + + +#define PRIMARY_SERIAL_PORT &Serial +#define PRIMARY_SERIAL_PORT_BAUD 115200 // This applies only when the port is in Command Line Interface mode. In Winkey mode it will default to 1200. + +#ifdef FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + #define SECONDARY_SERIAL_PORT &Serial1 + #define SECONDARY_SERIAL_PORT_BAUD 115200 +#endif + + +#define CW_DECODER_SCREEN_COLUMNS 120 // word wrap at this many columns +#define CW_DECODER_SPACE_PRINT_THRESH 4.5 // print space if no tone for this many element lengths +#define CW_DECODER_SPACE_DECODE_THRESH 2.0 // invoke character decode if no tone for this many element lengths +#define CW_DECODER_NOISE_FILTER 20 // ignore elements shorter than this (mS) + +#define STRAIGHT_KEY_ACTIVE_STATE LOW + +#ifdef FEATURE_DYNAMIC_DAH_TO_DIT_RATIO + #define DYNAMIC_DAH_TO_DIT_RATIO_LOWER_LIMIT_WPM 30 + #define DYNAMIC_DAH_TO_DIT_RATIO_LOWER_LIMIT_RATIO 300 // 300 = 3:1 ratio + #define DYNAMIC_DAH_TO_DIT_RATIO_UPPER_LIMIT_WPM 70 + #define DYNAMIC_DAH_TO_DIT_RATIO_UPPER_LIMIT_RATIO 240 // 240 = 2.4:1 ratio +#endif //FEATURE_DYNAMIC_DAH_TO_DIT_RATIO + +#if defined(FEATURE_COMPETITION_COMPRESSION_DETECTION) + #define COMPETITION_COMPRESSION_DETECTION_ARRAY_SIZE 16 + #define COMPETITION_COMPRESSION_DETECTION_TIME_INTERCHAR_LOWER_LIMIT 1 + #define COMPETITION_COMPRESSION_DETECTION_TIME_INTERCHAR_UPPER_LIMIT 6.0 + #define COMPETITION_COMPRESSION_DETECTION_AVERAGE_ALARM_THRESHOLD 3.0 +#endif //FEATURE_COMPETITION_COMPRESSION_DETECTION + +#if defined(FEATURE_SLEEP) + #define KEYER_AWAKE_PIN_AWAKE_STATE HIGH + #define KEYER_AWAKE_PIN_ASLEEP_STATE LOW +#endif + +#if defined(FEATURE_LCD_BACKLIGHT_AUTO_DIM) + #define keyer_power_led_awake_duty 255 // PWM duty cycle. 0 is 0%, 255 is 100% + #define keyer_power_led_asleep_duty 25 // 25 is quite dim. Use 0 for off +#endif + +#if defined(FEATURE_ETHERNET) + // #define FEATURE_ETHERNET_IP {192,168,1,178} // default IP address ("192.168.1.178") + // #define FEATURE_ETHERNET_MAC {0xDE,0xAD,0xBE,0xEF,0xFE,0xED} + #define FEATURE_ETHERNET_IP {192,168,1,179} // default IP address ("192.168.1.179") + #define FEATURE_ETHERNET_MAC {0xDE,0xAD,0xBE,0xEF,0xFE,0xEE} + #define FEATURE_ETHERNET_DNS {8,8,8,8} + + #define FEATURE_ETHERNET_GATEWAY {192,168,1,1} // default gateway + #define FEATURE_ETHERNET_SUBNET_MASK {255,255,255,0} // default subnet mask + #define FEATURE_ETHERNET_WEB_LISTENER_PORT 80 + #define FEATURE_UDP_SEND_BUFFER_SIZE 128 + #define FEATURE_UDP_RECEIVE_BUFFER_SIZE 128 +#endif //FEATURE_ETHERNET + +#define WEB_SERVER_CONTROL_TX_KEY_TIME_LIMIT_SECS 10 + +#define FEATURE_INTERNET_LINK_MAX_LINKS 2 +#define FEATURE_INTERNET_LINK_DEFAULT_RCV_UDP_PORT 8888 +#define FEATURE_INTERNET_LINK_BUFFER_TIME_MS 500 + +#if defined(FEATURE_4x4_KEYPAD)|| defined (FEATURE_3x4_KEYPAD) + #define KEYPAD_ROWS 4 //KeyPad Rows + #if defined(FEATURE_4x4_KEYPAD) + #define KEYPAD_COLS 4 //keypad Columns + #else + #define KEYPAD_COLS 3 + #endif + #define mem1 0 //Memory numbers for Keypad: Actual memory numbers start with "0" + #define mem2 1 + #define mem3 2 + #define mem4 3 + #define mem5 4 + #define mem6 5 + #define mem7 6 + #define mem8 7 + #define mem9 8 + #define mem10 9 + #define mem11 10 + #define mem12 11 +#endif //#if defined(FEATURE_4x4_KEYPAD)|| defined (FEATURE_3x4_KEYPAD) + +#define initial_sidetone_mode 1 // Sidetone mode, 0=OFF, 1=ON, 2=PADDLE_ONLY + +#define sd_card_spi_ss_line 4 + + +#if defined(OPTION_DFROBOT_LCD_COMMAND_BUTTONS) + + // For V1.1 board use these values + #define dfrobot_btnRIGHT_analog 50 + #define dfrobot_btnUP_analog 250 + #define dfrobot_btnDOWN_analog 450 + #define dfrobot_btnLEFT_analog 650 + #define dfrobot_btnSELECT_analog 850 + + // For V1.0 board use these values + // #define dfrobot_btnRIGHT_analog 50 + // #define dfrobot_btnUP_analog 195 + // #define dfrobot_btnDOWN_analog 380 + // #define dfrobot_btnLEFT_analog 555 + // #define dfrobot_btnSELECT_analog 790 + + // button to memory mappings (0 = command button, 1 = memory 1, 2 = memory 2, etc.) + #define dfrobot_btnRIGHT 2 + #define dfrobot_btnUP 1 + #define dfrobot_btnDOWN 3 + #define dfrobot_btnLEFT 4 + #define dfrobot_btnSELECT 0 + #define dfrobot_btnNONE 255 // do not change + +#endif + + +#define sequencer_pins_active_state HIGH +#define sequencer_pins_inactive_state LOW +#define ptt_line_active_state HIGH +#define ptt_line_inactive_state LOW +#define tx_key_line_active_state HIGH +#define tx_key_line_inactive_state LOW +#define ptt_input_pin_active_state LOW +#define ptt_input_pin_inactive_state HIGH +#define tx_inhibit_pin_active_state LOW +#define tx_inhibit_pin_inactive_state HIGH +#define tx_pause_pin_active_state LOW +#define tx_pause_pin_inactive_state HIGH +#define sidetone_line_active_state HIGH +#define sidetone_line_inactive_state LOW + +#if defined(ARDUINO_MAPLE_MINI) + #define button_value_factor 4095 +#else + #define button_value_factor 1023 +#endif + +#define farnsworth_timing_calibration 1.15 + +#define sidetone_volume_low_limit 10 +#define sidetone_volume_high_limit 500 + +#define custom_startup_field "your custom text here" // an example could be callsign and name, eg. "AB1XYZ Bob", (or "Worlds best operator" which requires a 20 column display), string length shouldo be no more than the number of columns on the display + +#define command_mode_acknowledgement_character 'E' + +#if defined(FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT) + #define command_a_iambic_a "A" + #define command_b_iambic_b "B" + #define command_c_single_paddle "SINGLE" + #define command_d_ultimatic "ULT" + #define command_h_weight_dit_dah_ratio_default "R" + #define command_i_tx_on "TX ON" + #define command_i_tx_off "TX OFF" + #define command_k_dit_dah_buffers_on "ON" + #define command_k_dit_dah_buffers_off "OFF" + #define command_n_paddle_reverse "REV" + #define command_n_paddle_normal "NORM" + #define command_o_sidetone_off "ST OFF" + #define command_o_sidetone_paddle_only "ST PD ONLY" + #define command_o_sidetone_on "ST ON" + #define command_t_tune_mode "TUNE" + #define command_v_potentiometer_on "POT ON" + #define command_v_potentiometer_off "POT OFF" + + #define command_error "ERR" + +#endif //FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT diff --git a/k3ng_keyer/keyer_settings_mortty_so2r_with_potentiometer.h b/k3ng_keyer/keyer_settings_mortty_so2r_with_potentiometer.h new file mode 100755 index 0000000..a63ac30 --- /dev/null +++ b/k3ng_keyer/keyer_settings_mortty_so2r_with_potentiometer.h @@ -0,0 +1,318 @@ +// Initial and hardcoded settings +#define initial_speed_wpm 26 // "factory default" keyer speed setting +#define initial_command_mode_speed_wpm 20 // "factory default" command mode speed setting +#define initial_sidetone_freq 600 // "factory default" sidetone frequency setting +#define sidetone_hz_limit_low 299 +#define sidetone_hz_limit_high 2001 +#define hz_high_beep 1500 // frequency in hertz of high beep +#define hz_low_beep 400 // frequency in hertz of low beep +#define initial_dah_to_dit_ratio 300 // 300 = 3 / normal 3:1 ratio +#define initial_ptt_lead_time_tx1 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx1 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx2 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx2 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx3 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx3 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx4 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx4 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx5 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx5 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx6 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx6 10 // PTT tail time in mS +#define initial_qrss_dit_length 1 // QRSS dit length in seconds +#define initial_pot_wpm_low_value 13 // Potentiometer WPM fully CCW +#define initial_pot_wpm_high_value 35 // Potentiometer WPM fully CW +#define wpm_limit_low 5 +#define wpm_limit_high 60 +#define potentiometer_change_threshold 0.9 // don't change the keyer speed until pot wpm has changed more than this +#define send_buffer_size 150 +#define default_length_letterspace 3 +#define default_length_wordspace 7 +#define default_keying_compensation 0 // number of milliseconds to extend all dits and dahs - for QSK on boatanchors +#define default_first_extension_time 0 // number of milliseconds to extend first sent dit or dah +#define default_pot_full_scale_reading 1023 +#define default_weighting 50 // 50 = weighting factor of 1 (normal) +#define default_ptt_hang_time_wordspace_units 0.0 +#define winkey_c0_wait_time 1 // the number of milliseconds to wait to send 0xc0 byte after send buffer has been sent +#define winkey_command_timeout_ms 5000 +#define winkey_discard_bytes_startup 3 // this is used if OPTION_WINKEY_DISCARD_BYTES_AT_STARTUP is enabled above +#define winkey_xoff_threshold 20 // the number of chars in the buffer when we begin sending XOFFs +#define winkey_xon_threshold 10 // the number of chars in the buffer below which we deactivate XOFF +#define default_memory_repeat_time 3000 // time in milliseconds +#define LCD_COLUMNS 16 +#define LCD_ROWS 2 +#define lcd_i2c_address_mathertel_PCF8574 0x27 // I2C address of display for FEATURE_LCD_MATHERTEL_PCF8574 +#define lcd_i2c_address_fdebrander_lcd 0x27 // I2C address of display for FEATURE_LCD_I2C_FDEBRABANDER +#define lcd_i2c_address_ydv1_lcd 0x27 // I2C address of display for FEATURE_LCD_YDv1 +//#define lcd_i2c_address_ydv1_lcd 0x38 // I2C address of display for FEATURE_LCD_YDv1 +#define lcd_i2c_address_sainsmart_lcd 0x27 // I2C address of display for FEATURE_LCD_SAINSMART_I2C +#define hell_pixel_microseconds 4025 +#define program_memory_limit_consec_spaces 1 +#define serial_leading_zeros 1 // set to 1 to activate leading zeros in serial numbers (i.e. #1 = 001) +#define serial_cut_numbers 0 // set to 1 to activate cut numbers in serial numbers (i.e. #10 = 1T, #19 = 1N) +#define go_to_sleep_inactivity_time 10 // minutes - FEATURE_SLEEP +#define dim_backlight_inactive_time 5 // minutes - FEATURE_LCD_BACKLIGHT_AUTO_DIM +#define default_cmos_super_keyer_iambic_b_timing_percent 33 // use with FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING; should be between 0 to 99 % (0% = true iambic b;100% = iambic a behavior) +#define default_cw_echo_timing_factor 1.75 // "factory default" setting +#define default_autospace_timing_factor 2.0 // "factory default" setting +#define winkey_paddle_echo_buffer_decode_timing_factor 0.25 +#define potentiometer_always_on 0 +#define ptt_interlock_check_every_ms 100 +#define ptt_interlock_active_state HIGH +#define unknown_cw_character '*' +#define cli_paddle_echo_on_at_boot 1 +#define cli_straight_key_echo_on_at_boot 1 +#define tx_key_dit_and_dah_pins_active_state HIGH +#define tx_key_dit_and_dah_pins_inactive_state LOW +#define potentiometer_check_interval_ms 0 //=====150 +#define potentiometer_reading_threshold 1 +#define default_paddle_interruption_quiet_time_element_lengths 0 +#define default_wordsworth_wordspace 6 +#define default_wordsworth_repetition 1 +#define serial_program_memory_buffer_size 500 +#define eeprom_write_time_ms 30000 + +#ifdef FEATURE_BUTTONS + #define analog_buttons_number_of_buttons 4 + #define analog_buttons_r1 10 + #define analog_buttons_r2 1 +#endif + + +#if defined(FEATURE_BUTTONS) && !defined(FEATURE_PS2_KEYBOARD) && !defined(FEATURE_USB_KEYBOARD) && !defined(FEATURE_COMMAND_LINE_INTERFACE) && !defined(FEATURE_WINKEY_EMULATION) + #define number_of_memories byte(analog_buttons_number_of_buttons-1) +#else + #define number_of_memories byte(12) +#endif + +#ifdef FEATURE_CAPACITIVE_PADDLE_PINS + #define capacitance_threshold 2 +#endif //FEATURE_CAPACITIVE_PADDLE_PINS + +#ifdef FEATURE_LED_RING + #define led_ring_low_limit 10 + #define led_ring_high_limit 50 +#endif //FEATURE_LED_RING + +#ifdef FEATURE_QLF + #define qlf_dit_max 125 + #define qlf_dit_min 75 + #define qlf_dah_max 200 + #define qlf_dah_min 100 + #define qlf_on_by_default 0 +#endif //FEATURE_QLF + + +#ifdef FEATURE_WINKEY_EMULATION + #ifdef OPTION_WINKEY_UCXLOG_9600_BAUD + #define WINKEY_DEFAULT_BAUD 9600 + #else + #define WINKEY_DEFAULT_BAUD 1200 + #endif //OPTION_WINKEY_UCXLOG_9600_BAUD +// alter these below to map alternate sidetones for Winkey interface protocol emulation +#ifdef OPTION_WINKEY_2_SUPPORT + #define WINKEY_SIDETONE_1 3759 + #define WINKEY_SIDETONE_2 1879 + #define WINKEY_SIDETONE_3 1252 + #define WINKEY_SIDETONE_4 940 + #define WINKEY_SIDETONE_5 752 + #define WINKEY_SIDETONE_6 625 + #define WINKEY_SIDETONE_7 535 + #define WINKEY_SIDETONE_8 469 + #define WINKEY_SIDETONE_9 417 + #define WINKEY_SIDETONE_10 375 +#else //OPTION_WINKEY_2_SUPPORT + #define WINKEY_SIDETONE_1 4000 + #define WINKEY_SIDETONE_2 2000 + #define WINKEY_SIDETONE_3 1333 + #define WINKEY_SIDETONE_4 1000 + #define WINKEY_SIDETONE_5 800 + #define WINKEY_SIDETONE_6 666 + #define WINKEY_SIDETONE_7 571 + #define WINKEY_SIDETONE_8 500 + #define WINKEY_SIDETONE_9 444 + #define WINKEY_SIDETONE_10 400 +#endif //OPTION_WINKEY_2_SUPPORT + +#define WINKEY_1_REPORT_VERSION_NUMBER 10 +#define WINKEY_2_REPORT_VERSION_NUMBER 23 + +// alter these to map to alternate hang time wordspace units +#define WINKEY_HANG_TIME_1_0 1.0 +#define WINKEY_HANG_TIME_1_33 1.33 +#define WINKEY_HANG_TIME_1_66 1.66 +#define WINKEY_HANG_TIME_2_0 2.0 + +#define WINKEY_RETURN_THIS_FOR_ADMIN_GET_CAL 0x16 +#define WINKEY_RETURN_THIS_FOR_ADMIN_PADDLE_A2D 0xEE +#define WINKEY_RETURN_THIS_FOR_ADMIN_SPEED_A2D 0x00 + +#endif //FEATURE_WINKEY_EMULATION + + + +#define PRIMARY_SERIAL_PORT &Serial +#define PRIMARY_SERIAL_PORT_BAUD 115200 // This applies only when the port is in Command Line Interface mode. In Winkey mode it will default to 1200. + +#ifdef FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + #define SECONDARY_SERIAL_PORT &Serial1 + #define SECONDARY_SERIAL_PORT_BAUD 115200 +#endif + + +#define CW_DECODER_SCREEN_COLUMNS 120 // word wrap at this many columns +#define CW_DECODER_SPACE_PRINT_THRESH 4.5 // print space if no tone for this many element lengths +#define CW_DECODER_SPACE_DECODE_THRESH 2.0 // invoke character decode if no tone for this many element lengths +#define CW_DECODER_NOISE_FILTER 20 // ignore elements shorter than this (mS) + +#define STRAIGHT_KEY_ACTIVE_STATE LOW + +#ifdef FEATURE_DYNAMIC_DAH_TO_DIT_RATIO + #define DYNAMIC_DAH_TO_DIT_RATIO_LOWER_LIMIT_WPM 30 + #define DYNAMIC_DAH_TO_DIT_RATIO_LOWER_LIMIT_RATIO 300 // 300 = 3:1 ratio + #define DYNAMIC_DAH_TO_DIT_RATIO_UPPER_LIMIT_WPM 70 + #define DYNAMIC_DAH_TO_DIT_RATIO_UPPER_LIMIT_RATIO 240 // 240 = 2.4:1 ratio +#endif //FEATURE_DYNAMIC_DAH_TO_DIT_RATIO + +#if defined(FEATURE_COMPETITION_COMPRESSION_DETECTION) + #define COMPETITION_COMPRESSION_DETECTION_ARRAY_SIZE 16 + #define COMPETITION_COMPRESSION_DETECTION_TIME_INTERCHAR_LOWER_LIMIT 1 + #define COMPETITION_COMPRESSION_DETECTION_TIME_INTERCHAR_UPPER_LIMIT 6.0 + #define COMPETITION_COMPRESSION_DETECTION_AVERAGE_ALARM_THRESHOLD 3.0 +#endif //FEATURE_COMPETITION_COMPRESSION_DETECTION + +#if defined(FEATURE_SLEEP) + #define KEYER_AWAKE_PIN_AWAKE_STATE HIGH + #define KEYER_AWAKE_PIN_ASLEEP_STATE LOW +#endif + +#if defined(FEATURE_LCD_BACKLIGHT_AUTO_DIM) + #define keyer_power_led_awake_duty 255 // PWM duty cycle. 0 is 0%, 255 is 100% + #define keyer_power_led_asleep_duty 25 // 25 is quite dim. Use 0 for off +#endif + +#if defined(FEATURE_ETHERNET) + // #define FEATURE_ETHERNET_IP {192,168,1,178} // default IP address ("192.168.1.178") + // #define FEATURE_ETHERNET_MAC {0xDE,0xAD,0xBE,0xEF,0xFE,0xED} + #define FEATURE_ETHERNET_IP {192,168,1,179} // default IP address ("192.168.1.179") + #define FEATURE_ETHERNET_MAC {0xDE,0xAD,0xBE,0xEF,0xFE,0xEE} + #define FEATURE_ETHERNET_DNS {8,8,8,8} + + #define FEATURE_ETHERNET_GATEWAY {192,168,1,1} // default gateway + #define FEATURE_ETHERNET_SUBNET_MASK {255,255,255,0} // default subnet mask + #define FEATURE_ETHERNET_WEB_LISTENER_PORT 80 + #define FEATURE_UDP_SEND_BUFFER_SIZE 128 + #define FEATURE_UDP_RECEIVE_BUFFER_SIZE 128 +#endif //FEATURE_ETHERNET + +#define WEB_SERVER_CONTROL_TX_KEY_TIME_LIMIT_SECS 10 + +#define FEATURE_INTERNET_LINK_MAX_LINKS 2 +#define FEATURE_INTERNET_LINK_DEFAULT_RCV_UDP_PORT 8888 +#define FEATURE_INTERNET_LINK_BUFFER_TIME_MS 500 + +#if defined(FEATURE_4x4_KEYPAD)|| defined (FEATURE_3x4_KEYPAD) + #define KEYPAD_ROWS 4 //KeyPad Rows + #if defined(FEATURE_4x4_KEYPAD) + #define KEYPAD_COLS 4 //keypad Columns + #else + #define KEYPAD_COLS 3 + #endif + #define mem1 0 //Memory numbers for Keypad: Actual memory numbers start with "0" + #define mem2 1 + #define mem3 2 + #define mem4 3 + #define mem5 4 + #define mem6 5 + #define mem7 6 + #define mem8 7 + #define mem9 8 + #define mem10 9 + #define mem11 10 + #define mem12 11 +#endif //#if defined(FEATURE_4x4_KEYPAD)|| defined (FEATURE_3x4_KEYPAD) + +#define initial_sidetone_mode 1 // Sidetone mode, 0=OFF, 1=ON, 2=PADDLE_ONLY + +#define sd_card_spi_ss_line 4 + + +#if defined(OPTION_DFROBOT_LCD_COMMAND_BUTTONS) + + // For V1.1 board use these values + #define dfrobot_btnRIGHT_analog 50 + #define dfrobot_btnUP_analog 250 + #define dfrobot_btnDOWN_analog 450 + #define dfrobot_btnLEFT_analog 650 + #define dfrobot_btnSELECT_analog 850 + + // For V1.0 board use these values + // #define dfrobot_btnRIGHT_analog 50 + // #define dfrobot_btnUP_analog 195 + // #define dfrobot_btnDOWN_analog 380 + // #define dfrobot_btnLEFT_analog 555 + // #define dfrobot_btnSELECT_analog 790 + + // button to memory mappings (0 = command button, 1 = memory 1, 2 = memory 2, etc.) + #define dfrobot_btnRIGHT 2 + #define dfrobot_btnUP 1 + #define dfrobot_btnDOWN 3 + #define dfrobot_btnLEFT 4 + #define dfrobot_btnSELECT 0 + #define dfrobot_btnNONE 255 // do not change + +#endif + + +#define sequencer_pins_active_state HIGH +#define sequencer_pins_inactive_state LOW +#define ptt_line_active_state HIGH +#define ptt_line_inactive_state LOW +#define tx_key_line_active_state HIGH +#define tx_key_line_inactive_state LOW +#define ptt_input_pin_active_state LOW +#define ptt_input_pin_inactive_state HIGH +#define tx_inhibit_pin_active_state LOW +#define tx_inhibit_pin_inactive_state HIGH +#define tx_pause_pin_active_state LOW +#define tx_pause_pin_inactive_state HIGH +#define sidetone_line_active_state HIGH +#define sidetone_line_inactive_state LOW + +#if defined(ARDUINO_MAPLE_MINI) + #define button_value_factor 4095 +#else + #define button_value_factor 1023 +#endif + +#define farnsworth_timing_calibration 1.15 + +#define sidetone_volume_low_limit 10 +#define sidetone_volume_high_limit 500 + +#define custom_startup_field "your custom text here" // an example could be callsign and name, eg. "AB1XYZ Bob", (or "Worlds best operator" which requires a 20 column display), string length shouldo be no more than the number of columns on the display + +#define command_mode_acknowledgement_character 'E' + +#if defined(FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT) + #define command_a_iambic_a "A" + #define command_b_iambic_b "B" + #define command_c_single_paddle "SINGLE" + #define command_d_ultimatic "ULT" + #define command_h_weight_dit_dah_ratio_default "R" + #define command_i_tx_on "TX ON" + #define command_i_tx_off "TX OFF" + #define command_k_dit_dah_buffers_on "ON" + #define command_k_dit_dah_buffers_off "OFF" + #define command_n_paddle_reverse "REV" + #define command_n_paddle_normal "NORM" + #define command_o_sidetone_off "ST OFF" + #define command_o_sidetone_paddle_only "ST PD ONLY" + #define command_o_sidetone_on "ST ON" + #define command_t_tune_mode "TUNE" + #define command_v_potentiometer_on "POT ON" + #define command_v_potentiometer_off "POT OFF" + + #define command_error "ERR" + +#endif //FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT diff --git a/k3ng_keyer/keyer_settings_nanokeyer_rev_b.h b/k3ng_keyer/keyer_settings_nanokeyer_rev_b.h new file mode 100755 index 0000000..5120aa8 --- /dev/null +++ b/k3ng_keyer/keyer_settings_nanokeyer_rev_b.h @@ -0,0 +1,309 @@ +// Initial and hardcoded settings +#define initial_speed_wpm 26 // "factory default" keyer speed setting +#define initial_command_mode_speed_wpm 20 // "factory default" command mode speed setting +#define initial_sidetone_freq 600 // "factory default" sidetone frequency setting +#define sidetone_hz_limit_low 299 +#define sidetone_hz_limit_high 2001 +#define hz_high_beep 1500 // frequency in hertz of high beep +#define hz_low_beep 400 // frequency in hertz of low beep +#define initial_dah_to_dit_ratio 300 // 300 = 3 / normal 3:1 ratio +#define initial_ptt_lead_time_tx1 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx1 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx2 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx2 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx3 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx3 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx4 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx4 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx5 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx5 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx6 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx6 10 // PTT tail time in mS +#define initial_qrss_dit_length 1 // QRSS dit length in seconds +#define initial_pot_wpm_low_value 13 // Potentiometer WPM fully CCW +#define initial_pot_wpm_high_value 35 // Potentiometer WPM fully CW +#define wpm_limit_low 5 +#define wpm_limit_high 60 +#define potentiometer_change_threshold 0.9 // don't change the keyer speed until pot wpm has changed more than this +#define send_buffer_size 150 +#define default_length_letterspace 3 +#define default_length_wordspace 7 +#define default_keying_compensation 0 // number of milliseconds to extend all dits and dahs - for QSK on boatanchors +#define default_first_extension_time 0 // number of milliseconds to extend first sent dit or dah +#define default_pot_full_scale_reading 1023 +#define default_weighting 50 // 50 = weighting factor of 1 (normal) +#define default_ptt_hang_time_wordspace_units 0.0 +#define winkey_c0_wait_time 1 // the number of milliseconds to wait to send 0xc0 byte after send buffer has been sent +#define winkey_command_timeout_ms 5000 +#define winkey_discard_bytes_startup 3 // this is used if OPTION_WINKEY_DISCARD_BYTES_AT_STARTUP is enabled above +#define winkey_xoff_threshold 20 // the number of chars in the buffer when we begin sending XOFFs +#define winkey_xon_threshold 10 // the number of chars in the buffer below which we deactivate XOFF +#define default_memory_repeat_time 3000 // time in milliseconds +#define LCD_COLUMNS 16 +#define LCD_ROWS 2 +#define lcd_i2c_address_mathertel_PCF8574 0x27 // I2C address of display for FEATURE_LCD_MATHERTEL_PCF8574 +#define lcd_i2c_address_fdebrander_lcd 0x27 // I2C address of display for FEATURE_LCD_I2C_FDEBRABANDER +#define lcd_i2c_address_ydv1_lcd 0x27 // I2C address of display for FEATURE_LCD_YDv1 +//#define lcd_i2c_address_ydv1_lcd 0x38 // I2C address of display for FEATURE_LCD_YDv1 +#define lcd_i2c_address_sainsmart_lcd 0x27 // I2C address of display for FEATURE_LCD_SAINSMART_I2C +#define hell_pixel_microseconds 4025 +#define program_memory_limit_consec_spaces 1 +#define serial_leading_zeros 1 // set to 1 to activate leading zeros in serial numbers (i.e. #1 = 001) +#define serial_cut_numbers 0 // set to 1 to activate cut numbers in serial numbers (i.e. #10 = 1T, #19 = 1N) +#define go_to_sleep_inactivity_time 10 // minutes - FEATURE_SLEEP +#define dim_backlight_inactive_time 5 // minutes - FEATURE_LCD_BACKLIGHT_AUTO_DIM +#define default_cmos_super_keyer_iambic_b_timing_percent 33 // use with FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING; should be between 0 to 99 % (0% = true iambic b;100% = iambic a behavior) +#define default_cw_echo_timing_factor 1.75 // "factory default" setting +#define default_autospace_timing_factor 2.0 // "factory default" setting +#define winkey_paddle_echo_buffer_decode_timing_factor 0.25 +#define potentiometer_always_on 0 +#define ptt_interlock_check_every_ms 100 +#define ptt_interlock_active_state HIGH +#define unknown_cw_character '*' +#define cli_paddle_echo_on_at_boot 1 +#define cli_straight_key_echo_on_at_boot 1 +#define tx_key_dit_and_dah_pins_active_state HIGH +#define tx_key_dit_and_dah_pins_inactive_state LOW +#define potentiometer_check_interval_ms 150 +#define potentiometer_reading_threshold 1 +#define default_paddle_interruption_quiet_time_element_lengths 0 +#define default_wordsworth_wordspace 6 +#define default_wordsworth_repetition 1 +#define serial_program_memory_buffer_size 500 +#define eeprom_write_time_ms 30000 + +#ifdef FEATURE_BUTTONS +#define analog_buttons_number_of_buttons 4 +#define analog_buttons_r1 10 +#define analog_buttons_r2 1 +#endif + + +#if defined(FEATURE_BUTTONS) && !defined(FEATURE_PS2_KEYBOARD) && !defined(FEATURE_USB_KEYBOARD) && !defined(FEATURE_COMMAND_LINE_INTERFACE) && !defined(FEATURE_WINKEY_EMULATION) +#define number_of_memories byte(analog_buttons_number_of_buttons-1) +#else +#define number_of_memories byte(3) //12 +#endif + +#ifdef FEATURE_CAPACITIVE_PADDLE_PINS +#define capacitance_threshold 2 +#endif //FEATURE_CAPACITIVE_PADDLE_PINS + +#ifdef FEATURE_LED_RING +#define led_ring_low_limit 10 +#define led_ring_high_limit 50 +#endif //FEATURE_LED_RING + +#ifdef FEATURE_QLF +#define qlf_dit_max 125 +#define qlf_dit_min 75 +#define qlf_dah_max 200 +#define qlf_dah_min 100 +#define qlf_on_by_default 0 +#endif //FEATURE_QLF + + +#ifdef FEATURE_WINKEY_EMULATION + #ifdef OPTION_WINKEY_UCXLOG_9600_BAUD + #define WINKEY_DEFAULT_BAUD 9600 + #else + #define WINKEY_DEFAULT_BAUD 1200 + #endif //OPTION_WINKEY_UCXLOG_9600_BAUD +// alter these below to map alternate sidetones for Winkey interface protocol emulation + #ifdef OPTION_WINKEY_2_SUPPORT + #define WINKEY_SIDETONE_1 3759 + #define WINKEY_SIDETONE_2 1879 + #define WINKEY_SIDETONE_3 1252 + #define WINKEY_SIDETONE_4 940 + #define WINKEY_SIDETONE_5 752 + #define WINKEY_SIDETONE_6 625 + #define WINKEY_SIDETONE_7 535 + #define WINKEY_SIDETONE_8 469 + #define WINKEY_SIDETONE_9 417 + #define WINKEY_SIDETONE_10 375 + #else //OPTION_WINKEY_2_SUPPORT + #define WINKEY_SIDETONE_1 4000 + #define WINKEY_SIDETONE_2 2000 + #define WINKEY_SIDETONE_3 1333 + #define WINKEY_SIDETONE_4 1000 + #define WINKEY_SIDETONE_5 800 + #define WINKEY_SIDETONE_6 666 + #define WINKEY_SIDETONE_7 571 + #define WINKEY_SIDETONE_8 500 + #define WINKEY_SIDETONE_9 444 + #define WINKEY_SIDETONE_10 400 + #endif //OPTION_WINKEY_2_SUPPORT + +#define WINKEY_1_REPORT_VERSION_NUMBER 10 +#define WINKEY_2_REPORT_VERSION_NUMBER 23 + +// alter these to map to alternate hang time wordspace units +#define WINKEY_HANG_TIME_1_0 1.0 +#define WINKEY_HANG_TIME_1_33 1.33 +#define WINKEY_HANG_TIME_1_66 1.66 +#define WINKEY_HANG_TIME_2_0 2.0 + +#define WINKEY_RETURN_THIS_FOR_ADMIN_GET_CAL 0x16 +#define WINKEY_RETURN_THIS_FOR_ADMIN_PADDLE_A2D 0xEE +#define WINKEY_RETURN_THIS_FOR_ADMIN_SPEED_A2D 0x00 + +#endif //FEATURE_WINKEY_EMULATION + +#define PRIMARY_SERIAL_PORT &Serial +#define PRIMARY_SERIAL_PORT_BAUD 115200 // This applies only when the port is in Command Line Interface mode. In Winkey mode it will default to 1200. + +#ifdef FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + #define SECONDARY_SERIAL_PORT &Serial1 + #define SECONDARY_SERIAL_PORT_BAUD 115200 +#endif + +#define CW_DECODER_SCREEN_COLUMNS 40 // word wrap at this many columns +#define CW_DECODER_SPACE_PRINT_THRESH 4.5 // print space if no tone for this many element lengths +#define CW_DECODER_SPACE_DECODE_THRESH 2.0 // invoke character decode if no tone for this many element lengths +#define CW_DECODER_NOISE_FILTER 20 // ignore elements shorter than this (mS) + +#define STRAIGHT_KEY_ACTIVE_STATE LOW + +#ifdef FEATURE_DYNAMIC_DAH_TO_DIT_RATIO + #define DYNAMIC_DAH_TO_DIT_RATIO_LOWER_LIMIT_WPM 30 + #define DYNAMIC_DAH_TO_DIT_RATIO_LOWER_LIMIT_RATIO 300 // 300 = 3:1 ratio + #define DYNAMIC_DAH_TO_DIT_RATIO_UPPER_LIMIT_WPM 70 + #define DYNAMIC_DAH_TO_DIT_RATIO_UPPER_LIMIT_RATIO 240 // 240 = 2.4:1 ratio +#endif //FEATURE_DYNAMIC_DAH_TO_DIT_RATIO + +#if defined(FEATURE_SLEEP) + #define KEYER_AWAKE_PIN_AWAKE_STATE HIGH + #define KEYER_AWAKE_PIN_ASLEEP_STATE LOW +#endif + +#if defined(FEATURE_LCD_BACKLIGHT_AUTO_DIM) + #define keyer_power_led_awake_duty 255 // PWM duty cycle. 0 is 0%, 255 is 100% + #define keyer_power_led_asleep_duty 25 // 25 is quite dim. Use 0 for off +#endif + +#if defined(FEATURE_ETHERNET) + // #define FEATURE_ETHERNET_IP {192,168,1,178} // default IP address ("192.168.1.178") + // #define FEATURE_ETHERNET_MAC {0xDE,0xAD,0xBE,0xEF,0xFE,0xED} + #define FEATURE_ETHERNET_IP {192,168,1,179} // default IP address ("192.168.1.178") + #define FEATURE_ETHERNET_MAC {0xDE,0xAD,0xBE,0xEF,0xFE,0xEE} + #define FEATURE_ETHERNET_DNS {8,8,8,8} + + #define FEATURE_ETHERNET_GATEWAY {192,168,1,1} // default gateway + #define FEATURE_ETHERNET_SUBNET_MASK {255,255,255,0} // default subnet mask + #define FEATURE_ETHERNET_WEB_LISTENER_PORT 80 + #define FEATURE_UDP_SEND_BUFFER_SIZE 128 + #define FEATURE_UDP_RECEIVE_BUFFER_SIZE 128 +#endif //FEATURE_ETHERNET + +#define WEB_SERVER_CONTROL_TX_KEY_TIME_LIMIT_SECS 10 + +#define FEATURE_INTERNET_LINK_MAX_LINKS 2 +#define FEATURE_INTERNET_LINK_DEFAULT_RCV_UDP_PORT 8888 +#define FEATURE_INTERNET_LINK_BUFFER_TIME_MS 500 + +#if defined(FEATURE_4x4_KEYPAD)|| defined (FEATURE_3x4_KEYPAD) + #define KEYPAD_ROWS 4 //KeyPad Rows + #if defined(FEATURE_4x4_KEYPAD) + #define KEYPAD_COLS 4 //keypad Columns + #else + #define KEYPAD_COLS 3 + #endif + #define mem1 0 //Memory numbers for Keypad: Actual memory numbers start with "0" + #define mem2 1 + #define mem3 2 + #define mem4 3 + #define mem5 4 + #define mem6 5 + #define mem7 6 + #define mem8 7 + #define mem9 8 + #define mem10 9 + #define mem11 10 + #define mem12 11 +#endif //#if defined(FEATURE_4x4_KEYPAD)|| defined (FEATURE_3x4_KEYPAD) + +#define initial_sidetone_mode 1 // Sidetone mode, 0=OFF, 1=ON, 2=PADDLE_ONLY + +#define sd_card_spi_ss_line 4 + +#if defined(OPTION_DFROBOT_LCD_COMMAND_BUTTONS) + + // For V1.1 board use these values + #define dfrobot_btnRIGHT_analog 50 + #define dfrobot_btnUP_analog 250 + #define dfrobot_btnDOWN_analog 450 + #define dfrobot_btnLEFT_analog 650 + #define dfrobot_btnSELECT_analog 850 + + // For V1.0 board use these values + // #define dfrobot_btnRIGHT_analog 50 + // #define dfrobot_btnUP_analog 195 + // #define dfrobot_btnDOWN_analog 380 + // #define dfrobot_btnLEFT_analog 555 + // #define dfrobot_btnSELECT_analog 790 + + // button to memory mappings (0 = command button, 1 = memory 1, 2 = memory 2, etc.) + #define dfrobot_btnRIGHT 2 + #define dfrobot_btnUP 1 + #define dfrobot_btnDOWN 3 + #define dfrobot_btnLEFT 4 + #define dfrobot_btnSELECT 0 + #define dfrobot_btnNONE 255 // do not change + +#endif + +#define sequencer_pins_active_state HIGH +#define sequencer_pins_inactive_state LOW + +#define sequencer_pins_active_state HIGH +#define sequencer_pins_inactive_state LOW +#define ptt_line_active_state HIGH +#define ptt_line_inactive_state LOW +#define tx_key_line_active_state HIGH +#define tx_key_line_inactive_state LOW +#define ptt_input_pin_active_state LOW +#define ptt_input_pin_inactive_state HIGH +#define tx_inhibit_pin_active_state LOW +#define tx_inhibit_pin_inactive_state HIGH +#define tx_pause_pin_active_state LOW +#define tx_pause_pin_inactive_state HIGH +#define sidetone_line_active_state HIGH +#define sidetone_line_inactive_state LOW + +#if defined(ARDUINO_MAPLE_MINI) + #define button_value_factor 4095 //sp5iou contributed +#else + #define button_value_factor 1023 +#endif + +#define farnsworth_timing_calibration 1.15 + +#define sidetone_volume_low_limit 10 +#define sidetone_volume_high_limit 500 + +#define custom_startup_field "your custom text here" // an example could be callsign and name, eg. "AB1XYZ Bob", (or "Worlds best operator" which requires a 20 column display), string length shouldo be no more than the number of columns on the display + +#define command_mode_acknowledgement_character 'E' + +#if defined(FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT) + #define command_a_iambic_a "A" + #define command_b_iambic_b "B" + #define command_c_single_paddle "SINGLE" + #define command_d_ultimatic "ULT" + #define command_h_weight_dit_dah_ratio_default "R" + #define command_i_tx_on "TX ON" + #define command_i_tx_off "TX OFF" + #define command_k_dit_dah_buffers_on "ON" + #define command_k_dit_dah_buffers_off "OFF" + #define command_n_paddle_reverse "REV" + #define command_n_paddle_normal "NORM" + #define command_o_sidetone_off "ST OFF" + #define command_o_sidetone_paddle_only "ST PD ONLY" + #define command_o_sidetone_on "ST ON" + #define command_t_tune_mode "TUNE" + #define command_v_potentiometer_on "POT ON" + #define command_v_potentiometer_off "POT OFF" + + #define command_error "ERR" + +#endif //FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT diff --git a/k3ng_keyer/keyer_settings_nanokeyer_rev_d.h b/k3ng_keyer/keyer_settings_nanokeyer_rev_d.h new file mode 100755 index 0000000..b754af7 --- /dev/null +++ b/k3ng_keyer/keyer_settings_nanokeyer_rev_d.h @@ -0,0 +1,310 @@ +// Initial and hardcoded settings +#define initial_speed_wpm 26 // "factory default" keyer speed setting +#define initial_command_mode_speed_wpm 20 // "factory default" command mode speed setting +#define initial_sidetone_freq 600 // "factory default" sidetone frequency setting +#define sidetone_hz_limit_low 299 +#define sidetone_hz_limit_high 2001 +#define hz_high_beep 1500 // frequency in hertz of high beep +#define hz_low_beep 400 // frequency in hertz of low beep +#define initial_dah_to_dit_ratio 300 // 300 = 3 / normal 3:1 ratio +#define initial_ptt_lead_time_tx1 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx1 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx2 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx2 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx3 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx3 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx4 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx4 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx5 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx5 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx6 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx6 10 // PTT tail time in mS +#define initial_qrss_dit_length 1 // QRSS dit length in seconds +#define initial_pot_wpm_low_value 13 // Potentiometer WPM fully CCW +#define initial_pot_wpm_high_value 35 // Potentiometer WPM fully CW +#define wpm_limit_low 5 +#define wpm_limit_high 60 +#define potentiometer_change_threshold 0.9 // don't change the keyer speed until pot wpm has changed more than this +#define send_buffer_size 150 +#define default_length_letterspace 3 +#define default_length_wordspace 7 +#define default_keying_compensation 0 // number of milliseconds to extend all dits and dahs - for QSK on boatanchors +#define default_first_extension_time 0 // number of milliseconds to extend first sent dit or dah +#define default_pot_full_scale_reading 1023 +#define default_weighting 50 // 50 = weighting factor of 1 (normal) +#define default_ptt_hang_time_wordspace_units 0.0 +#define winkey_c0_wait_time 1 // the number of milliseconds to wait to send 0xc0 byte after send buffer has been sent +#define winkey_command_timeout_ms 5000 +#define winkey_discard_bytes_startup 3 // this is used if OPTION_WINKEY_DISCARD_BYTES_AT_STARTUP is enabled above +#define winkey_xoff_threshold 20 // the number of chars in the buffer when we begin sending XOFFs +#define winkey_xon_threshold 10 // the number of chars in the buffer below which we deactivate XOFF +#define default_memory_repeat_time 3000 // time in milliseconds +#define LCD_COLUMNS 16 +#define LCD_ROWS 2 +#define lcd_i2c_address_mathertel_PCF8574 0x27 // I2C address of display for FEATURE_LCD_MATHERTEL_PCF8574 +#define lcd_i2c_address_fdebrander_lcd 0x27 // I2C address of display for FEATURE_LCD_I2C_FDEBRABANDER +#define lcd_i2c_address_ydv1_lcd 0x27 // I2C address of display for FEATURE_LCD_YDv1 +//#define lcd_i2c_address_ydv1_lcd 0x38 // I2C address of display for FEATURE_LCD_YDv1 +#define lcd_i2c_address_sainsmart_lcd 0x27 // I2C address of display for FEATURE_LCD_SAINSMART_I2C +#define hell_pixel_microseconds 4025 +#define program_memory_limit_consec_spaces 1 +#define serial_leading_zeros 1 // set to 1 to activate leading zeros in serial numbers (i.e. #1 = 001) +#define serial_cut_numbers 0 // set to 1 to activate cut numbers in serial numbers (i.e. #10 = 1T, #19 = 1N) +#define go_to_sleep_inactivity_time 10 // minutes - FEATURE_SLEEP +#define dim_backlight_inactive_time 5 // minutes - FEATURE_LCD_BACKLIGHT_AUTO_DIM +#define default_cmos_super_keyer_iambic_b_timing_percent 33 // use with FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING; should be between 0 to 99 % (0% = true iambic b;100% = iambic a behavior) +#define default_cw_echo_timing_factor 1.75 // "factory default" setting +#define default_autospace_timing_factor 2.0 // "factory default" setting +#define winkey_paddle_echo_buffer_decode_timing_factor 0.25 +#define potentiometer_always_on 0 +#define ptt_interlock_check_every_ms 100 +#define ptt_interlock_active_state HIGH +#define unknown_cw_character '*' +#define cli_paddle_echo_on_at_boot 1 +#define cli_straight_key_echo_on_at_boot 1 +#define tx_key_dit_and_dah_pins_active_state HIGH +#define tx_key_dit_and_dah_pins_inactive_state LOW +#define potentiometer_check_interval_ms 150 +#define potentiometer_reading_threshold 1 +#define default_paddle_interruption_quiet_time_element_lengths 0 +#define default_wordsworth_wordspace 6 +#define default_wordsworth_repetition 1 +#define serial_program_memory_buffer_size 500 +#define eeprom_write_time_ms 30000 + +#ifdef FEATURE_BUTTONS +#define analog_buttons_number_of_buttons 4 +#define analog_buttons_r1 10 +#define analog_buttons_r2 1 +#endif + + +#if defined(FEATURE_BUTTONS) && !defined(FEATURE_PS2_KEYBOARD) && !defined(FEATURE_USB_KEYBOARD) && !defined(FEATURE_COMMAND_LINE_INTERFACE) && !defined(FEATURE_WINKEY_EMULATION) +#define number_of_memories byte(analog_buttons_number_of_buttons-1) +#else +#define number_of_memories byte(12) +#endif + +#ifdef FEATURE_CAPACITIVE_PADDLE_PINS +#define capacitance_threshold 2 +#endif //FEATURE_CAPACITIVE_PADDLE_PINS + +#ifdef FEATURE_LED_RING +#define led_ring_low_limit 10 +#define led_ring_high_limit 50 +#endif //FEATURE_LED_RING + +#ifdef FEATURE_QLF +#define qlf_dit_max 125 +#define qlf_dit_min 75 +#define qlf_dah_max 200 +#define qlf_dah_min 100 +#define qlf_on_by_default 0 +#endif //FEATURE_QLF + + +#ifdef FEATURE_WINKEY_EMULATION + #ifdef OPTION_WINKEY_UCXLOG_9600_BAUD + #define WINKEY_DEFAULT_BAUD 9600 + #else + #define WINKEY_DEFAULT_BAUD 1200 + #endif //OPTION_WINKEY_UCXLOG_9600_BAUD +// alter these below to map alternate sidetones for Winkey interface protocol emulation + #ifdef OPTION_WINKEY_2_SUPPORT + #define WINKEY_SIDETONE_1 3759 + #define WINKEY_SIDETONE_2 1879 + #define WINKEY_SIDETONE_3 1252 + #define WINKEY_SIDETONE_4 940 + #define WINKEY_SIDETONE_5 752 + #define WINKEY_SIDETONE_6 625 + #define WINKEY_SIDETONE_7 535 + #define WINKEY_SIDETONE_8 469 + #define WINKEY_SIDETONE_9 417 + #define WINKEY_SIDETONE_10 375 + #else //OPTION_WINKEY_2_SUPPORT + #define WINKEY_SIDETONE_1 4000 + #define WINKEY_SIDETONE_2 2000 + #define WINKEY_SIDETONE_3 1333 + #define WINKEY_SIDETONE_4 1000 + #define WINKEY_SIDETONE_5 800 + #define WINKEY_SIDETONE_6 666 + #define WINKEY_SIDETONE_7 571 + #define WINKEY_SIDETONE_8 500 + #define WINKEY_SIDETONE_9 444 + #define WINKEY_SIDETONE_10 400 +#endif //OPTION_WINKEY_2_SUPPORT + +#define WINKEY_1_REPORT_VERSION_NUMBER 10 +#define WINKEY_2_REPORT_VERSION_NUMBER 23 + +// alter these to map to alternate hang time wordspace units +#define WINKEY_HANG_TIME_1_0 1.0 +#define WINKEY_HANG_TIME_1_33 1.33 +#define WINKEY_HANG_TIME_1_66 1.66 +#define WINKEY_HANG_TIME_2_0 2.0 + +#define WINKEY_RETURN_THIS_FOR_ADMIN_GET_CAL 0x16 +#define WINKEY_RETURN_THIS_FOR_ADMIN_PADDLE_A2D 0xEE +#define WINKEY_RETURN_THIS_FOR_ADMIN_SPEED_A2D 0x00 + +#endif //FEATURE_WINKEY_EMULATION + +#define PRIMARY_SERIAL_PORT &Serial +#define PRIMARY_SERIAL_PORT_BAUD 115200 // This applies only when the port is in Command Line Interface mode. In Winkey mode it will default to 1200. + +#ifdef FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + #define SECONDARY_SERIAL_PORT &Serial1 + #define SECONDARY_SERIAL_PORT_BAUD 115200 +#endif + +#define CW_DECODER_SCREEN_COLUMNS 40 // word wrap at this many columns +#define CW_DECODER_SPACE_PRINT_THRESH 4.5 // print space if no tone for this many element lengths +#define CW_DECODER_SPACE_DECODE_THRESH 2.0 // invoke character decode if no tone for this many element lengths +#define CW_DECODER_NOISE_FILTER 20 // ignore elements shorter than this (mS) + +#define STRAIGHT_KEY_ACTIVE_STATE LOW + + +#ifdef FEATURE_DYNAMIC_DAH_TO_DIT_RATIO + #define DYNAMIC_DAH_TO_DIT_RATIO_LOWER_LIMIT_WPM 30 + #define DYNAMIC_DAH_TO_DIT_RATIO_LOWER_LIMIT_RATIO 300 // 300 = 3:1 ratio + #define DYNAMIC_DAH_TO_DIT_RATIO_UPPER_LIMIT_WPM 70 + #define DYNAMIC_DAH_TO_DIT_RATIO_UPPER_LIMIT_RATIO 240 // 240 = 2.4:1 ratio +#endif //FEATURE_DYNAMIC_DAH_TO_DIT_RATIO + +#if defined(FEATURE_SLEEP) + #define KEYER_AWAKE_PIN_AWAKE_STATE HIGH + #define KEYER_AWAKE_PIN_ASLEEP_STATE LOW +#endif + +#if defined(FEATURE_LCD_BACKLIGHT_AUTO_DIM) + #define keyer_power_led_awake_duty 255 // PWM duty cycle. 0 is 0%, 255 is 100% + #define keyer_power_led_asleep_duty 25 // 25 is quite dim. Use 0 for off +#endif + +#if defined(FEATURE_ETHERNET) + // #define FEATURE_ETHERNET_IP {192,168,1,178} // default IP address ("192.168.1.178") + // #define FEATURE_ETHERNET_MAC {0xDE,0xAD,0xBE,0xEF,0xFE,0xED} + #define FEATURE_ETHERNET_IP {192,168,1,179} // default IP address ("192.168.1.178") + #define FEATURE_ETHERNET_MAC {0xDE,0xAD,0xBE,0xEF,0xFE,0xEE} + #define FEATURE_ETHERNET_DNS {8,8,8,8} + + #define FEATURE_ETHERNET_GATEWAY {192,168,1,1} // default gateway + #define FEATURE_ETHERNET_SUBNET_MASK {255,255,255,0} // default subnet mask + #define FEATURE_ETHERNET_WEB_LISTENER_PORT 80 + #define FEATURE_UDP_SEND_BUFFER_SIZE 128 + #define FEATURE_UDP_RECEIVE_BUFFER_SIZE 128 +#endif //FEATURE_ETHERNET + +#define WEB_SERVER_CONTROL_TX_KEY_TIME_LIMIT_SECS 10 + +#define FEATURE_INTERNET_LINK_MAX_LINKS 2 +#define FEATURE_INTERNET_LINK_DEFAULT_RCV_UDP_PORT 8888 +#define FEATURE_INTERNET_LINK_BUFFER_TIME_MS 500 + +#if defined(FEATURE_4x4_KEYPAD)|| defined (FEATURE_3x4_KEYPAD) + #define KEYPAD_ROWS 4 //KeyPad Rows + #if defined(FEATURE_4x4_KEYPAD) + #define KEYPAD_COLS 4 //keypad Columns + #else + #define KEYPAD_COLS 3 + #endif + #define mem1 0 //Memory numbers for Keypad: Actual memory numbers start with "0" + #define mem2 1 + #define mem3 2 + #define mem4 3 + #define mem5 4 + #define mem6 5 + #define mem7 6 + #define mem8 7 + #define mem9 8 + #define mem10 9 + #define mem11 10 + #define mem12 11 +#endif //#if defined(FEATURE_4x4_KEYPAD)|| defined (FEATURE_3x4_KEYPAD) + +#define initial_sidetone_mode 1 // Sidetone mode, 0=OFF, 1=ON, 2=PADDLE_ONLY + +#define sd_card_spi_ss_line 4 + +#if defined(OPTION_DFROBOT_LCD_COMMAND_BUTTONS) + + // For V1.1 board use these values + #define dfrobot_btnRIGHT_analog 50 + #define dfrobot_btnUP_analog 250 + #define dfrobot_btnDOWN_analog 450 + #define dfrobot_btnLEFT_analog 650 + #define dfrobot_btnSELECT_analog 850 + + // For V1.0 board use these values + // #define dfrobot_btnRIGHT_analog 50 + // #define dfrobot_btnUP_analog 195 + // #define dfrobot_btnDOWN_analog 380 + // #define dfrobot_btnLEFT_analog 555 + // #define dfrobot_btnSELECT_analog 790 + + // button to memory mappings (0 = command button, 1 = memory 1, 2 = memory 2, etc.) + #define dfrobot_btnRIGHT 2 + #define dfrobot_btnUP 1 + #define dfrobot_btnDOWN 3 + #define dfrobot_btnLEFT 4 + #define dfrobot_btnSELECT 0 + #define dfrobot_btnNONE 255 // do not change + +#endif + +#define sequencer_pins_active_state HIGH +#define sequencer_pins_inactive_state LOW + +#define sequencer_pins_active_state HIGH +#define sequencer_pins_inactive_state LOW +#define ptt_line_active_state HIGH +#define ptt_line_inactive_state LOW +#define tx_key_line_active_state HIGH +#define tx_key_line_inactive_state LOW +#define ptt_input_pin_active_state LOW +#define ptt_input_pin_inactive_state HIGH +#define tx_inhibit_pin_active_state LOW +#define tx_inhibit_pin_inactive_state HIGH +#define tx_pause_pin_active_state LOW +#define tx_pause_pin_inactive_state HIGH +#define sidetone_line_active_state HIGH +#define sidetone_line_inactive_state LOW + +#if defined(ARDUINO_MAPLE_MINI) + #define button_value_factor 4095 //sp5iou contributed +#else + #define button_value_factor 1023 +#endif + +#define farnsworth_timing_calibration 1.15 + +#define sidetone_volume_low_limit 10 +#define sidetone_volume_high_limit 500 + +#define custom_startup_field "your custom text here" // an example could be callsign and name, eg. "AB1XYZ Bob", (or "Worlds best operator" which requires a 20 column display), string length shouldo be no more than the number of columns on the display + +#define command_mode_acknowledgement_character 'E' + +#if defined(FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT) + #define command_a_iambic_a "A" + #define command_b_iambic_b "B" + #define command_c_single_paddle "SINGLE" + #define command_d_ultimatic "ULT" + #define command_h_weight_dit_dah_ratio_default "R" + #define command_i_tx_on "TX ON" + #define command_i_tx_off "TX OFF" + #define command_k_dit_dah_buffers_on "ON" + #define command_k_dit_dah_buffers_off "OFF" + #define command_n_paddle_reverse "REV" + #define command_n_paddle_normal "NORM" + #define command_o_sidetone_off "ST OFF" + #define command_o_sidetone_paddle_only "ST PD ONLY" + #define command_o_sidetone_on "ST ON" + #define command_t_tune_mode "TUNE" + #define command_v_potentiometer_on "POT ON" + #define command_v_potentiometer_off "POT OFF" + + #define command_error "ERR" + +#endif //FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT diff --git a/k3ng_keyer/keyer_settings_open_interface.h b/k3ng_keyer/keyer_settings_open_interface.h new file mode 100755 index 0000000..cef97b5 --- /dev/null +++ b/k3ng_keyer/keyer_settings_open_interface.h @@ -0,0 +1,312 @@ +// This file is for the Open Interface http://remoteqth.com/open-interface.php + +// Initial and hardcoded settings +#define initial_speed_wpm 26 // "factory default" keyer speed setting +#define initial_command_mode_speed_wpm 20 // "factory default" command mode speed setting +#define initial_sidetone_freq 600 // "factory default" sidetone frequency setting +#define sidetone_hz_limit_low 299 +#define sidetone_hz_limit_high 2001 +#define hz_high_beep 1500 // frequency in hertz of high beep +#define hz_low_beep 400 // frequency in hertz of low beep +#define initial_dah_to_dit_ratio 300 // 300 = 3 / normal 3:1 ratio +#define initial_ptt_lead_time_tx1 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx1 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx2 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx2 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx3 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx3 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx4 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx4 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx5 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx5 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx6 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx6 10 // PTT tail time in mS +#define initial_qrss_dit_length 1 // QRSS dit length in seconds +#define initial_pot_wpm_low_value 13 // Potentiometer WPM fully CCW +#define initial_pot_wpm_high_value 35 // Potentiometer WPM fully CW +#define wpm_limit_low 5 +#define wpm_limit_high 60 +#define potentiometer_change_threshold 0.9 // don't change the keyer speed until pot wpm has changed more than this +#define send_buffer_size 150 +#define default_length_letterspace 3 +#define default_length_wordspace 7 +#define default_keying_compensation 0 // number of milliseconds to extend all dits and dahs - for QSK on boatanchors +#define default_first_extension_time 0 // number of milliseconds to extend first sent dit or dah +#define default_pot_full_scale_reading 1023 +#define default_weighting 50 // 50 = weighting factor of 1 (normal) +#define default_ptt_hang_time_wordspace_units 0.0 +#define winkey_c0_wait_time 1 // the number of milliseconds to wait to send 0xc0 byte after send buffer has been sent +#define winkey_command_timeout_ms 5000 +#define winkey_discard_bytes_startup 3 // this is used if OPTION_WINKEY_DISCARD_BYTES_AT_STARTUP is enabled above +#define winkey_xoff_threshold 20 // the number of chars in the buffer when we begin sending XOFFs +#define winkey_xon_threshold 10 // the number of chars in the buffer below which we deactivate XOFF +#define default_memory_repeat_time 3000 // time in milliseconds +#define LCD_COLUMNS 16 +#define LCD_ROWS 2 +#define lcd_i2c_address_mathertel_PCF8574 0x27 // I2C address of display for FEATURE_LCD_MATHERTEL_PCF8574 +#define lcd_i2c_address_fdebrander_lcd 0x27 // I2C address of display for FEATURE_LCD_I2C_FDEBRABANDER +#define lcd_i2c_address_ydv1_lcd 0x27 // I2C address of display for FEATURE_LCD_YDv1 +//#define lcd_i2c_address_ydv1_lcd 0x38 // I2C address of display for FEATURE_LCD_YDv1 +#define lcd_i2c_address_sainsmart_lcd 0x27 // I2C address of display for FEATURE_LCD_SAINSMART_I2C +#define hell_pixel_microseconds 4025 +#define program_memory_limit_consec_spaces 1 +#define serial_leading_zeros 1 // set to 1 to activate leading zeros in serial numbers (i.e. #1 = 001) +#define serial_cut_numbers 0 // set to 1 to activate cut numbers in serial numbers (i.e. #10 = 1T, #19 = 1N) +#define go_to_sleep_inactivity_time 10 // minutes - FEATURE_SLEEP +#define dim_backlight_inactive_time 5 // minutes - FEATURE_LCD_BACKLIGHT_AUTO_DIM +#define default_cmos_super_keyer_iambic_b_timing_percent 33 // use with FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING; should be between 0 to 99 % (0% = true iambic b;100% = iambic a behavior) +#define default_cw_echo_timing_factor 1.75 // "factory default" setting +#define default_autospace_timing_factor 2.0 // "factory default" setting +#define winkey_paddle_echo_buffer_decode_timing_factor 0.25 +#define potentiometer_always_on 0 +#define ptt_interlock_check_every_ms 100 +#define ptt_interlock_active_state HIGH +#define unknown_cw_character '*' +#define cli_paddle_echo_on_at_boot 1 +#define cli_straight_key_echo_on_at_boot 1 +#define tx_key_dit_and_dah_pins_active_state HIGH +#define tx_key_dit_and_dah_pins_inactive_state LOW +#define potentiometer_check_interval_ms 150 +#define potentiometer_reading_threshold 1 +#define default_paddle_interruption_quiet_time_element_lengths 0 +#define default_wordsworth_wordspace 6 +#define default_wordsworth_repetition 1 +#define serial_program_memory_buffer_size 500 +#define eeprom_write_time_ms 30000 + +#ifdef FEATURE_BUTTONS +#define analog_buttons_number_of_buttons 4 +#define analog_buttons_r1 10 +#define analog_buttons_r2 1 +#endif + + +#if defined(FEATURE_BUTTONS) && !defined(FEATURE_PS2_KEYBOARD) && !defined(FEATURE_USB_KEYBOARD) && !defined(FEATURE_COMMAND_LINE_INTERFACE) && !defined(FEATURE_WINKEY_EMULATION) +#define number_of_memories byte(analog_buttons_number_of_buttons-1) +#else +#define number_of_memories byte(12) +#endif + +#ifdef FEATURE_CAPACITIVE_PADDLE_PINS +#define capacitance_threshold 2 +#endif //FEATURE_CAPACITIVE_PADDLE_PINS + +#ifdef FEATURE_LED_RING +#define led_ring_low_limit 10 +#define led_ring_high_limit 50 +#endif //FEATURE_LED_RING + +#ifdef FEATURE_QLF +#define qlf_dit_max 125 +#define qlf_dit_min 75 +#define qlf_dah_max 200 +#define qlf_dah_min 100 +#define qlf_on_by_default 0 +#endif //FEATURE_QLF + + +#ifdef FEATURE_WINKEY_EMULATION + #ifdef OPTION_WINKEY_UCXLOG_9600_BAUD + #define WINKEY_DEFAULT_BAUD 9600 + #else + #define WINKEY_DEFAULT_BAUD 1200 + #endif //OPTION_WINKEY_UCXLOG_9600_BAUD +// alter these below to map alternate sidetones for Winkey interface protocol emulation +#ifdef OPTION_WINKEY_2_SUPPORT + #define WINKEY_SIDETONE_1 3759 + #define WINKEY_SIDETONE_2 1879 + #define WINKEY_SIDETONE_3 1252 + #define WINKEY_SIDETONE_4 940 + #define WINKEY_SIDETONE_5 752 + #define WINKEY_SIDETONE_6 625 + #define WINKEY_SIDETONE_7 535 + #define WINKEY_SIDETONE_8 469 + #define WINKEY_SIDETONE_9 417 + #define WINKEY_SIDETONE_10 375 + #else //OPTION_WINKEY_2_SUPPORT + #define WINKEY_SIDETONE_1 4000 + #define WINKEY_SIDETONE_2 2000 + #define WINKEY_SIDETONE_3 1333 + #define WINKEY_SIDETONE_4 1000 + #define WINKEY_SIDETONE_5 800 + #define WINKEY_SIDETONE_6 666 + #define WINKEY_SIDETONE_7 571 + #define WINKEY_SIDETONE_8 500 + #define WINKEY_SIDETONE_9 444 + #define WINKEY_SIDETONE_10 400 +#endif //OPTION_WINKEY_2_SUPPORT + +#define WINKEY_1_REPORT_VERSION_NUMBER 10 +#define WINKEY_2_REPORT_VERSION_NUMBER 23 + +// alter these to map to alternate hang time wordspace units +#define WINKEY_HANG_TIME_1_0 1.0 +#define WINKEY_HANG_TIME_1_33 1.33 +#define WINKEY_HANG_TIME_1_66 1.66 +#define WINKEY_HANG_TIME_2_0 2.0 + +#define WINKEY_RETURN_THIS_FOR_ADMIN_GET_CAL 0x16 +#define WINKEY_RETURN_THIS_FOR_ADMIN_PADDLE_A2D 0xEE +#define WINKEY_RETURN_THIS_FOR_ADMIN_SPEED_A2D 0x00 + +#endif //FEATURE_WINKEY_EMULATION + + +#define PRIMARY_SERIAL_PORT &Serial +#define PRIMARY_SERIAL_PORT_BAUD 115200 // This applies only when the port is in Command Line Interface mode. In Winkey mode it will default to 1200. + +#ifdef FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + #define SECONDARY_SERIAL_PORT &Serial1 + #define SECONDARY_SERIAL_PORT_BAUD 115200 +#endif + +#define CW_DECODER_SCREEN_COLUMNS 40 // word wrap at this many columns +#define CW_DECODER_SPACE_PRINT_THRESH 4.5 // print space if no tone for this many element lengths +#define CW_DECODER_SPACE_DECODE_THRESH 2.0 // invoke character decode if no tone for this many element lengths +#define CW_DECODER_NOISE_FILTER 20 // ignore elements shorter than this (mS) + +#define STRAIGHT_KEY_ACTIVE_STATE LOW + +#ifdef FEATURE_DYNAMIC_DAH_TO_DIT_RATIO + #define DYNAMIC_DAH_TO_DIT_RATIO_LOWER_LIMIT_WPM 30 + #define DYNAMIC_DAH_TO_DIT_RATIO_LOWER_LIMIT_RATIO 300 // 300 = 3:1 ratio + #define DYNAMIC_DAH_TO_DIT_RATIO_UPPER_LIMIT_WPM 70 + #define DYNAMIC_DAH_TO_DIT_RATIO_UPPER_LIMIT_RATIO 240 // 240 = 2.4:1 ratio +#endif //FEATURE_DYNAMIC_DAH_TO_DIT_RATIO + +#if defined(FEATURE_SLEEP) + #define KEYER_AWAKE_PIN_AWAKE_STATE HIGH + #define KEYER_AWAKE_PIN_ASLEEP_STATE LOW +#endif + +#if defined(FEATURE_LCD_BACKLIGHT_AUTO_DIM) + #define keyer_power_led_awake_duty 255 // PWM duty cycle. 0 is 0%, 255 is 100% + #define keyer_power_led_asleep_duty 25 // 25 is quite dim. Use 0 for off +#endif + +#if defined(FEATURE_ETHERNET) + // #define FEATURE_ETHERNET_IP {192,168,1,178} // default IP address ("192.168.1.178") + // #define FEATURE_ETHERNET_MAC {0xDE,0xAD,0xBE,0xEF,0xFE,0xED} + #define FEATURE_ETHERNET_IP {192,168,1,179} // default IP address ("192.168.1.178") + #define FEATURE_ETHERNET_MAC {0xDE,0xAD,0xBE,0xEF,0xFE,0xEE} + #define FEATURE_ETHERNET_DNS {8,8,8,8} + + #define FEATURE_ETHERNET_GATEWAY {192,168,1,1} // default gateway + #define FEATURE_ETHERNET_SUBNET_MASK {255,255,255,0} // default subnet mask + #define FEATURE_ETHERNET_WEB_LISTENER_PORT 80 + #define FEATURE_UDP_SEND_BUFFER_SIZE 128 + #define FEATURE_UDP_RECEIVE_BUFFER_SIZE 128 +#endif //FEATURE_ETHERNET + +#define WEB_SERVER_CONTROL_TX_KEY_TIME_LIMIT_SECS 10 + +#define FEATURE_INTERNET_LINK_MAX_LINKS 2 +#define FEATURE_INTERNET_LINK_DEFAULT_RCV_UDP_PORT 8888 +#define FEATURE_INTERNET_LINK_BUFFER_TIME_MS 500 + +#if defined(FEATURE_4x4_KEYPAD)|| defined (FEATURE_3x4_KEYPAD) + #define KEYPAD_ROWS 4 //KeyPad Rows + #if defined(FEATURE_4x4_KEYPAD) + #define KEYPAD_COLS 4 //keypad Columns + #else + #define KEYPAD_COLS 3 + #endif + #define mem1 0 //Memory numbers for Keypad: Actual memory numbers start with "0" + #define mem2 1 + #define mem3 2 + #define mem4 3 + #define mem5 4 + #define mem6 5 + #define mem7 6 + #define mem8 7 + #define mem9 8 + #define mem10 9 + #define mem11 10 + #define mem12 11 +#endif //#if defined(FEATURE_4x4_KEYPAD)|| defined (FEATURE_3x4_KEYPAD) + +#define initial_sidetone_mode 1 // Sidetone mode, 0=OFF, 1=ON, 2=PADDLE_ONLY + +#define sd_card_spi_ss_line 4 + +#if defined(OPTION_DFROBOT_LCD_COMMAND_BUTTONS) + + // For V1.1 board use these values + #define dfrobot_btnRIGHT_analog 50 + #define dfrobot_btnUP_analog 250 + #define dfrobot_btnDOWN_analog 450 + #define dfrobot_btnLEFT_analog 650 + #define dfrobot_btnSELECT_analog 850 + + // For V1.0 board use these values + // #define dfrobot_btnRIGHT_analog 50 + // #define dfrobot_btnUP_analog 195 + // #define dfrobot_btnDOWN_analog 380 + // #define dfrobot_btnLEFT_analog 555 + // #define dfrobot_btnSELECT_analog 790 + + // button to memory mappings (0 = command button, 1 = memory 1, 2 = memory 2, etc.) + #define dfrobot_btnRIGHT 2 + #define dfrobot_btnUP 1 + #define dfrobot_btnDOWN 3 + #define dfrobot_btnLEFT 4 + #define dfrobot_btnSELECT 0 + #define dfrobot_btnNONE 255 // do not change + +#endif + +#define sequencer_pins_active_state HIGH +#define sequencer_pins_inactive_state LOW + +#define sequencer_pins_active_state HIGH +#define sequencer_pins_inactive_state LOW +#define ptt_line_active_state HIGH +#define ptt_line_inactive_state LOW +#define tx_key_line_active_state HIGH +#define tx_key_line_inactive_state LOW +#define ptt_input_pin_active_state LOW +#define ptt_input_pin_inactive_state HIGH +#define tx_inhibit_pin_active_state LOW +#define tx_inhibit_pin_inactive_state HIGH +#define tx_pause_pin_active_state LOW +#define tx_pause_pin_inactive_state HIGH +#define sidetone_line_active_state HIGH +#define sidetone_line_inactive_state LOW + +#if defined(ARDUINO_MAPLE_MINI) + #define button_value_factor 4095 //sp5iou contributed +#else + #define button_value_factor 1023 +#endif + +#define farnsworth_timing_calibration 1.15 + +#define sidetone_volume_low_limit 10 +#define sidetone_volume_high_limit 500 + +#define custom_startup_field "your custom text here" // an example could be callsign and name, eg. "AB1XYZ Bob", (or "Worlds best operator" which requires a 20 column display), string length shouldo be no more than the number of columns on the display + +#define command_mode_acknowledgement_character 'E' + +#if defined(FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT) + #define command_a_iambic_a "A" + #define command_b_iambic_b "B" + #define command_c_single_paddle "SINGLE" + #define command_d_ultimatic "ULT" + #define command_h_weight_dit_dah_ratio_default "R" + #define command_i_tx_on "TX ON" + #define command_i_tx_off "TX OFF" + #define command_k_dit_dah_buffers_on "ON" + #define command_k_dit_dah_buffers_off "OFF" + #define command_n_paddle_reverse "REV" + #define command_n_paddle_normal "NORM" + #define command_o_sidetone_off "ST OFF" + #define command_o_sidetone_paddle_only "ST PD ONLY" + #define command_o_sidetone_on "ST ON" + #define command_t_tune_mode "TUNE" + #define command_v_potentiometer_on "POT ON" + #define command_v_potentiometer_off "POT OFF" + + #define command_error "ERR" + +#endif //FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT diff --git a/k3ng_keyer/keyer_settings_opencwkeyer_mk2.h b/k3ng_keyer/keyer_settings_opencwkeyer_mk2.h new file mode 100755 index 0000000..d0fa9c5 --- /dev/null +++ b/k3ng_keyer/keyer_settings_opencwkeyer_mk2.h @@ -0,0 +1,316 @@ +// Initial and hardcoded settings +#define initial_speed_wpm 25 // "factory default" keyer speed setting +#define initial_command_mode_speed_wpm 20 // "factory default" command mode speed setting +#define initial_sidetone_freq 600 // "factory default" sidetone frequency setting +#define sidetone_hz_limit_low 299 +#define sidetone_hz_limit_high 2001 +#define hz_high_beep 1500 // frequency in hertz of high beep +#define hz_low_beep 400 // frequency in hertz of low beep +#define initial_dah_to_dit_ratio 300 // 300 = 3 / normal 3:1 ratio +#define initial_ptt_lead_time_tx1 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx1 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx2 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx2 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx3 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx3 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx4 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx4 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx5 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx5 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx6 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx6 10 // PTT tail time in mS +#define initial_qrss_dit_length 1 // QRSS dit length in seconds +#define initial_pot_wpm_low_value 13 // Potentiometer WPM fully CCW +#define initial_pot_wpm_high_value 40 // Potentiometer WPM fully CW +#define wpm_limit_low 5 +#define wpm_limit_high 60 +#define potentiometer_change_threshold 0.9 // don't change the keyer speed until pot wpm has changed more than this +#define send_buffer_size 150 +#define default_length_letterspace 3 +#define default_length_wordspace 7 +#define default_keying_compensation 0 // number of milliseconds to extend all dits and dahs - for QSK on boatanchors +#define default_first_extension_time 0 // number of milliseconds to extend first sent dit or dah +#define default_pot_full_scale_reading 1023 +#define default_weighting 50 // 50 = weighting factor of 1 (normal) +#define default_ptt_hang_time_wordspace_units 0.0 +#define winkey_c0_wait_time 1 // the number of milliseconds to wait to send 0xc0 byte after send buffer has been sent +#define winkey_command_timeout_ms 5000 +#define winkey_discard_bytes_startup 3 // this is used if OPTION_WINKEY_DISCARD_BYTES_AT_STARTUP is enabled above +#define winkey_xoff_threshold 20 // the number of chars in the buffer when we begin sending XOFFs +#define winkey_xon_threshold 10 // the number of chars in the buffer below which we deactivate XOFF +#define default_memory_repeat_time 3000 // time in milliseconds +#define LCD_COLUMNS 16 +#define LCD_ROWS 2 +#define lcd_i2c_address_mathertel_PCF8574 0x27 // I2C address of display for FEATURE_LCD_MATHERTEL_PCF8574 +#define lcd_i2c_address_fdebrander_lcd 0x27 // I2C address of display for FEATURE_LCD_I2C_FDEBRABANDER +#define lcd_i2c_address_ydv1_lcd 0x27 // I2C address of display for FEATURE_LCD_YDv1 +//#define lcd_i2c_address_ydv1_lcd 0x38 // I2C address of display for FEATURE_LCD_YDv1 +#define lcd_i2c_address_sainsmart_lcd 0x27 // I2C address of display for FEATURE_LCD_SAINSMART_I2C +#define hell_pixel_microseconds 4025 +#define program_memory_limit_consec_spaces 1 +#define serial_leading_zeros 1 // set to 1 to activate leading zeros in serial numbers (i.e. #1 = 001) +#define serial_cut_numbers 0 // set to 1 to activate cut numbers in serial numbers (i.e. #10 = 1T, #19 = 1N) +#define go_to_sleep_inactivity_time 10 // minutes - FEATURE_SLEEP +#define dim_backlight_inactive_time 5 // minutes - FEATURE_LCD_BACKLIGHT_AUTO_DIM +#define default_cmos_super_keyer_iambic_b_timing_percent 33 // use with FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING; should be between 0 to 99 % (0% = true iambic b;100% = iambic a behavior) +#define default_cw_echo_timing_factor 1.75 // "factory default" setting +#define default_autospace_timing_factor 2.0 // "factory default" setting +#define winkey_paddle_echo_buffer_decode_timing_factor 0.25 +#define potentiometer_always_on 0 +#define ptt_interlock_check_every_ms 100 +#define ptt_interlock_active_state HIGH +#define unknown_cw_character '*' +#define cli_paddle_echo_on_at_boot 1 +#define cli_straight_key_echo_on_at_boot 1 +#define tx_key_dit_and_dah_pins_active_state HIGH +#define tx_key_dit_and_dah_pins_inactive_state LOW +#define potentiometer_check_interval_ms 150 +#define potentiometer_reading_threshold 1 +#define default_paddle_interruption_quiet_time_element_lengths 0 +#define default_wordsworth_wordspace 6 +#define default_wordsworth_repetition 1 +#define serial_program_memory_buffer_size 500 +#define eeprom_write_time_ms 30000 + +#ifdef FEATURE_BUTTONS + #define analog_buttons_number_of_buttons 3 // includes the command button (command button + 3 memory buttons = 4) + #define analog_buttons_r1 10 + #define analog_buttons_r2 1 +#endif + + +#if defined(FEATURE_BUTTONS) && !defined(FEATURE_PS2_KEYBOARD) && !defined(FEATURE_USB_KEYBOARD) && !defined(FEATURE_COMMAND_LINE_INTERFACE) && !defined(FEATURE_WINKEY_EMULATION) + #define number_of_memories byte(analog_buttons_number_of_buttons-1) +#else + #define number_of_memories byte(12) +#endif + +#ifdef FEATURE_CAPACITIVE_PADDLE_PINS + #define capacitance_threshold 2 +#endif //FEATURE_CAPACITIVE_PADDLE_PINS + +#ifdef FEATURE_LED_RING + #define led_ring_low_limit 10 + #define led_ring_high_limit 50 +#endif //FEATURE_LED_RING + +#ifdef FEATURE_QLF + #define qlf_dit_max 125 + #define qlf_dit_min 75 + #define qlf_dah_max 200 + #define qlf_dah_min 100 + #define qlf_on_by_default 0 +#endif //FEATURE_QLF + + +#ifdef FEATURE_WINKEY_EMULATION + #ifdef OPTION_WINKEY_UCXLOG_9600_BAUD + #define WINKEY_DEFAULT_BAUD 9600 + #else + #define WINKEY_DEFAULT_BAUD 1200 + #endif //OPTION_WINKEY_UCXLOG_9600_BAUD +// alter these below to map alternate sidetones for Winkey interface protocol emulation +#ifdef OPTION_WINKEY_2_SUPPORT + #define WINKEY_SIDETONE_1 3759 + #define WINKEY_SIDETONE_2 1879 + #define WINKEY_SIDETONE_3 1252 + #define WINKEY_SIDETONE_4 940 + #define WINKEY_SIDETONE_5 752 + #define WINKEY_SIDETONE_6 625 + #define WINKEY_SIDETONE_7 535 + #define WINKEY_SIDETONE_8 469 + #define WINKEY_SIDETONE_9 417 + #define WINKEY_SIDETONE_10 375 +#else //OPTION_WINKEY_2_SUPPORT + #define WINKEY_SIDETONE_1 4000 + #define WINKEY_SIDETONE_2 2000 + #define WINKEY_SIDETONE_3 1333 + #define WINKEY_SIDETONE_4 1000 + #define WINKEY_SIDETONE_5 800 + #define WINKEY_SIDETONE_6 666 + #define WINKEY_SIDETONE_7 571 + #define WINKEY_SIDETONE_8 500 + #define WINKEY_SIDETONE_9 444 + #define WINKEY_SIDETONE_10 400 +#endif //OPTION_WINKEY_2_SUPPORT + +#define WINKEY_1_REPORT_VERSION_NUMBER 10 +#define WINKEY_2_REPORT_VERSION_NUMBER 23 + +// alter these to map to alternate hang time wordspace units +#define WINKEY_HANG_TIME_1_0 1.0 +#define WINKEY_HANG_TIME_1_33 1.33 +#define WINKEY_HANG_TIME_1_66 1.66 +#define WINKEY_HANG_TIME_2_0 2.0 + +#define WINKEY_RETURN_THIS_FOR_ADMIN_GET_CAL 0x16 +#define WINKEY_RETURN_THIS_FOR_ADMIN_PADDLE_A2D 0xEE +#define WINKEY_RETURN_THIS_FOR_ADMIN_SPEED_A2D 0x00 + +#endif //FEATURE_WINKEY_EMULATION + + + +#define PRIMARY_SERIAL_PORT &Serial +#define PRIMARY_SERIAL_PORT_BAUD 115200 // This applies only when the port is in Command Line Interface mode. In Winkey mode it will default to 1200. + +#ifdef FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + #define SECONDARY_SERIAL_PORT &Serial1 + #define SECONDARY_SERIAL_PORT_BAUD 115200 +#endif + + +#define CW_DECODER_SCREEN_COLUMNS 120 // word wrap at this many columns +#define CW_DECODER_SPACE_PRINT_THRESH 4.5 // print space if no tone for this many element lengths +#define CW_DECODER_SPACE_DECODE_THRESH 2.0 // invoke character decode if no tone for this many element lengths +#define CW_DECODER_NOISE_FILTER 20 // ignore elements shorter than this (mS) + +#define STRAIGHT_KEY_ACTIVE_STATE LOW + +#ifdef FEATURE_DYNAMIC_DAH_TO_DIT_RATIO + #define DYNAMIC_DAH_TO_DIT_RATIO_LOWER_LIMIT_WPM 30 + #define DYNAMIC_DAH_TO_DIT_RATIO_LOWER_LIMIT_RATIO 300 // 300 = 3:1 ratio + #define DYNAMIC_DAH_TO_DIT_RATIO_UPPER_LIMIT_WPM 70 + #define DYNAMIC_DAH_TO_DIT_RATIO_UPPER_LIMIT_RATIO 240 // 240 = 2.4:1 ratio +#endif //FEATURE_DYNAMIC_DAH_TO_DIT_RATIO + +#if defined(FEATURE_COMPETITION_COMPRESSION_DETECTION) + #define COMPETITION_COMPRESSION_DETECTION_ARRAY_SIZE 16 + #define COMPETITION_COMPRESSION_DETECTION_TIME_INTERCHAR_LOWER_LIMIT 1 + #define COMPETITION_COMPRESSION_DETECTION_TIME_INTERCHAR_UPPER_LIMIT 6.0 + #define COMPETITION_COMPRESSION_DETECTION_AVERAGE_ALARM_THRESHOLD 3.0 +#endif //FEATURE_COMPETITION_COMPRESSION_DETECTION + +#if defined(FEATURE_SLEEP) + #define KEYER_AWAKE_PIN_AWAKE_STATE HIGH + #define KEYER_AWAKE_PIN_ASLEEP_STATE LOW +#endif + +#if defined(FEATURE_LCD_BACKLIGHT_AUTO_DIM) + #define keyer_power_led_awake_duty 255 // PWM duty cycle. 0 is 0%, 255 is 100% + #define keyer_power_led_asleep_duty 25 // 25 is quite dim. Use 0 for off +#endif + +#if defined(FEATURE_ETHERNET) + // #define FEATURE_ETHERNET_IP {192,168,1,178} // default IP address ("192.168.1.178") + // #define FEATURE_ETHERNET_MAC {0xDE,0xAD,0xBE,0xEF,0xFE,0xED} + #define FEATURE_ETHERNET_IP {192,168,1,179} // default IP address ("192.168.1.179") + #define FEATURE_ETHERNET_MAC {0xDE,0xAD,0xBE,0xEF,0xFE,0xEE} + #define FEATURE_ETHERNET_DNS {8,8,8,8} + + #define FEATURE_ETHERNET_GATEWAY {192,168,1,1} // default gateway + #define FEATURE_ETHERNET_SUBNET_MASK {255,255,255,0} // default subnet mask + #define FEATURE_ETHERNET_WEB_LISTENER_PORT 80 + #define FEATURE_UDP_SEND_BUFFER_SIZE 128 + #define FEATURE_UDP_RECEIVE_BUFFER_SIZE 128 +#endif //FEATURE_ETHERNET + +#define WEB_SERVER_CONTROL_TX_KEY_TIME_LIMIT_SECS 10 + +#define FEATURE_INTERNET_LINK_MAX_LINKS 2 +#define FEATURE_INTERNET_LINK_DEFAULT_RCV_UDP_PORT 8888 +#define FEATURE_INTERNET_LINK_BUFFER_TIME_MS 500 + +#if defined(FEATURE_4x4_KEYPAD)|| defined (FEATURE_3x4_KEYPAD) + #define KEYPAD_ROWS 4 //KeyPad Rows + #if defined(FEATURE_4x4_KEYPAD) + #define KEYPAD_COLS 4 //keypad Columns + #else + #define KEYPAD_COLS 3 + #endif + #define mem1 0 //Memory numbers for Keypad: Actual memory numbers start with "0" + #define mem2 1 + #define mem3 2 + #define mem4 3 + #define mem5 4 + #define mem6 5 + #define mem7 6 + #define mem8 7 + #define mem9 8 + #define mem10 9 + #define mem11 10 + #define mem12 11 +#endif //#if defined(FEATURE_4x4_KEYPAD)|| defined (FEATURE_3x4_KEYPAD) + +#define initial_sidetone_mode 1 // Sidetone mode, 0=OFF, 1=ON, 2=PADDLE_ONLY + +#define sd_card_spi_ss_line 4 + +#if defined(OPTION_DFROBOT_LCD_COMMAND_BUTTONS) + + // For V1.1 board use these values + #define dfrobot_btnRIGHT_analog 50 + #define dfrobot_btnUP_analog 250 + #define dfrobot_btnDOWN_analog 450 + #define dfrobot_btnLEFT_analog 650 + #define dfrobot_btnSELECT_analog 850 + + // For V1.0 board use these values + // #define dfrobot_btnRIGHT_analog 50 + // #define dfrobot_btnUP_analog 195 + // #define dfrobot_btnDOWN_analog 380 + // #define dfrobot_btnLEFT_analog 555 + // #define dfrobot_btnSELECT_analog 790 + + // button to memory mappings (0 = command button, 1 = memory 1, 2 = memory 2, etc.) + #define dfrobot_btnRIGHT 2 + #define dfrobot_btnUP 1 + #define dfrobot_btnDOWN 3 + #define dfrobot_btnLEFT 4 + #define dfrobot_btnSELECT 0 + #define dfrobot_btnNONE 255 // do not change + +#endif + +#define sequencer_pins_active_state HIGH +#define sequencer_pins_inactive_state LOW +#define ptt_line_active_state HIGH +#define ptt_line_inactive_state LOW +#define tx_key_line_active_state HIGH +#define tx_key_line_inactive_state LOW +#define ptt_input_pin_active_state LOW +#define ptt_input_pin_inactive_state HIGH +#define tx_inhibit_pin_active_state LOW +#define tx_inhibit_pin_inactive_state HIGH +#define tx_pause_pin_active_state LOW +#define tx_pause_pin_inactive_state HIGH +#define sidetone_line_active_state HIGH +#define sidetone_line_inactive_state LOW + +#if defined(ARDUINO_MAPLE_MINI) + #define button_value_factor 4095 //sp5iou contributed +#else + #define button_value_factor 1023 +#endif + +#define farnsworth_timing_calibration 1.15 + +#define sidetone_volume_low_limit 10 +#define sidetone_volume_high_limit 500 + +#define custom_startup_field "your custom text here" // an example could be callsign and name, eg. "AB1XYZ Bob", (or "Worlds best operator" which requires a 20 column display), string length shouldo be no more than the number of columns on the display + +#define command_mode_acknowledgement_character 'E' + +#if defined(FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT) + #define command_a_iambic_a "A" + #define command_b_iambic_b "B" + #define command_c_single_paddle "SINGLE" + #define command_d_ultimatic "ULT" + #define command_h_weight_dit_dah_ratio_default "R" + #define command_i_tx_on "TX ON" + #define command_i_tx_off "TX OFF" + #define command_k_dit_dah_buffers_on "ON" + #define command_k_dit_dah_buffers_off "OFF" + #define command_n_paddle_reverse "REV" + #define command_n_paddle_normal "NORM" + #define command_o_sidetone_off "ST OFF" + #define command_o_sidetone_paddle_only "ST PD ONLY" + #define command_o_sidetone_on "ST ON" + #define command_t_tune_mode "TUNE" + #define command_v_potentiometer_on "POT ON" + #define command_v_potentiometer_off "POT OFF" + + #define command_error "ERR" + +#endif //FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT diff --git a/k3ng_keyer/keyer_settings_test.h b/k3ng_keyer/keyer_settings_test.h new file mode 100755 index 0000000..1ba174b --- /dev/null +++ b/k3ng_keyer/keyer_settings_test.h @@ -0,0 +1,652 @@ + +// ######## ######## ###### ######## +// ## ## ## ## ## +// ## ## ## ## +// ## ###### ###### ## +// ## ## ## ## +// ## ## ## ## ## +// ## ######## ###### ## + +#define TEST_SETTINGS_1 +// #define TEST_SETTINGS_2 + +#if defined(TEST_SETTINGS_1) + +#define initial_speed_wpm 26 // "factory default" keyer speed setting +#define initial_command_mode_speed_wpm 20 // "factory default" command mode speed setting +#define initial_sidetone_freq 600 // "factory default" sidetone frequency setting +#define sidetone_hz_limit_low 299 +#define sidetone_hz_limit_high 2001 +#define hz_high_beep 1500 // frequency in hertz of high beep +#define hz_low_beep 400 // frequency in hertz of low beep +#define initial_dah_to_dit_ratio 300 // 300 = 3 / normal 3:1 ratio +#define initial_ptt_lead_time_tx1 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx1 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx2 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx2 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx3 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx3 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx4 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx4 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx5 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx5 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx6 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx6 10 // PTT tail time in mS +#define initial_qrss_dit_length 1 // QRSS dit length in seconds +#define initial_pot_wpm_low_value 13 // Potentiometer WPM fully CCW +#define initial_pot_wpm_high_value 35 // Potentiometer WPM fully CW +#define wpm_limit_low 5 +#define wpm_limit_high 60 +#define potentiometer_change_threshold 0.9 // don't change the keyer speed until pot wpm has changed more than this +#define send_buffer_size 150 +#define default_length_letterspace 3 +#define default_length_wordspace 7 +#define default_keying_compensation 0 // number of milliseconds to extend all dits and dahs - for QSK on boatanchors +#define default_first_extension_time 0 // number of milliseconds to extend first sent dit or dah +#define default_pot_full_scale_reading 1023 +#define default_weighting 50 // 50 = weighting factor of 1 (normal) +#define default_ptt_hang_time_wordspace_units 0.0 +#define winkey_c0_wait_time 1 // the number of milliseconds to wait to send 0xc0 byte after send buffer has been sent +#define winkey_command_timeout_ms 5000 +#define winkey_discard_bytes_startup 3 // this is used if OPTION_WINKEY_DISCARD_BYTES_AT_STARTUP is enabled above +#define winkey_xoff_threshold 20 // the number of chars in the buffer when we begin sending XOFFs +#define winkey_xon_threshold 10 // the number of chars in the buffer below which we deactivate XOFF +#define default_memory_repeat_time 3000 // time in milliseconds +#define LCD_COLUMNS 16 +#define LCD_ROWS 2 +#define lcd_i2c_address_mathertel_PCF8574 0x27 // I2C address of display for FEATURE_LCD_MATHERTEL_PCF8574 +#define lcd_i2c_address_fdebrander_lcd 0x27 // I2C address of display for FEATURE_LCD_I2C_FDEBRABANDER +#define lcd_i2c_address_ydv1_lcd 0x27 // I2C address of display for FEATURE_LCD_YDv1 +//#define lcd_i2c_address_ydv1_lcd 0x38 // I2C address of display for FEATURE_LCD_YDv1 +#define lcd_i2c_address_sainsmart_lcd 0x27 // I2C address of display for FEATURE_LCD_SAINSMART_I2C +#define hell_pixel_microseconds 4025 +#define program_memory_limit_consec_spaces 1 +#define serial_leading_zeros 1 // set to 1 to activate leading zeros in serial numbers (i.e. #1 = 001) +#define serial_cut_numbers 0 // set to 1 to activate cut numbers in serial numbers (i.e. #10 = 1T, #19 = 1N) +#define go_to_sleep_inactivity_time 10 // minutes - FEATURE_SLEEP +#define dim_backlight_inactive_time 5 // minutes - FEATURE_LCD_BACKLIGHT_AUTO_DIM +#define default_cmos_super_keyer_iambic_b_timing_percent 33 // use with FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING; should be between 0 to 99 % (0% = true iambic b;100% = iambic a behavior) +#define default_cw_echo_timing_factor 1.75 // "factory default" setting +#define default_autospace_timing_factor 2.0 // "factory default" setting +#define winkey_paddle_echo_buffer_decode_timing_factor 0.25 +#define potentiometer_always_on 0 +#define ptt_interlock_check_every_ms 100 +#define ptt_interlock_active_state HIGH +#define unknown_cw_character '*' +#define cli_paddle_echo_on_at_boot 1 +#define cli_straight_key_echo_on_at_boot 1 +#define tx_key_dit_and_dah_pins_active_state LOW +#define tx_key_dit_and_dah_pins_inactive_state HIGH +#define potentiometer_check_interval_ms 150 +#define potentiometer_reading_threshold 1 +#define default_paddle_interruption_quiet_time_element_lengths 0 +#define default_wordsworth_wordspace 6 +#define default_wordsworth_repetition 1 +#define serial_program_memory_buffer_size 500 +#define eeprom_write_time_ms 30000 + +#ifdef FEATURE_BUTTONS + #define analog_buttons_number_of_buttons 4 + #define analog_buttons_r1 10 + #define analog_buttons_r2 1 +#endif + + +#if defined(FEATURE_BUTTONS) && !defined(FEATURE_PS2_KEYBOARD) && !defined(FEATURE_USB_KEYBOARD) && !defined(FEATURE_COMMAND_LINE_INTERFACE) && !defined(FEATURE_WINKEY_EMULATION) + #define number_of_memories byte(analog_buttons_number_of_buttons-1) +#else + #define number_of_memories byte(12) +#endif + +#ifdef FEATURE_CAPACITIVE_PADDLE_PINS + #define capacitance_threshold 2 +#endif //FEATURE_CAPACITIVE_PADDLE_PINS + +#ifdef FEATURE_LED_RING + #define led_ring_low_limit 10 + #define led_ring_high_limit 50 +#endif //FEATURE_LED_RING + +#ifdef FEATURE_QLF + #define qlf_dit_max 125 + #define qlf_dit_min 75 + #define qlf_dah_max 200 + #define qlf_dah_min 100 + #define qlf_on_by_default 0 +#endif //FEATURE_QLF + + +#ifdef FEATURE_WINKEY_EMULATION + #ifdef OPTION_WINKEY_UCXLOG_9600_BAUD + #define WINKEY_DEFAULT_BAUD 9600 + #else + #define WINKEY_DEFAULT_BAUD 1200 + #endif //OPTION_WINKEY_UCXLOG_9600_BAUD +// alter these below to map alternate sidetones for Winkey interface protocol emulation +#ifdef OPTION_WINKEY_2_SUPPORT + #define WINKEY_SIDETONE_1 3759 + #define WINKEY_SIDETONE_2 1879 + #define WINKEY_SIDETONE_3 1252 + #define WINKEY_SIDETONE_4 940 + #define WINKEY_SIDETONE_5 752 + #define WINKEY_SIDETONE_6 625 + #define WINKEY_SIDETONE_7 535 + #define WINKEY_SIDETONE_8 469 + #define WINKEY_SIDETONE_9 417 + #define WINKEY_SIDETONE_10 375 +#else //OPTION_WINKEY_2_SUPPORT + #define WINKEY_SIDETONE_1 4000 + #define WINKEY_SIDETONE_2 2000 + #define WINKEY_SIDETONE_3 1333 + #define WINKEY_SIDETONE_4 1000 + #define WINKEY_SIDETONE_5 800 + #define WINKEY_SIDETONE_6 666 + #define WINKEY_SIDETONE_7 571 + #define WINKEY_SIDETONE_8 500 + #define WINKEY_SIDETONE_9 444 + #define WINKEY_SIDETONE_10 400 +#endif //OPTION_WINKEY_2_SUPPORT + +#define WINKEY_1_REPORT_VERSION_NUMBER 10 +#define WINKEY_2_REPORT_VERSION_NUMBER 23 + +// alter these to map to alternate hang time wordspace units +#define WINKEY_HANG_TIME_1_0 1.0 +#define WINKEY_HANG_TIME_1_33 1.33 +#define WINKEY_HANG_TIME_1_66 1.66 +#define WINKEY_HANG_TIME_2_0 2.0 + +#define WINKEY_RETURN_THIS_FOR_ADMIN_GET_CAL 0x16 +#define WINKEY_RETURN_THIS_FOR_ADMIN_PADDLE_A2D 0xEE +#define WINKEY_RETURN_THIS_FOR_ADMIN_SPEED_A2D 0x00 + +#endif //FEATURE_WINKEY_EMULATION + + +#define PRIMARY_SERIAL_PORT &Serial +#define PRIMARY_SERIAL_PORT_BAUD 115200 // This applies only when the port is in Command Line Interface mode. In Winkey mode it will default to 1200. + +#ifdef FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + #define SECONDARY_SERIAL_PORT &Serial1 + #define SECONDARY_SERIAL_PORT_BAUD 115200 +#endif + + +#define CW_DECODER_SCREEN_COLUMNS 120 // word wrap at this many columns +#define CW_DECODER_SPACE_PRINT_THRESH 4.5 // print space if no tone for this many element lengths +#define CW_DECODER_SPACE_DECODE_THRESH 2.0 // invoke character decode if no tone for this many element lengths +#define CW_DECODER_NOISE_FILTER 20 // ignore elements shorter than this (mS) + +#define STRAIGHT_KEY_ACTIVE_STATE LOW + +#ifdef FEATURE_DYNAMIC_DAH_TO_DIT_RATIO + #define DYNAMIC_DAH_TO_DIT_RATIO_LOWER_LIMIT_WPM 30 + #define DYNAMIC_DAH_TO_DIT_RATIO_LOWER_LIMIT_RATIO 300 // 300 = 3:1 ratio + #define DYNAMIC_DAH_TO_DIT_RATIO_UPPER_LIMIT_WPM 70 + #define DYNAMIC_DAH_TO_DIT_RATIO_UPPER_LIMIT_RATIO 240 // 240 = 2.4:1 ratio +#endif //FEATURE_DYNAMIC_DAH_TO_DIT_RATIO + +#if defined(FEATURE_COMPETITION_COMPRESSION_DETECTION) + #define COMPETITION_COMPRESSION_DETECTION_ARRAY_SIZE 16 + #define COMPETITION_COMPRESSION_DETECTION_TIME_INTERCHAR_LOWER_LIMIT 1 + #define COMPETITION_COMPRESSION_DETECTION_TIME_INTERCHAR_UPPER_LIMIT 6.0 + #define COMPETITION_COMPRESSION_DETECTION_AVERAGE_ALARM_THRESHOLD 3.0 +#endif //FEATURE_COMPETITION_COMPRESSION_DETECTION + +#if defined(FEATURE_SLEEP) + #define KEYER_AWAKE_PIN_AWAKE_STATE HIGH + #define KEYER_AWAKE_PIN_ASLEEP_STATE LOW +#endif + +#if defined(FEATURE_LCD_BACKLIGHT_AUTO_DIM) + #define keyer_power_led_awake_duty 255 // PWM duty cycle. 0 is 0%, 255 is 100% + #define keyer_power_led_asleep_duty 25 // 25 is quite dim. Use 0 for off +#endif + +#if defined(FEATURE_ETHERNET) + #define FEATURE_ETHERNET_IP {192,168,1,178} // default IP address ("192.168.1.178") + #define FEATURE_ETHERNET_MAC {0xDE,0xAD,0xBE,0xEF,0xFE,0xED} + #define FEATURE_ETHERNET_DNS {8,8,8,8} +// #define FEATURE_ETHERNET_IP {192,168,1,179} // default IP address ("192.168.1.179") +// #define FEATURE_ETHERNET_MAC {0xDE,0xAD,0xBE,0xEF,0xFE,0xEE} + + #define FEATURE_ETHERNET_GATEWAY {192,168,1,1} // default gateway + #define FEATURE_ETHERNET_SUBNET_MASK {255,255,255,0} // default subnet mask + #define FEATURE_ETHERNET_WEB_LISTENER_PORT 80 + #define FEATURE_UDP_SEND_BUFFER_SIZE 128 + #define FEATURE_UDP_RECEIVE_BUFFER_SIZE 128 +#endif //FEATURE_ETHERNET + +#define FEATURE_INTERNET_LINK_MAX_LINKS 2 +#define FEATURE_INTERNET_LINK_DEFAULT_RCV_UDP_PORT 8888 +#define FEATURE_INTERNET_LINK_BUFFER_TIME_MS 500 // increase this time if you have greater than 500 ms latency on your link +#define FEATURE_INTERNET_LINK_SVC_DURING_LOOP_TIME_MS 20 +#define FEATURE_INTERNET_LINK_KEY_DOWN_TIMEOUT_SECS 8 + +#define WEB_SERVER_CONTROL_TX_KEY_TIME_LIMIT_SECS 10 + +#if defined(FEATURE_4x4_KEYPAD)|| defined (FEATURE_3x4_KEYPAD) + #define KEYPAD_ROWS 4 //KeyPad Rows + #if defined(FEATURE_4x4_KEYPAD) + #define KEYPAD_COLS 4 //keypad Columns + #else + #define KEYPAD_COLS 3 + #endif + #define mem1 0 //Memory numbers for Keypad: Actual memory numbers start with "0" + #define mem2 1 + #define mem3 2 + #define mem4 3 + #define mem5 4 + #define mem6 5 + #define mem7 6 + #define mem8 7 + #define mem9 8 + #define mem10 9 + #define mem11 10 + #define mem12 11 +#endif //#if defined(FEATURE_4x4_KEYPAD)|| defined (FEATURE_3x4_KEYPAD) + +#define initial_sidetone_mode 1 // Sidetone mode, 0=OFF, 1=ON, 2=PADDLE_ONLY + +#define sd_card_spi_ss_line 4 + + +#if defined(OPTION_DFROBOT_LCD_COMMAND_BUTTONS) + + // For V1.1 board use these values + #define dfrobot_btnRIGHT_analog 50 + #define dfrobot_btnUP_analog 250 + #define dfrobot_btnDOWN_analog 450 + #define dfrobot_btnLEFT_analog 650 + #define dfrobot_btnSELECT_analog 850 + + // For V1.0 board use these values + // #define dfrobot_btnRIGHT_analog 50 + // #define dfrobot_btnUP_analog 195 + // #define dfrobot_btnDOWN_analog 380 + // #define dfrobot_btnLEFT_analog 555 + // #define dfrobot_btnSELECT_analog 790 + + // button to memory mappings (0 = command button, 1 = memory 1, 2 = memory 2, etc.) + #define dfrobot_btnRIGHT 2 + #define dfrobot_btnUP 1 + #define dfrobot_btnDOWN 3 + #define dfrobot_btnLEFT 4 + #define dfrobot_btnSELECT 0 + #define dfrobot_btnNONE 255 // do not change + +#endif + + +#define sequencer_pins_active_state HIGH +#define sequencer_pins_inactive_state LOW +#define ptt_line_active_state HIGH +#define ptt_line_inactive_state LOW +#define tx_key_line_active_state HIGH +#define tx_key_line_inactive_state LOW +#define ptt_input_pin_active_state LOW +#define ptt_input_pin_inactive_state HIGH +#define tx_inhibit_pin_active_state LOW +#define tx_inhibit_pin_inactive_state HIGH +#define tx_pause_pin_active_state LOW +#define tx_pause_pin_inactive_state HIGH +#define sidetone_line_active_state HIGH +#define sidetone_line_inactive_state LOW + +#if defined(ARDUINO_MAPLE_MINI) + #define button_value_factor 4095 +#else + #define button_value_factor 1023 +#endif + +#define farnsworth_timing_calibration 1.15 + +#define sidetone_volume_low_limit 10 +#define sidetone_volume_high_limit 500 + +#define command_mode_acknowledgement_character 'C' + +#if defined(FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT) + #define command_a_iambic_a "A" + #define command_b_iambic_b "B" + #define command_c_single_paddle "SINGLE" + #define command_d_ultimatic "ULT" + #define command_h_weight_dit_dah_ratio_default "R" + #define command_i_tx_on "TX ON" + #define command_i_tx_off "TX OFF" + #define command_k_dit_dah_buffers_on "ON" + #define command_k_dit_dah_buffers_off "OFF" + #define command_n_paddle_reverse "REV" + #define command_n_paddle_normal "NORM" + #define command_o_sidetone_off "ST OFF" + #define command_o_sidetone_paddle_only "ST PD ONLY" + #define command_o_sidetone_on "ST ON" + #define command_t_tune_mode "TUNE" + #define command_v_potentiometer_on "POT ON" + #define command_v_potentiometer_off "POT OFF" + + #define command_error "ERR" + +#endif //FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT + +#endif //TEST_SETTINGS_1 + +//********************************************************************************************************************* +//********************************************************************************************************************* +//********************************************************************************************************************* +//********************************************************************************************************************* +//********************************************************************************************************************* + +#if defined(TEST_SETTINGS_2) + + +#define initial_speed_wpm 17 // "factory default" keyer speed setting +#define initial_sidetone_freq 588 // "factory default" sidetone frequency setting +#define sidetone_hz_limit_low 299 +#define sidetone_hz_limit_high 2001 +#define hz_high_beep 1500 // frequency in hertz of high beep +#define hz_low_beep 400 // frequency in hertz of low beep +#define initial_dah_to_dit_ratio 300 // 300 = 3 / normal 3:1 ratio +#define initial_ptt_lead_time_tx1 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx1 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx2 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx2 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx3 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx3 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx4 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx4 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx5 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx5 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx6 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx6 10 // PTT tail time in mS +#define initial_qrss_dit_length 1 // QRSS dit length in seconds +#define initial_pot_wpm_low_value 12 // Potentiometer WPM fully CCW +#define initial_pot_wpm_high_value 30 // Potentiometer WPM fully CW +#define wpm_limit_low 5 +#define wpm_limit_high 60 +#define potentiometer_change_threshold 0.9 // don't change the keyer speed until pot wpm has changed more than this +#define potentiometer_reading_threshold 1 +#define send_buffer_size 150 +#define default_length_letterspace 3 +#define default_length_wordspace 7 +#define default_keying_compensation 0 // number of milliseconds to extend all dits and dahs - for QSK on boatanchors +#define default_first_extension_time 0 // number of milliseconds to extend first sent dit or dah +#define default_pot_full_scale_reading 1023 +#define default_weighting 50 // 50 = weighting factor of 1 (normal) +#define default_ptt_hang_time_wordspace_units 0.0 +#define winkey_c0_wait_time 1 // the number of milliseconds to wait to send 0xc0 byte after send buffer has been sent +#define winkey_command_timeout_ms 5000 +#define winkey_discard_bytes_startup 3 // this is used if OPTION_WINKEY_DISCARD_BYTES_AT_STARTUP is enabled above +#define winkey_xoff_threshold 20 // the number of chars in the buffer when we begin sending XOFFs +#define winkey_xon_threshold 10 // the number of chars in the buffer below which we deactivate XOFF +#define default_memory_repeat_time 3000 // time in milliseconds +#define LCD_COLUMNS 16 +#define LCD_ROWS 2 +#define lcd_i2c_address_mathertel_PCF8574 0x27 // I2C address of display +#define hell_pixel_microseconds 4025 +#define program_memory_limit_consec_spaces 1 +#define serial_leading_zeros 1 // set to 1 to activate leading zeros in serial numbers (i.e. #1 = 001) +#define serial_cut_numbers 0 // set to 1 to activate cut numbers in serial numbers (i.e. #10 = 1T, #19 = 1N) +#define go_to_sleep_inactivity_time 10 // minutes - FEATURE_SLEEP +#define default_cmos_super_keyer_iambic_b_timing_percent 33 // use with FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING; should be between 0 to 99 % (0% = true iambic b;100% = iambic a behavior) +#define cw_echo_timing_factor 0.25 +#define winkey_paddle_echo_buffer_decode_time_factor 1600.0 +#define potentiometer_always_on 0 +#define ptt_interlock_check_every_ms 100 +#define ptt_interlock_active_state HIGH +#define unknown_cw_character '*' +#define cli_paddle_echo_on_at_boot 1 +#define cli_straight_key_echo_on_at_boot 1 +#define tx_key_dit_and_dah_pins_active_state HIGH +#define tx_key_dit_and_dah_pins_inactive_state LOW +#define potentiometer_check_interval_ms 150 +#define default_paddle_interruption_quiet_time_element_lengths 0 +#define default_wordsworth_wordspace 6 +#define default_wordsworth_repetition 1 +#define serial_program_memory_buffer_size 500 +#define eeprom_write_time_ms 30000 + +#ifdef FEATURE_BUTTONS + #define analog_buttons_number_of_buttons 4 // includes the command button (command button + 3 memory buttons = 4) + #define analog_buttons_r1 10 + #define analog_buttons_r2 1.2 +#endif + + +#if defined(FEATURE_BUTTONS) && !defined(FEATURE_PS2_KEYBOARD) && !defined(FEATURE_USB_KEYBOARD) && !defined(FEATURE_COMMAND_LINE_INTERFACE) && !defined(FEATURE_WINKEY_EMULATION) + #define number_of_memories byte(analog_buttons_number_of_buttons-1) +#else + #define number_of_memories byte(12) +#endif + +#ifdef FEATURE_CAPACITIVE_PADDLE_PINS + #define capacitance_threshold 2 +#endif //FEATURE_CAPACITIVE_PADDLE_PINS + +#ifdef FEATURE_LED_RING + #define led_ring_low_limit 10 + #define led_ring_high_limit 50 +#endif //FEATURE_LED_RING + +#ifdef FEATURE_QLF + #define qlf_dit_max 125 + #define qlf_dit_min 75 + #define qlf_dah_max 200 + #define qlf_dah_min 100 + #define qlf_on_by_default 0 +#endif //FEATURE_QLF + + +#ifdef FEATURE_WINKEY_EMULATION + #ifdef OPTION_WINKEY_UCXLOG_9600_BAUD + #define WINKEY_DEFAULT_BAUD 9600 + #else + #define WINKEY_DEFAULT_BAUD 1200 + #endif //OPTION_WINKEY_UCXLOG_9600_BAUD +// alter these below to map alternate sidetones for Winkey interface protocol emulation +#ifdef OPTION_WINKEY_2_SUPPORT + #define WINKEY_SIDETONE_1 3759 + #define WINKEY_SIDETONE_2 1879 + #define WINKEY_SIDETONE_3 1252 + #define WINKEY_SIDETONE_4 940 + #define WINKEY_SIDETONE_5 752 + #define WINKEY_SIDETONE_6 625 + #define WINKEY_SIDETONE_7 535 + #define WINKEY_SIDETONE_8 469 + #define WINKEY_SIDETONE_9 417 + #define WINKEY_SIDETONE_10 375 +#else //OPTION_WINKEY_2_SUPPORT + #define WINKEY_SIDETONE_1 4000 + #define WINKEY_SIDETONE_2 2000 + #define WINKEY_SIDETONE_3 1333 + #define WINKEY_SIDETONE_4 1000 + #define WINKEY_SIDETONE_5 800 + #define WINKEY_SIDETONE_6 666 + #define WINKEY_SIDETONE_7 571 + #define WINKEY_SIDETONE_8 500 + #define WINKEY_SIDETONE_9 444 + #define WINKEY_SIDETONE_10 400 +#endif //OPTION_WINKEY_2_SUPPORT + +#define WINKEY_1_REPORT_VERSION_NUMBER 10 +#define WINKEY_2_REPORT_VERSION_NUMBER 23 + +// alter these to map to alternate hang time wordspace units +#define WINKEY_HANG_TIME_1_0 1.0 +#define WINKEY_HANG_TIME_1_33 1.33 +#define WINKEY_HANG_TIME_1_66 1.66 +#define WINKEY_HANG_TIME_2_0 2.0 + +#define WINKEY_RETURN_THIS_FOR_ADMIN_GET_CAL 0x16 +#define WINKEY_RETURN_THIS_FOR_ADMIN_PADDLE_A2D 0xEE +#define WINKEY_RETURN_THIS_FOR_ADMIN_SPEED_A2D 0x00 + +#endif //FEATURE_WINKEY_EMULATION + + + +#define PRIMARY_SERIAL_PORT &Serial +#define PRIMARY_SERIAL_PORT_BAUD 115200 // This applies only when the port is in Command Line Interface mode. In Winkey mode it will default to 1200. + +#ifdef FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + #define SECONDARY_SERIAL_PORT &Serial1 + #define SECONDARY_SERIAL_PORT_BAUD 115200 +#endif + + +#define CW_DECODER_SCREEN_COLUMNS 120 // word wrap at this many columns +#define CW_DECODER_SPACE_PRINT_THRESH 4.5 // print space if no tone for this many element lengths +#define CW_DECODER_SPACE_DECODE_THRESH 2.0 // invoke character decode if no tone for this many element lengths +#define CW_DECODER_NOISE_FILTER 20 // ignore elements shorter than this (mS) + +#define STRAIGHT_KEY_ACTIVE_STATE LOW + +#ifdef FEATURE_DYNAMIC_DAH_TO_DIT_RATIO + #define DYNAMIC_DAH_TO_DIT_RATIO_LOWER_LIMIT_WPM 30 + #define DYNAMIC_DAH_TO_DIT_RATIO_LOWER_LIMIT_RATIO 300 // 300 = 3:1 ratio + #define DYNAMIC_DAH_TO_DIT_RATIO_UPPER_LIMIT_WPM 70 + #define DYNAMIC_DAH_TO_DIT_RATIO_UPPER_LIMIT_RATIO 240 // 240 = 2.4:1 ratio +#endif //FEATURE_DYNAMIC_DAH_TO_DIT_RATIO + +#if defined(FEATURE_COMPETITION_COMPRESSION_DETECTION) + #define COMPETITION_COMPRESSION_DETECTION_ARRAY_SIZE 16 + #define COMPETITION_COMPRESSION_DETECTION_TIME_INTERCHAR_LOWER_LIMIT 1 + #define COMPETITION_COMPRESSION_DETECTION_TIME_INTERCHAR_UPPER_LIMIT 6.0 + #define COMPETITION_COMPRESSION_DETECTION_AVERAGE_ALARM_THRESHOLD 3.0 +#endif //FEATURE_COMPETITION_COMPRESSION_DETECTION + +#if defined(FEATURE_SLEEP) + #define KEYER_AWAKE_PIN_AWAKE_STATE HIGH + #define KEYER_AWAKE_PIN_ASLEEP_STATE LOW +#endif + +#if defined(FEATURE_ETHERNET) + // #define FEATURE_ETHERNET_IP {192,168,1,178} // default IP address ("192.168.1.178") + // #define FEATURE_ETHERNET_MAC {0xDE,0xAD,0xBE,0xEF,0xFE,0xED} + #define FEATURE_ETHERNET_IP {192,168,1,179} // default IP address ("192.168.1.179") + #define FEATURE_ETHERNET_MAC {0xDE,0xAD,0xBE,0xEF,0xFE,0xEE} + + #define FEATURE_ETHERNET_GATEWAY {192,168,1,1} // default gateway + #define FEATURE_ETHERNET_SUBNET_MASK {255,255,255,0} // default subnet mask + #define FEATURE_ETHERNET_WEB_LISTENER_PORT 80 + #define FEATURE_UDP_SEND_BUFFER_SIZE 128 + #define FEATURE_UDP_RECEIVE_BUFFER_SIZE 128 +#endif //FEATURE_ETHERNET + +#define WEB_SERVER_CONTROL_TX_KEY_TIME_LIMIT_SECS 10 + +#define FEATURE_INTERNET_LINK_MAX_LINKS 2 +#define FEATURE_INTERNET_LINK_DEFAULT_RCV_UDP_PORT 8888 +#define FEATURE_INTERNET_LINK_BUFFER_TIME_MS 500 + +#if defined(FEATURE_4x4_KEYPAD)|| defined (FEATURE_3x4_KEYPAD) + #define KEYPAD_ROWS 4 //KeyPad Rows + #if defined(FEATURE_4x4_KEYPAD) + #define KEYPAD_COLS 4 //keypad Columns + #else + #define KEYPAD_COLS 3 + #endif + #define mem1 0 //Memory numbers for Keypad: Actual memory numbers start with "0" + #define mem2 1 + #define mem3 2 + #define mem4 3 + #define mem5 4 + #define mem6 5 + #define mem7 6 + #define mem8 7 + #define mem9 8 + #define mem10 9 + #define mem11 10 + #define mem12 11 +#endif //#if defined(FEATURE_4x4_KEYPAD)|| defined (FEATURE_3x4_KEYPAD) + +#define initial_sidetone_mode 1 // Sidetone mode, 0=OFF, 1=ON, 2=PADDLE_ONLY +#define initial_command_mode_speed_wpm 20 + +#if defined(OPTION_DFROBOT_LCD_COMMAND_BUTTONS) + + // For V1.1 board use these values + #define dfrobot_btnRIGHT_analog 50 + #define dfrobot_btnUP_analog 250 + #define dfrobot_btnDOWN_analog 450 + #define dfrobot_btnLEFT_analog 650 + #define dfrobot_btnSELECT_analog 850 + + // For V1.0 board use these values + // #define dfrobot_btnRIGHT_analog 50 + // #define dfrobot_btnUP_analog 195 + // #define dfrobot_btnDOWN_analog 380 + // #define dfrobot_btnLEFT_analog 555 + // #define dfrobot_btnSELECT_analog 790 + + // button to memory mappings (0 = command button, 1 = memory 1, 2 = memory 2, etc.) + #define dfrobot_btnRIGHT 2 + #define dfrobot_btnUP 1 + #define dfrobot_btnDOWN 3 + #define dfrobot_btnLEFT 4 + #define dfrobot_btnSELECT 0 + #define dfrobot_btnNONE 255 // do not change + +#endif + + +#define sequencer_pins_active_state HIGH +#define sequencer_pins_inactive_state LOW +#define ptt_line_active_state HIGH +#define ptt_line_inactive_state LOW +#define tx_key_line_active_state HIGH +#define tx_key_line_inactive_state LOW +#define ptt_input_pin_active_state LOW +#define ptt_input_pin_inactive_state HIGH +#define tx_inhibit_pin_active_state LOW +#define tx_inhibit_pin_inactive_state HIGH +#define tx_pause_pin_active_state LOW +#define tx_pause_pin_inactive_state HIGH + + +#if defined(ARDUINO_MAPLE_MINI) + #define button_value_factor 4095 //sp5iou contributed +#else + #define button_value_factor 1023 +#endif + +#define farnsworth_timing_calibration 1.15 + +#define sidetone_volume_low_limit 10 +#define sidetone_volume_high_limit 500 + +#define custom_startup_field "your custom text here" // an example could be callsign and name, eg. "AB1XYZ Bob", (or "Worlds best operator" which requires a 20 column display), string length shouldo be no more than the number of columns on the display + +#define command_mode_acknowledgement_character 'R' + +#if defined(FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT) + #define command_a_iambic_a "A" + #define command_b_iambic_b "B" + #define command_c_single_paddle "SINGLE" + #define command_d_ultimatic "ULT" + #define command_h_weight_dit_dah_ratio_default "R" + #define command_i_tx_on "TX ON" + #define command_i_tx_off "TX OFF" + #define command_k_dit_dah_buffers_on "ON" + #define command_k_dit_dah_buffers_off "OFF" + #define command_n_paddle_reverse "REV" + #define command_n_paddle_normal "NORM" + #define command_o_sidetone_off "ST OFF" + #define command_o_sidetone_paddle_only "ST PD ONLY" + #define command_o_sidetone_on "ST ON" + #define command_v_potentiometer_on "POT ON" + #define command_v_potentiometer_off "POT OFF" + + #define command_error "ERR" + +#endif //FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT + +#endif //TEST_SETTINGS_2 + +// ######## ######## ###### ######## +// ## ## ## ## ## +// ## ## ## ## +// ## ###### ###### ## +// ## ## ## ## +// ## ## ## ## ## +// ## ######## ###### ## diff --git a/k3ng_keyer/keyer_settings_test_everything.h b/k3ng_keyer/keyer_settings_test_everything.h new file mode 100755 index 0000000..f8d3410 --- /dev/null +++ b/k3ng_keyer/keyer_settings_test_everything.h @@ -0,0 +1,361 @@ + +// ######## ######## ###### ######## +// ## ## ## ## ## +// ## ## ## ## EVERYTHING +// ## ###### ###### ## +// ## ## ## ## +// ## ## ## ## ## +// ## ######## ###### ## + + + +#define initial_speed_wpm 26 // "factory default" keyer speed setting +#define initial_command_mode_speed_wpm 20 // "factory default" command mode speed setting +#define initial_sidetone_freq 600 // "factory default" sidetone frequency setting +#define sidetone_hz_limit_low 299 +#define sidetone_hz_limit_high 2001 +#define hz_high_beep 1500 // frequency in hertz of high beep +#define hz_low_beep 400 // frequency in hertz of low beep +#define initial_dah_to_dit_ratio 300 // 300 = 3 / normal 3:1 ratio +#define initial_ptt_lead_time_tx1 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx1 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx2 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx2 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx3 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx3 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx4 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx4 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx5 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx5 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx6 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx6 10 // PTT tail time in mS +#define initial_qrss_dit_length 1 // QRSS dit length in seconds +#define initial_pot_wpm_low_value 13 // Potentiometer WPM fully CCW +#define initial_pot_wpm_high_value 35 // Potentiometer WPM fully CW +#define wpm_limit_low 5 +#define wpm_limit_high 60 +#define potentiometer_change_threshold 0.9 // don't change the keyer speed until pot wpm has changed more than this +#define send_buffer_size 150 +#define default_length_letterspace 3 +#define default_length_wordspace 7 +#define default_keying_compensation 0 // number of milliseconds to extend all dits and dahs - for QSK on boatanchors +#define default_first_extension_time 0 // number of milliseconds to extend first sent dit or dah +#define default_pot_full_scale_reading 1023 +#define default_weighting 50 // 50 = weighting factor of 1 (normal) +#define default_ptt_hang_time_wordspace_units 0.0 +#define winkey_c0_wait_time 1 // the number of milliseconds to wait to send 0xc0 byte after send buffer has been sent +#define winkey_command_timeout_ms 5000 +#define winkey_discard_bytes_startup 3 // this is used if OPTION_WINKEY_DISCARD_BYTES_AT_STARTUP is enabled above +#define winkey_xoff_threshold 20 // the number of chars in the buffer when we begin sending XOFFs +#define winkey_xon_threshold 10 // the number of chars in the buffer below which we deactivate XOFF +#define default_memory_repeat_time 3000 // time in milliseconds +#define LCD_COLUMNS 16 +#define LCD_ROWS 2 +#define lcd_i2c_address_mathertel_PCF8574 0x27 // I2C address of display for FEATURE_LCD_MATHERTEL_PCF8574 +#define lcd_i2c_address_fdebrander_lcd 0x27 // I2C address of display for FEATURE_LCD_I2C_FDEBRABANDER +#define lcd_i2c_address_ydv1_lcd 0x27 // I2C address of display for FEATURE_LCD_YDv1 +//#define lcd_i2c_address_ydv1_lcd 0x38 // I2C address of display for FEATURE_LCD_YDv1 +#define lcd_i2c_address_sainsmart_lcd 0x27 // I2C address of display for FEATURE_LCD_SAINSMART_I2C +#define hell_pixel_microseconds 4025 +#define program_memory_limit_consec_spaces 1 +#define serial_leading_zeros 1 // set to 1 to activate leading zeros in serial numbers (i.e. #1 = 001) +#define serial_cut_numbers 0 // set to 1 to activate cut numbers in serial numbers (i.e. #10 = 1T, #19 = 1N) +#define go_to_sleep_inactivity_time 10 // minutes - FEATURE_SLEEP +#define dim_backlight_inactive_time 5 // minutes - FEATURE_LCD_BACKLIGHT_AUTO_DIM +#define default_cmos_super_keyer_iambic_b_timing_percent 33 // use with FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING; should be between 0 to 99 % (0% = true iambic b;100% = iambic a behavior) +#define default_cw_echo_timing_factor 1.75 // "factory default" setting +#define default_autospace_timing_factor 2.0 // "factory default" setting +#define winkey_paddle_echo_buffer_decode_timing_factor 0.25 +#define potentiometer_always_on 0 +#define ptt_interlock_check_every_ms 100 +#define ptt_interlock_active_state HIGH +#define unknown_cw_character '*' +#define cli_paddle_echo_on_at_boot 1 +#define cli_straight_key_echo_on_at_boot 1 +#define tx_key_dit_and_dah_pins_active_state LOW +#define tx_key_dit_and_dah_pins_inactive_state HIGH +#define potentiometer_check_interval_ms 150 +#define potentiometer_reading_threshold 1 +#define default_paddle_interruption_quiet_time_element_lengths 0 +#define default_wordsworth_wordspace 6 +#define default_wordsworth_repetition 1 +#define serial_program_memory_buffer_size 500 +#define eeprom_write_time_ms 30000 + +#ifdef FEATURE_BUTTONS + #define analog_buttons_number_of_buttons 4 + #define analog_buttons_r1 10 + #define analog_buttons_r2 1 +#endif + + +#if defined(FEATURE_BUTTONS) && !defined(FEATURE_PS2_KEYBOARD) && !defined(FEATURE_USB_KEYBOARD) && !defined(FEATURE_COMMAND_LINE_INTERFACE) && !defined(FEATURE_WINKEY_EMULATION) + #define number_of_memories byte(analog_buttons_number_of_buttons-1) +#else + #define number_of_memories byte(12) +#endif + +#ifdef FEATURE_CAPACITIVE_PADDLE_PINS + #define capacitance_threshold 2 +#endif //FEATURE_CAPACITIVE_PADDLE_PINS + +#ifdef FEATURE_LED_RING + #define led_ring_low_limit 10 + #define led_ring_high_limit 50 +#endif //FEATURE_LED_RING + +#ifdef FEATURE_QLF + #define qlf_dit_max 125 + #define qlf_dit_min 75 + #define qlf_dah_max 200 + #define qlf_dah_min 100 + #define qlf_on_by_default 0 +#endif //FEATURE_QLF + + +#ifdef FEATURE_WINKEY_EMULATION + #ifdef OPTION_WINKEY_UCXLOG_9600_BAUD + #define WINKEY_DEFAULT_BAUD 9600 + #else + #define WINKEY_DEFAULT_BAUD 1200 + #endif //OPTION_WINKEY_UCXLOG_9600_BAUD +// alter these below to map alternate sidetones for Winkey interface protocol emulation +#ifdef OPTION_WINKEY_2_SUPPORT + #define WINKEY_SIDETONE_1 3759 + #define WINKEY_SIDETONE_2 1879 + #define WINKEY_SIDETONE_3 1252 + #define WINKEY_SIDETONE_4 940 + #define WINKEY_SIDETONE_5 752 + #define WINKEY_SIDETONE_6 625 + #define WINKEY_SIDETONE_7 535 + #define WINKEY_SIDETONE_8 469 + #define WINKEY_SIDETONE_9 417 + #define WINKEY_SIDETONE_10 375 +#else //OPTION_WINKEY_2_SUPPORT + #define WINKEY_SIDETONE_1 4000 + #define WINKEY_SIDETONE_2 2000 + #define WINKEY_SIDETONE_3 1333 + #define WINKEY_SIDETONE_4 1000 + #define WINKEY_SIDETONE_5 800 + #define WINKEY_SIDETONE_6 666 + #define WINKEY_SIDETONE_7 571 + #define WINKEY_SIDETONE_8 500 + #define WINKEY_SIDETONE_9 444 + #define WINKEY_SIDETONE_10 400 +#endif //OPTION_WINKEY_2_SUPPORT + +#define WINKEY_1_REPORT_VERSION_NUMBER 10 +#define WINKEY_2_REPORT_VERSION_NUMBER 23 + +// alter these to map to alternate hang time wordspace units +#define WINKEY_HANG_TIME_1_0 1.0 +#define WINKEY_HANG_TIME_1_33 1.33 +#define WINKEY_HANG_TIME_1_66 1.66 +#define WINKEY_HANG_TIME_2_0 2.0 + +#define WINKEY_RETURN_THIS_FOR_ADMIN_GET_CAL 0x16 +#define WINKEY_RETURN_THIS_FOR_ADMIN_PADDLE_A2D 0xEE +#define WINKEY_RETURN_THIS_FOR_ADMIN_SPEED_A2D 0x00 + +#endif //FEATURE_WINKEY_EMULATION + + +#define PRIMARY_SERIAL_PORT &Serial +#define PRIMARY_SERIAL_PORT_BAUD 115200 // This applies only when the port is in Command Line Interface mode. In Winkey mode it will default to 1200. + +#ifdef FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + #define SECONDARY_SERIAL_PORT &Serial1 + #define SECONDARY_SERIAL_PORT_BAUD 115200 +#endif + + +#define CW_DECODER_SCREEN_COLUMNS 120 // word wrap at this many columns +#define CW_DECODER_SPACE_PRINT_THRESH 4.5 // print space if no tone for this many element lengths +#define CW_DECODER_SPACE_DECODE_THRESH 2.0 // invoke character decode if no tone for this many element lengths +#define CW_DECODER_NOISE_FILTER 20 // ignore elements shorter than this (mS) + +#define STRAIGHT_KEY_ACTIVE_STATE LOW + +#ifdef FEATURE_DYNAMIC_DAH_TO_DIT_RATIO + #define DYNAMIC_DAH_TO_DIT_RATIO_LOWER_LIMIT_WPM 30 + #define DYNAMIC_DAH_TO_DIT_RATIO_LOWER_LIMIT_RATIO 300 // 300 = 3:1 ratio + #define DYNAMIC_DAH_TO_DIT_RATIO_UPPER_LIMIT_WPM 70 + #define DYNAMIC_DAH_TO_DIT_RATIO_UPPER_LIMIT_RATIO 240 // 240 = 2.4:1 ratio +#endif //FEATURE_DYNAMIC_DAH_TO_DIT_RATIO + +#if defined(FEATURE_COMPETITION_COMPRESSION_DETECTION) + #define COMPETITION_COMPRESSION_DETECTION_ARRAY_SIZE 16 + #define COMPETITION_COMPRESSION_DETECTION_TIME_INTERCHAR_LOWER_LIMIT 1 + #define COMPETITION_COMPRESSION_DETECTION_TIME_INTERCHAR_UPPER_LIMIT 6.0 + #define COMPETITION_COMPRESSION_DETECTION_AVERAGE_ALARM_THRESHOLD 3.0 +#endif //FEATURE_COMPETITION_COMPRESSION_DETECTION + +#if defined(FEATURE_SLEEP) + #define KEYER_AWAKE_PIN_AWAKE_STATE HIGH + #define KEYER_AWAKE_PIN_ASLEEP_STATE LOW +#endif + +#if defined(FEATURE_LCD_BACKLIGHT_AUTO_DIM) + #define keyer_power_led_awake_duty 255 // PWM duty cycle. 0 is 0%, 255 is 100% + #define keyer_power_led_asleep_duty 25 // 25 is quite dim. Use 0 for off +#endif + +#if defined(FEATURE_ETHERNET) + #define FEATURE_ETHERNET_IP {192,168,1,178} // default IP address ("192.168.1.178") + #define FEATURE_ETHERNET_MAC {0xDE,0xAD,0xBE,0xEF,0xFE,0xED} + #define FEATURE_ETHERNET_DNS {8,8,8,8} +// #define FEATURE_ETHERNET_IP {192,168,1,179} // default IP address ("192.168.1.179") +// #define FEATURE_ETHERNET_MAC {0xDE,0xAD,0xBE,0xEF,0xFE,0xEE} + + #define FEATURE_ETHERNET_GATEWAY {192,168,1,1} // default gateway + #define FEATURE_ETHERNET_SUBNET_MASK {255,255,255,0} // default subnet mask + #define FEATURE_ETHERNET_WEB_LISTENER_PORT 80 + #define FEATURE_UDP_SEND_BUFFER_SIZE 128 + #define FEATURE_UDP_RECEIVE_BUFFER_SIZE 128 +#endif //FEATURE_ETHERNET + +#define FEATURE_INTERNET_LINK_MAX_LINKS 2 +#define FEATURE_INTERNET_LINK_DEFAULT_RCV_UDP_PORT 8888 +#define FEATURE_INTERNET_LINK_BUFFER_TIME_MS 500 // increase this time if you have greater than 500 ms latency on your link +#define FEATURE_INTERNET_LINK_SVC_DURING_LOOP_TIME_MS 20 +#define FEATURE_INTERNET_LINK_KEY_DOWN_TIMEOUT_SECS 8 + +#define WEB_SERVER_CONTROL_TX_KEY_TIME_LIMIT_SECS 10 + +#if defined(FEATURE_4x4_KEYPAD)|| defined (FEATURE_3x4_KEYPAD) + #define KEYPAD_ROWS 4 //KeyPad Rows + #if defined(FEATURE_4x4_KEYPAD) + #define KEYPAD_COLS 4 //keypad Columns + #else + #define KEYPAD_COLS 3 + #endif + #define mem1 0 //Memory numbers for Keypad: Actual memory numbers start with "0" + #define mem2 1 + #define mem3 2 + #define mem4 3 + #define mem5 4 + #define mem6 5 + #define mem7 6 + #define mem8 7 + #define mem9 8 + #define mem10 9 + #define mem11 10 + #define mem12 11 +#endif //#if defined(FEATURE_4x4_KEYPAD)|| defined (FEATURE_3x4_KEYPAD) + +#define initial_sidetone_mode 1 // Sidetone mode, 0=OFF, 1=ON, 2=PADDLE_ONLY + +#define sd_card_spi_ss_line 4 + + +#if defined(OPTION_DFROBOT_LCD_COMMAND_BUTTONS) + + // For V1.1 board use these values + #define dfrobot_btnRIGHT_analog 50 + #define dfrobot_btnUP_analog 250 + #define dfrobot_btnDOWN_analog 450 + #define dfrobot_btnLEFT_analog 650 + #define dfrobot_btnSELECT_analog 850 + + // For V1.0 board use these values + // #define dfrobot_btnRIGHT_analog 50 + // #define dfrobot_btnUP_analog 195 + // #define dfrobot_btnDOWN_analog 380 + // #define dfrobot_btnLEFT_analog 555 + // #define dfrobot_btnSELECT_analog 790 + + // button to memory mappings (0 = command button, 1 = memory 1, 2 = memory 2, etc.) + #define dfrobot_btnRIGHT 2 + #define dfrobot_btnUP 1 + #define dfrobot_btnDOWN 3 + #define dfrobot_btnLEFT 4 + #define dfrobot_btnSELECT 0 + #define dfrobot_btnNONE 255 // do not change + +#endif + + +#define sequencer_pins_active_state HIGH +#define sequencer_pins_inactive_state LOW +#define ptt_line_active_state HIGH +#define ptt_line_inactive_state LOW +#define tx_key_line_active_state HIGH +#define tx_key_line_inactive_state LOW +#define ptt_input_pin_active_state LOW +#define ptt_input_pin_inactive_state HIGH +#define tx_inhibit_pin_active_state LOW +#define tx_inhibit_pin_inactive_state HIGH +#define tx_pause_pin_active_state LOW +#define tx_pause_pin_inactive_state HIGH +#define sidetone_line_active_state HIGH +#define sidetone_line_inactive_state LOW + +#if defined(ARDUINO_MAPLE_MINI) + #define button_value_factor 4095 +#else + #define button_value_factor 1023 +#endif + +#define farnsworth_timing_calibration 1.15 + +#define sidetone_volume_low_limit 10 +#define sidetone_volume_high_limit 500 + +#ifdef FEATURE_SO2R_BASE + #ifdef FEATURE_POTENTIOMETER + #ifdef FEATURE_SO2R_SWITCHES + #ifdef FEATURE_SO2R_ANTENNAS + #define SO2R_DEVICE_NAME "SO2R Mini PSA" + #else + #define SO2R_DEVICE_NAME "SO2R Mini PS" + #endif + #else + #ifdef FEATURE_SO2R_ANTENNAS + #define SO2R_DEVICE_NAME "SO2R Mini PA" + #else + #define SO2R_DEVICE_NAME "SO2R Mini P" + #endif + #endif + #else + #ifdef FEATURE_SO2R_SWITCHES + #ifdef FEATURE_SO2R_ANTENNAS + #define SO2R_DEVICE_NAME "SO2R Mini SA" + #else + #define SO2R_DEVICE_NAME "SO2R Mini S" + #endif + #else + #ifdef FEATURE_SO2R_ANTENNAS + #define SO2R_DEVICE_NAME "SO2R Mini A" + #else + #define SO2R_DEVICE_NAME "SO2R Mini" + #endif + #endif + #endif +#endif + +#define custom_startup_field "your custom text here" // an example could be callsign and name, eg. "AB1XYZ Bob", (or "Worlds best operator" which requires a 20 column display), string length shouldo be no more than the number of columns on the display + +#define command_mode_acknowledgement_character 'E' + +#if defined(FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT) + #define command_a_iambic_a "A" + #define command_b_iambic_b "B" + #define command_c_single_paddle "SINGLE" + #define command_d_ultimatic "ULT" + #define command_h_weight_dit_dah_ratio_default "R" + #define command_i_tx_on "TX ON" + #define command_i_tx_off "TX OFF" + #define command_k_dit_dah_buffers_on "ON" + #define command_k_dit_dah_buffers_off "OFF" + #define command_n_paddle_reverse "REV" + #define command_n_paddle_normal "NORM" + #define command_o_sidetone_off "ST OFF" + #define command_o_sidetone_paddle_only "ST PD ONLY" + #define command_o_sidetone_on "ST ON" + #define command_t_tune_mode "TUNE" + #define command_v_potentiometer_on "POT ON" + #define command_v_potentiometer_off "POT OFF" + + #define command_error "ERR" + +#endif //FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT diff --git a/k3ng_keyer/keyer_settings_tinykeyer.h b/k3ng_keyer/keyer_settings_tinykeyer.h new file mode 100755 index 0000000..7054afc --- /dev/null +++ b/k3ng_keyer/keyer_settings_tinykeyer.h @@ -0,0 +1,310 @@ +// Initial and hardcoded settings +// TinyKeyer by OK1RR +#define initial_speed_wpm 30 // "factory default" keyer speed setting +#define initial_command_mode_speed_wpm 20 // "factory default" command mode speed setting +#define initial_sidetone_freq 444 // "factory default" sidetone frequency setting +#define sidetone_hz_limit_low 299 +#define sidetone_hz_limit_high 2001 +#define hz_high_beep 1500 // frequency in hertz of high beep +#define hz_low_beep 300 // frequency in hertz of low beep +#define initial_dah_to_dit_ratio 300 // 300 = 3 / normal 3:1 ratio +#define initial_ptt_lead_time_tx1 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx1 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx2 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx2 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx3 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx3 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx4 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx4 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx5 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx5 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx6 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx6 10 // PTT tail time in mS +#define initial_qrss_dit_length 1 // QRSS dit length in seconds +#define initial_pot_wpm_low_value 13 // Potentiometer WPM fully CCW +#define initial_pot_wpm_high_value 35 // Potentiometer WPM fully CW +#define wpm_limit_low 5 +#define wpm_limit_high 90 +#define potentiometer_change_threshold 0.9 // don't change the keyer speed until pot wpm has changed more than this +#define send_buffer_size 250 +#define default_length_letterspace 3 +#define default_length_wordspace 7 +#define default_keying_compensation 0 // number of milliseconds to extend all dits and dahs - for QSK on boatanchors +#define default_first_extension_time 0 // number of milliseconds to extend first sent dit or dah +#define default_pot_full_scale_reading 1023 +#define default_weighting 50 // 50 = weighting factor of 1 (normal) +#define default_ptt_hang_time_wordspace_units 0.0 +#define winkey_c0_wait_time 1 // the number of milliseconds to wait to send 0xc0 byte after send buffer has been sent +#define winkey_command_timeout_ms 5000 +#define winkey_discard_bytes_startup 3 // this is used if OPTION_WINKEY_DISCARD_BYTES_AT_STARTUP is enabled above +#define winkey_xoff_threshold 20 // the number of chars in the buffer when we begin sending XOFFs +#define winkey_xon_threshold 10 // the number of chars in the buffer below which we deactivate XOFF +#define default_memory_repeat_time 3000 // time in milliseconds +#define LCD_COLUMNS 16 +#define LCD_ROWS 2 +#define lcd_i2c_address_mathertel_PCF8574 0x27 // I2C address of display for FEATURE_LCD_MATHERTEL_PCF8574 +#define lcd_i2c_address_fdebrander_lcd 0x27 // I2C address of display for FEATURE_LCD_I2C_FDEBRABANDER +#define lcd_i2c_address_ydv1_lcd 0x27 // I2C address of display for FEATURE_LCD_YDv1 +//#define lcd_i2c_address_ydv1_lcd 0x38 // I2C address of display for FEATURE_LCD_YDv1 +#define lcd_i2c_address_sainsmart_lcd 0x27 // I2C address of display for FEATURE_LCD_SAINSMART_I2C +#define hell_pixel_microseconds 4025 +#define program_memory_limit_consec_spaces 1 +#define serial_leading_zeros 1 // set to 1 to activate leading zeros in serial numbers (i.e. #1 = 001) +#define serial_cut_numbers 0 // set to 1 to activate cut numbers in serial numbers (i.e. #10 = 1T, #19 = 1N) +#define go_to_sleep_inactivity_time 10 // minutes - FEATURE_SLEEP +#define dim_backlight_inactive_time 5 // minutes - FEATURE_LCD_BACKLIGHT_AUTO_DIM +#define default_cmos_super_keyer_iambic_b_timing_percent 33 // use with FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING; should be between 0 to 99 % (0% = true iambic b;100% = iambic a behavior) +#define default_cw_echo_timing_factor 1.75 // "factory default" setting +#define default_autospace_timing_factor 2.0 // "factory default" setting +#define winkey_paddle_echo_buffer_decode_timing_factor 0.25 +#define potentiometer_always_on 0 +#define ptt_interlock_check_every_ms 100 +#define ptt_interlock_active_state HIGH +#define unknown_cw_character '*' +#define cli_paddle_echo_on_at_boot 1 +#define cli_straight_key_echo_on_at_boot 1 +#define tx_key_dit_and_dah_pins_active_state HIGH +#define tx_key_dit_and_dah_pins_inactive_state LOW +#define potentiometer_check_interval_ms 150 +#define potentiometer_reading_threshold 1 +#define default_paddle_interruption_quiet_time_element_lengths 7 +#define default_wordsworth_wordspace 6 +#define default_wordsworth_repetition 1 +#define serial_program_memory_buffer_size 500 +#define eeprom_write_time_ms 30000 + +#ifdef FEATURE_BUTTONS +#define analog_buttons_number_of_buttons 1 +#define analog_buttons_r1 10 +#define analog_buttons_r2 1 +#endif + + +#if defined(FEATURE_BUTTONS) && !defined(FEATURE_PS2_KEYBOARD) && !defined(FEATURE_USB_KEYBOARD) && !defined(FEATURE_COMMAND_LINE_INTERFACE) && !defined(FEATURE_WINKEY_EMULATION) +#define number_of_memories byte(analog_buttons_number_of_buttons-1) +#else +#define number_of_memories byte(1) +#endif + +#ifdef FEATURE_CAPACITIVE_PADDLE_PINS +#define capacitance_threshold 2 +#endif //FEATURE_CAPACITIVE_PADDLE_PINS + +#ifdef FEATURE_LED_RING +#define led_ring_low_limit 10 +#define led_ring_high_limit 50 +#endif //FEATURE_LED_RING + +#ifdef FEATURE_QLF +#define qlf_dit_max 125 +#define qlf_dit_min 75 +#define qlf_dah_max 200 +#define qlf_dah_min 100 +#define qlf_on_by_default 0 +#endif //FEATURE_QLF + + +#ifdef FEATURE_WINKEY_EMULATION + #ifdef OPTION_WINKEY_UCXLOG_9600_BAUD + #define WINKEY_DEFAULT_BAUD 9600 + #else + #define WINKEY_DEFAULT_BAUD 1200 + #endif //OPTION_WINKEY_UCXLOG_9600_BAUD +// alter these below to map alternate sidetones for Winkey interface protocol emulation + #ifdef OPTION_WINKEY_2_SUPPORT + #define WINKEY_SIDETONE_1 3759 + #define WINKEY_SIDETONE_2 1879 + #define WINKEY_SIDETONE_3 1252 + #define WINKEY_SIDETONE_4 940 + #define WINKEY_SIDETONE_5 752 + #define WINKEY_SIDETONE_6 625 + #define WINKEY_SIDETONE_7 535 + #define WINKEY_SIDETONE_8 469 + #define WINKEY_SIDETONE_9 417 + #define WINKEY_SIDETONE_10 375 + #else //OPTION_WINKEY_2_SUPPORT + #define WINKEY_SIDETONE_1 4000 + #define WINKEY_SIDETONE_2 2000 + #define WINKEY_SIDETONE_3 1333 + #define WINKEY_SIDETONE_4 1000 + #define WINKEY_SIDETONE_5 800 + #define WINKEY_SIDETONE_6 666 + #define WINKEY_SIDETONE_7 571 + #define WINKEY_SIDETONE_8 500 + #define WINKEY_SIDETONE_9 444 + #define WINKEY_SIDETONE_10 400 +#endif //OPTION_WINKEY_2_SUPPORT + +#define WINKEY_1_REPORT_VERSION_NUMBER 10 +#define WINKEY_2_REPORT_VERSION_NUMBER 23 + +// alter these to map to alternate hang time wordspace units +#define WINKEY_HANG_TIME_1_0 1.0 +#define WINKEY_HANG_TIME_1_33 1.33 +#define WINKEY_HANG_TIME_1_66 1.66 +#define WINKEY_HANG_TIME_2_0 2.0 + +#define WINKEY_RETURN_THIS_FOR_ADMIN_GET_CAL 0x16 +#define WINKEY_RETURN_THIS_FOR_ADMIN_PADDLE_A2D 0xEE +#define WINKEY_RETURN_THIS_FOR_ADMIN_SPEED_A2D 0x00 + +#endif //FEATURE_WINKEY_EMULATION + +#define PRIMARY_SERIAL_PORT &Serial +#define PRIMARY_SERIAL_PORT_BAUD 115200 // This applies only when the port is in Command Line Interface mode. In Winkey mode it will default to 1200. + +#ifdef FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + #define SECONDARY_SERIAL_PORT &Serial1 + #define SECONDARY_SERIAL_PORT_BAUD 115200 +#endif + +#define CW_DECODER_SCREEN_COLUMNS 40 // word wrap at this many columns +#define CW_DECODER_SPACE_PRINT_THRESH 4.5 // print space if no tone for this many element lengths +#define CW_DECODER_SPACE_DECODE_THRESH 2.0 // invoke character decode if no tone for this many element lengths +#define CW_DECODER_NOISE_FILTER 20 // ignore elements shorter than this (mS) + +#define STRAIGHT_KEY_ACTIVE_STATE LOW + +#ifdef FEATURE_DYNAMIC_DAH_TO_DIT_RATIO + #define DYNAMIC_DAH_TO_DIT_RATIO_LOWER_LIMIT_WPM 30 + #define DYNAMIC_DAH_TO_DIT_RATIO_LOWER_LIMIT_RATIO 300 // 300 = 3:1 ratio + #define DYNAMIC_DAH_TO_DIT_RATIO_UPPER_LIMIT_WPM 70 + #define DYNAMIC_DAH_TO_DIT_RATIO_UPPER_LIMIT_RATIO 240 // 240 = 2.4:1 ratio +#endif //FEATURE_DYNAMIC_DAH_TO_DIT_RATIO + +#if defined(FEATURE_SLEEP) + #define KEYER_AWAKE_PIN_AWAKE_STATE HIGH + #define KEYER_AWAKE_PIN_ASLEEP_STATE LOW +#endif + +#if defined(FEATURE_LCD_BACKLIGHT_AUTO_DIM) + #define keyer_power_led_awake_duty 255 // PWM duty cycle. 0 is 0%, 255 is 100% + #define keyer_power_led_asleep_duty 25 // 25 is quite dim. Use 0 for off +#endif + +#if defined(FEATURE_ETHERNET) + // #define FEATURE_ETHERNET_IP {192,168,1,178} // default IP address ("192.168.1.178") + // #define FEATURE_ETHERNET_MAC {0xDE,0xAD,0xBE,0xEF,0xFE,0xED} + #define FEATURE_ETHERNET_IP {192,168,1,179} // default IP address ("192.168.1.178") + #define FEATURE_ETHERNET_MAC {0xDE,0xAD,0xBE,0xEF,0xFE,0xEE} + #define FEATURE_ETHERNET_DNS {8,8,8,8} + + #define FEATURE_ETHERNET_GATEWAY {192,168,1,1} // default gateway + #define FEATURE_ETHERNET_SUBNET_MASK {255,255,255,0} // default subnet mask + #define FEATURE_ETHERNET_WEB_LISTENER_PORT 80 + #define FEATURE_UDP_SEND_BUFFER_SIZE 128 + #define FEATURE_UDP_RECEIVE_BUFFER_SIZE 128 +#endif //FEATURE_ETHERNET + +#define WEB_SERVER_CONTROL_TX_KEY_TIME_LIMIT_SECS 10 + +#define FEATURE_INTERNET_LINK_MAX_LINKS 2 +#define FEATURE_INTERNET_LINK_DEFAULT_RCV_UDP_PORT 8888 +#define FEATURE_INTERNET_LINK_BUFFER_TIME_MS 500 + +#if defined(FEATURE_4x4_KEYPAD)|| defined (FEATURE_3x4_KEYPAD) + #define KEYPAD_ROWS 4 //KeyPad Rows + #if defined(FEATURE_4x4_KEYPAD) + #define KEYPAD_COLS 4 //keypad Columns + #else + #define KEYPAD_COLS 3 + #endif + #define mem1 0 //Memory numbers for Keypad: Actual memory numbers start with "0" + #define mem2 1 + #define mem3 2 + #define mem4 3 + #define mem5 4 + #define mem6 5 + #define mem7 6 + #define mem8 7 + #define mem9 8 + #define mem10 9 + #define mem11 10 + #define mem12 11 +#endif //#if defined(FEATURE_4x4_KEYPAD)|| defined (FEATURE_3x4_KEYPAD) + +#define initial_sidetone_mode 1 // Sidetone mode, 0=OFF, 1=ON, 2=PADDLE_ONLY + +#define sd_card_spi_ss_line 4 + +#if defined(OPTION_DFROBOT_LCD_COMMAND_BUTTONS) + + // For V1.1 board use these values + #define dfrobot_btnRIGHT_analog 50 + #define dfrobot_btnUP_analog 250 + #define dfrobot_btnDOWN_analog 450 + #define dfrobot_btnLEFT_analog 650 + #define dfrobot_btnSELECT_analog 850 + + // For V1.0 board use these values + // #define dfrobot_btnRIGHT_analog 50 + // #define dfrobot_btnUP_analog 195 + // #define dfrobot_btnDOWN_analog 380 + // #define dfrobot_btnLEFT_analog 555 + // #define dfrobot_btnSELECT_analog 790 + + // button to memory mappings (0 = command button, 1 = memory 1, 2 = memory 2, etc.) + #define dfrobot_btnRIGHT 2 + #define dfrobot_btnUP 1 + #define dfrobot_btnDOWN 3 + #define dfrobot_btnLEFT 4 + #define dfrobot_btnSELECT 0 + #define dfrobot_btnNONE 255 // do not change + +#endif + +#define sequencer_pins_active_state HIGH +#define sequencer_pins_inactive_state LOW + +#define sequencer_pins_active_state HIGH +#define sequencer_pins_inactive_state LOW +#define ptt_line_active_state HIGH +#define ptt_line_inactive_state LOW +#define tx_key_line_active_state HIGH +#define tx_key_line_inactive_state LOW +#define ptt_input_pin_active_state LOW +#define ptt_input_pin_inactive_state HIGH +#define tx_inhibit_pin_active_state LOW +#define tx_inhibit_pin_inactive_state HIGH +#define tx_pause_pin_active_state LOW +#define tx_pause_pin_inactive_state HIGH +#define sidetone_line_active_state HIGH +#define sidetone_line_inactive_state LOW + +#if defined(ARDUINO_MAPLE_MINI) + #define button_value_factor 4095 //sp5iou contributed +#else + #define button_value_factor 1023 +#endif + +#define farnsworth_timing_calibration 1.15 + +#define sidetone_volume_low_limit 10 +#define sidetone_volume_high_limit 500 + +#define custom_startup_field "your custom text here" // an example could be callsign and name, eg. "AB1XYZ Bob", (or "Worlds best operator" which requires a 20 column display), string length shouldo be no more than the number of columns on the display + +#define command_mode_acknowledgement_character 'E' + +#if defined(FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT) + #define command_a_iambic_a "A" + #define command_b_iambic_b "B" + #define command_c_single_paddle "SINGLE" + #define command_d_ultimatic "ULT" + #define command_h_weight_dit_dah_ratio_default "R" + #define command_i_tx_on "TX ON" + #define command_i_tx_off "TX OFF" + #define command_k_dit_dah_buffers_on "ON" + #define command_k_dit_dah_buffers_off "OFF" + #define command_n_paddle_reverse "REV" + #define command_n_paddle_normal "NORM" + #define command_o_sidetone_off "ST OFF" + #define command_o_sidetone_paddle_only "ST PD ONLY" + #define command_o_sidetone_on "ST ON" + #define command_t_tune_mode "TUNE" + #define command_v_potentiometer_on "POT ON" + #define command_v_potentiometer_off "POT OFF" + + #define command_error "ERR" + +#endif //FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT diff --git a/k3ng_keyer/keyer_settings_yaacwk.h b/k3ng_keyer/keyer_settings_yaacwk.h new file mode 100755 index 0000000..c3acb57 --- /dev/null +++ b/k3ng_keyer/keyer_settings_yaacwk.h @@ -0,0 +1,317 @@ +// Initial and hardcoded settings +#define initial_speed_wpm 26 // "factory default" keyer speed setting +#define initial_command_mode_speed_wpm 20 // "factory default" command mode speed setting +#define initial_sidetone_freq 600 // "factory default" sidetone frequency setting +#define sidetone_hz_limit_low 299 +#define sidetone_hz_limit_high 2001 +#define hz_high_beep 1500 // frequency in hertz of high beep +#define hz_low_beep 400 // frequency in hertz of low beep +#define initial_dah_to_dit_ratio 300 // 300 = 3 / normal 3:1 ratio +#define initial_ptt_lead_time_tx1 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx1 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx2 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx2 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx3 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx3 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx4 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx4 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx5 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx5 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx6 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx6 10 // PTT tail time in mS +#define initial_qrss_dit_length 1 // QRSS dit length in seconds +#define initial_pot_wpm_low_value 13 // Potentiometer WPM fully CCW +#define initial_pot_wpm_high_value 35 // Potentiometer WPM fully CW +#define wpm_limit_low 5 +#define wpm_limit_high 60 +#define potentiometer_change_threshold 0.9 // don't change the keyer speed until pot wpm has changed more than this +#define send_buffer_size 150 +#define default_length_letterspace 3 +#define default_length_wordspace 7 +#define default_keying_compensation 0 // number of milliseconds to extend all dits and dahs - for QSK on boatanchors +#define default_first_extension_time 0 // number of milliseconds to extend first sent dit or dah +#define default_pot_full_scale_reading 1023 +#define default_weighting 50 // 50 = weighting factor of 1 (normal) +#define default_ptt_hang_time_wordspace_units 0.0 +#define winkey_c0_wait_time 1 // the number of milliseconds to wait to send 0xc0 byte after send buffer has been sent +#define winkey_command_timeout_ms 5000 +#define winkey_discard_bytes_startup 3 // this is used if OPTION_WINKEY_DISCARD_BYTES_AT_STARTUP is enabled above +#define winkey_xoff_threshold 20 // the number of chars in the buffer when we begin sending XOFFs +#define winkey_xon_threshold 10 // the number of chars in the buffer below which we deactivate XOFF +#define default_memory_repeat_time 3000 // time in milliseconds +#define LCD_COLUMNS 20 +#define LCD_ROWS 4 +#define lcd_i2c_address_mathertel_PCF8574 0x27 // I2C address of display for FEATURE_LCD_MATHERTEL_PCF8574 +#define lcd_i2c_address_fdebrander_lcd 0x27 // I2C address of display for FEATURE_LCD_I2C_FDEBRABANDER +#define lcd_i2c_address_ydv1_lcd 0x27 // I2C address of display for FEATURE_LCD_YDv1 +//#define lcd_i2c_address_ydv1_lcd 0x38 // I2C address of display for FEATURE_LCD_YDv1 +#define lcd_i2c_address_sainsmart_lcd 0x27 // I2C address of display for FEATURE_LCD_SAINSMART_I2C +#define hell_pixel_microseconds 4025 +#define program_memory_limit_consec_spaces 1 +#define serial_leading_zeros 1 // set to 1 to activate leading zeros in serial numbers (i.e. #1 = 001) +#define serial_cut_numbers 0 // set to 1 to activate cut numbers in serial numbers (i.e. #10 = 1T, #19 = 1N) +#define go_to_sleep_inactivity_time 10 // minutes - FEATURE_SLEEP +#define dim_backlight_inactive_time 5 // minutes - FEATURE_LCD_BACKLIGHT_AUTO_DIM +#define default_cmos_super_keyer_iambic_b_timing_percent 33 // use with FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING; should be between 0 to 99 % (0% = true iambic b;100% = iambic a behavior) +#define default_cw_echo_timing_factor 1.75 // "factory default" setting +#define default_autospace_timing_factor 2.0 // "factory default" setting +#define winkey_paddle_echo_buffer_decode_timing_factor 0.25 +#define potentiometer_always_on 1 +#define ptt_interlock_check_every_ms 100 +#define ptt_interlock_active_state HIGH +#define unknown_cw_character '*' +#define cli_paddle_echo_on_at_boot 1 +#define cli_straight_key_echo_on_at_boot 1 +#define tx_key_dit_and_dah_pins_active_state HIGH +#define tx_key_dit_and_dah_pins_inactive_state LOW +#define potentiometer_check_interval_ms 150 +#define potentiometer_reading_threshold 1 +#define default_paddle_interruption_quiet_time_element_lengths 0 +#define default_wordsworth_wordspace 6 +#define default_wordsworth_repetition 1 +#define serial_program_memory_buffer_size 500 +#define eeprom_write_time_ms 30000 + + +#ifdef FEATURE_BUTTONS + #define analog_buttons_number_of_buttons 8 + #define analog_buttons_r1 10 + #define analog_buttons_r2 1 +#endif + + +#if defined(FEATURE_BUTTONS) && !defined(FEATURE_PS2_KEYBOARD) && !defined(FEATURE_USB_KEYBOARD) && !defined(FEATURE_COMMAND_LINE_INTERFACE) && !defined(FEATURE_WINKEY_EMULATION) + #define number_of_memories byte(analog_buttons_number_of_buttons-1) +#else + #define number_of_memories byte(12) +#endif + +#ifdef FEATURE_CAPACITIVE_PADDLE_PINS + #define capacitance_threshold 2 +#endif //FEATURE_CAPACITIVE_PADDLE_PINS + +#ifdef FEATURE_LED_RING + #define led_ring_low_limit 10 + #define led_ring_high_limit 50 +#endif //FEATURE_LED_RING + +#ifdef FEATURE_QLF + #define qlf_dit_max 125 + #define qlf_dit_min 75 + #define qlf_dah_max 200 + #define qlf_dah_min 100 + #define qlf_on_by_default 0 +#endif //FEATURE_QLF + + +#ifdef FEATURE_WINKEY_EMULATION + #ifdef OPTION_WINKEY_UCXLOG_9600_BAUD + #define WINKEY_DEFAULT_BAUD 9600 + #else + #define WINKEY_DEFAULT_BAUD 1200 + #endif //OPTION_WINKEY_UCXLOG_9600_BAUD +// alter these below to map alternate sidetones for Winkey interface protocol emulation +#ifdef OPTION_WINKEY_2_SUPPORT + #define WINKEY_SIDETONE_1 3759 + #define WINKEY_SIDETONE_2 1879 + #define WINKEY_SIDETONE_3 1252 + #define WINKEY_SIDETONE_4 940 + #define WINKEY_SIDETONE_5 752 + #define WINKEY_SIDETONE_6 625 + #define WINKEY_SIDETONE_7 535 + #define WINKEY_SIDETONE_8 469 + #define WINKEY_SIDETONE_9 417 + #define WINKEY_SIDETONE_10 375 +#else //OPTION_WINKEY_2_SUPPORT + #define WINKEY_SIDETONE_1 4000 + #define WINKEY_SIDETONE_2 2000 + #define WINKEY_SIDETONE_3 1333 + #define WINKEY_SIDETONE_4 1000 + #define WINKEY_SIDETONE_5 800 + #define WINKEY_SIDETONE_6 666 + #define WINKEY_SIDETONE_7 571 + #define WINKEY_SIDETONE_8 500 + #define WINKEY_SIDETONE_9 444 + #define WINKEY_SIDETONE_10 400 +#endif //OPTION_WINKEY_2_SUPPORT + +#define WINKEY_1_REPORT_VERSION_NUMBER 10 +#define WINKEY_2_REPORT_VERSION_NUMBER 23 + +// alter these to map to alternate hang time wordspace units +#define WINKEY_HANG_TIME_1_0 1.0 +#define WINKEY_HANG_TIME_1_33 1.33 +#define WINKEY_HANG_TIME_1_66 1.66 +#define WINKEY_HANG_TIME_2_0 2.0 + +#define WINKEY_RETURN_THIS_FOR_ADMIN_GET_CAL 0x16 +#define WINKEY_RETURN_THIS_FOR_ADMIN_PADDLE_A2D 0xEE +#define WINKEY_RETURN_THIS_FOR_ADMIN_SPEED_A2D 0x00 + +#endif //FEATURE_WINKEY_EMULATION + + +#define PRIMARY_SERIAL_PORT &Serial +#define PRIMARY_SERIAL_PORT_BAUD 115200 // This applies only when the port is in Command Line Interface mode. In Winkey mode it will default to 1200. + +#ifdef FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + #define SECONDARY_SERIAL_PORT &Serial1 + #define SECONDARY_SERIAL_PORT_BAUD 115200 +#endif + +#define CW_DECODER_SCREEN_COLUMNS 40 // word wrap at this many columns +#define CW_DECODER_SPACE_PRINT_THRESH 4.5 // print space if no tone for this many element lengths +#define CW_DECODER_SPACE_DECODE_THRESH 2.0 // invoke character decode if no tone for this many element lengths +#define CW_DECODER_NOISE_FILTER 20 // ignore elements shorter than this (mS) + +#define STRAIGHT_KEY_ACTIVE_STATE LOW + +#ifdef FEATURE_DYNAMIC_DAH_TO_DIT_RATIO + #define DYNAMIC_DAH_TO_DIT_RATIO_LOWER_LIMIT_WPM 30 + #define DYNAMIC_DAH_TO_DIT_RATIO_LOWER_LIMIT_RATIO 300 // 300 = 3:1 ratio + #define DYNAMIC_DAH_TO_DIT_RATIO_UPPER_LIMIT_WPM 70 + #define DYNAMIC_DAH_TO_DIT_RATIO_UPPER_LIMIT_RATIO 240 // 240 = 2.4:1 ratio +#endif //FEATURE_DYNAMIC_DAH_TO_DIT_RATIO + +#if defined(FEATURE_COMPETITION_COMPRESSION_DETECTION) + #define COMPETITION_COMPRESSION_DETECTION_ARRAY_SIZE 16 + #define COMPETITION_COMPRESSION_DETECTION_TIME_INTERCHAR_LOWER_LIMIT 1 + #define COMPETITION_COMPRESSION_DETECTION_TIME_INTERCHAR_UPPER_LIMIT 6.0 + #define COMPETITION_COMPRESSION_DETECTION_AVERAGE_ALARM_THRESHOLD 3.0 +#endif //FEATURE_COMPETITION_COMPRESSION_DETECTION + +#if defined(FEATURE_SLEEP) + #define KEYER_AWAKE_PIN_AWAKE_STATE HIGH + #define KEYER_AWAKE_PIN_ASLEEP_STATE LOW +#endif + +#if defined(FEATURE_LCD_BACKLIGHT_AUTO_DIM) + #define keyer_power_led_awake_duty 255 // PWM duty cycle. 0 is 0%, 255 is 100% + #define keyer_power_led_asleep_duty 25 // 25 is quite dim. Use 0 for off +#endif + +#if defined(FEATURE_ETHERNET) + // #define FEATURE_ETHERNET_IP {192,168,1,178} // default IP address ("192.168.1.178") + // #define FEATURE_ETHERNET_MAC {0xDE,0xAD,0xBE,0xEF,0xFE,0xED} + #define FEATURE_ETHERNET_IP {192,168,1,179} // default IP address ("192.168.1.178") + #define FEATURE_ETHERNET_MAC {0xDE,0xAD,0xBE,0xEF,0xFE,0xEE} + #define FEATURE_ETHERNET_DNS {8,8,8,8} + + #define FEATURE_ETHERNET_GATEWAY {192,168,1,1} // default gateway + #define FEATURE_ETHERNET_SUBNET_MASK {255,255,255,0} // default subnet mask + #define FEATURE_ETHERNET_WEB_LISTENER_PORT 80 + #define FEATURE_UDP_SEND_BUFFER_SIZE 128 + #define FEATURE_UDP_RECEIVE_BUFFER_SIZE 128 +#endif //FEATURE_ETHERNET + +#define WEB_SERVER_CONTROL_TX_KEY_TIME_LIMIT_SECS 10 + +#define FEATURE_INTERNET_LINK_MAX_LINKS 2 +#define FEATURE_INTERNET_LINK_DEFAULT_RCV_UDP_PORT 8888 +#define FEATURE_INTERNET_LINK_BUFFER_TIME_MS 500 + +#if defined(FEATURE_4x4_KEYPAD)|| defined (FEATURE_3x4_KEYPAD) + #define KEYPAD_ROWS 4 //KeyPad Rows + #if defined(FEATURE_4x4_KEYPAD) + #define KEYPAD_COLS 4 //keypad Columns + #else + #define KEYPAD_COLS 3 + #endif + #define mem1 0 //Memory numbers for Keypad: Actual memory numbers start with "0" + #define mem2 1 + #define mem3 2 + #define mem4 3 + #define mem5 4 + #define mem6 5 + #define mem7 6 + #define mem8 7 + #define mem9 8 + #define mem10 9 + #define mem11 10 + #define mem12 11 +#endif //#if defined(FEATURE_4x4_KEYPAD)|| defined (FEATURE_3x4_KEYPAD) + +#define initial_sidetone_mode 1 // Sidetone mode, 0=OFF, 1=ON, 2=PADDLE_ONLY + +#define sd_card_spi_ss_line 4 + +#if defined(OPTION_DFROBOT_LCD_COMMAND_BUTTONS) + + // For V1.1 board use these values + #define dfrobot_btnRIGHT_analog 50 + #define dfrobot_btnUP_analog 250 + #define dfrobot_btnDOWN_analog 450 + #define dfrobot_btnLEFT_analog 650 + #define dfrobot_btnSELECT_analog 850 + + // For V1.0 board use these values + // #define dfrobot_btnRIGHT_analog 50 + // #define dfrobot_btnUP_analog 195 + // #define dfrobot_btnDOWN_analog 380 + // #define dfrobot_btnLEFT_analog 555 + // #define dfrobot_btnSELECT_analog 790 + + // button to memory mappings (0 = command button, 1 = memory 1, 2 = memory 2, etc.) + #define dfrobot_btnRIGHT 2 + #define dfrobot_btnUP 1 + #define dfrobot_btnDOWN 3 + #define dfrobot_btnLEFT 4 + #define dfrobot_btnSELECT 0 + #define dfrobot_btnNONE 255 // do not change + +#endif + + +#define sequencer_pins_active_state HIGH +#define sequencer_pins_inactive_state LOW +#define ptt_line_active_state HIGH +#define ptt_line_inactive_state LOW +#define tx_key_line_active_state HIGH +#define tx_key_line_inactive_state LOW +#define ptt_input_pin_active_state LOW +#define ptt_input_pin_inactive_state HIGH +#define tx_inhibit_pin_active_state LOW +#define tx_inhibit_pin_inactive_state HIGH +#define tx_pause_pin_active_state LOW +#define tx_pause_pin_inactive_state HIGH +#define sidetone_line_active_state HIGH +#define sidetone_line_inactive_state LOW + +#if defined(ARDUINO_MAPLE_MINI) + #define button_value_factor 4095 //sp5iou contributed +#else + #define button_value_factor 1023 +#endif + +#define farnsworth_timing_calibration 1.15 + +#define sidetone_volume_low_limit 10 +#define sidetone_volume_high_limit 500 + +#define custom_startup_field "your custom text here" // an example could be callsign and name, eg. "AB1XYZ Bob", (or "Worlds best operator" which requires a 20 column display), string length shouldo be no more than the number of columns on the display + +#define command_mode_acknowledgement_character 'E' + +#if defined(FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT) + #define command_a_iambic_a "A" + #define command_b_iambic_b "B" + #define command_c_single_paddle "SINGLE" + #define command_d_ultimatic "ULT" + #define command_h_weight_dit_dah_ratio_default "R" + #define command_i_tx_on "TX ON" + #define command_i_tx_off "TX OFF" + #define command_k_dit_dah_buffers_on "ON" + #define command_k_dit_dah_buffers_off "OFF" + #define command_n_paddle_reverse "REV" + #define command_n_paddle_normal "NORM" + #define command_o_sidetone_off "ST OFF" + #define command_o_sidetone_paddle_only "ST PD ONLY" + #define command_o_sidetone_on "ST ON" + #define command_t_tune_mode "TUNE" + #define command_v_potentiometer_on "POT ON" + #define command_v_potentiometer_off "POT OFF" + + #define command_error "ERR" + +#endif //FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT + diff --git a/k3ng_keyer/keyer_settings_yccc_so2r_mini.h b/k3ng_keyer/keyer_settings_yccc_so2r_mini.h new file mode 100755 index 0000000..1fbf501 --- /dev/null +++ b/k3ng_keyer/keyer_settings_yccc_so2r_mini.h @@ -0,0 +1,351 @@ +// Initial and hardcoded settings +#define initial_speed_wpm 26 // "factory default" keyer speed setting +#define initial_command_mode_speed_wpm 20 // "factory default" command mode speed setting +#define initial_sidetone_freq 600 // "factory default" sidetone frequency setting +#define sidetone_hz_limit_low 299 +#define sidetone_hz_limit_high 2001 +#define hz_high_beep 1500 // frequency in hertz of high beep +#define hz_low_beep 400 // frequency in hertz of low beep +#define initial_dah_to_dit_ratio 300 // 300 = 3 / normal 3:1 ratio +#define initial_ptt_lead_time_tx1 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx1 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx2 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx2 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx3 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx3 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx4 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx4 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx5 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx5 10 // PTT tail time in mS +#define initial_ptt_lead_time_tx6 0 // PTT lead time in mS +#define initial_ptt_tail_time_tx6 10 // PTT tail time in mS +#define initial_qrss_dit_length 1 // QRSS dit length in seconds +#define initial_pot_wpm_low_value 13 // Potentiometer WPM fully CCW +#define initial_pot_wpm_high_value 35 // Potentiometer WPM fully CW +#define wpm_limit_low 5 +#define wpm_limit_high 60 +#define potentiometer_change_threshold 0.9 // don't change the keyer speed until pot wpm has changed more than this +#define send_buffer_size 150 +#define default_length_letterspace 3 +#define default_length_wordspace 7 +#define default_keying_compensation 0 // number of milliseconds to extend all dits and dahs - for QSK on boatanchors +#define default_first_extension_time 0 // number of milliseconds to extend first sent dit or dah +#define default_pot_full_scale_reading 1023 +#define default_weighting 50 // 50 = weighting factor of 1 (normal) +#define default_ptt_hang_time_wordspace_units 0.0 +#define winkey_c0_wait_time 1 // the number of milliseconds to wait to send 0xc0 byte after send buffer has been sent +#define winkey_command_timeout_ms 5000 +#define winkey_discard_bytes_startup 3 // this is used if OPTION_WINKEY_DISCARD_BYTES_AT_STARTUP is enabled above +#define winkey_xoff_threshold 20 // the number of chars in the buffer when we begin sending XOFFs +#define winkey_xon_threshold 10 // the number of chars in the buffer below which we deactivate XOFF +#define default_memory_repeat_time 3000 // time in milliseconds +#define LCD_COLUMNS 16 +#define LCD_ROWS 2 +#define lcd_i2c_address_mathertel_PCF8574 0x27 // I2C address of display for FEATURE_LCD_MATHERTEL_PCF8574 +#define lcd_i2c_address_fdebrander_lcd 0x27 // I2C address of display for FEATURE_LCD_I2C_FDEBRABANDER +#define lcd_i2c_address_ydv1_lcd 0x27 // I2C address of display for FEATURE_LCD_YDv1 +//#define lcd_i2c_address_ydv1_lcd 0x38 // I2C address of display for FEATURE_LCD_YDv1 +#define lcd_i2c_address_sainsmart_lcd 0x27 // I2C address of display for FEATURE_LCD_SAINSMART_I2C +#define hell_pixel_microseconds 4025 +#define program_memory_limit_consec_spaces 1 +#define serial_leading_zeros 1 // set to 1 to activate leading zeros in serial numbers (i.e. #1 = 001) +#define serial_cut_numbers 0 // set to 1 to activate cut numbers in serial numbers (i.e. #10 = 1T, #19 = 1N) +#define go_to_sleep_inactivity_time 10 // minutes - FEATURE_SLEEP +#define dim_backlight_inactive_time 5 // minutes - FEATURE_LCD_BACKLIGHT_AUTO_DIM +#define default_cmos_super_keyer_iambic_b_timing_percent 33 // use with FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING; should be between 0 to 99 % (0% = true iambic b;100% = iambic a behavior) +#define default_cw_echo_timing_factor 1.75 // "factory default" setting +#define default_autospace_timing_factor 2.0 // "factory default" setting +#define winkey_paddle_echo_buffer_decode_timing_factor 0.25 +#define potentiometer_always_on 0 +#define ptt_interlock_check_every_ms 100 +#define ptt_interlock_active_state HIGH +#define unknown_cw_character '*' +#define cli_paddle_echo_on_at_boot 1 +#define cli_straight_key_echo_on_at_boot 1 +#define tx_key_dit_and_dah_pins_active_state HIGH +#define tx_key_dit_and_dah_pins_inactive_state LOW +#define potentiometer_check_interval_ms 0 //=====150 +#define potentiometer_reading_threshold 1 +#define default_paddle_interruption_quiet_time_element_lengths 0 +#define default_wordsworth_wordspace 6 +#define default_wordsworth_repetition 1 +#define serial_program_memory_buffer_size 500 +#define eeprom_write_time_ms 30000 + +#ifdef FEATURE_BUTTONS + #define analog_buttons_number_of_buttons 4 + #define analog_buttons_r1 10 + #define analog_buttons_r2 1 +#endif + + +#if defined(FEATURE_BUTTONS) && !defined(FEATURE_PS2_KEYBOARD) && !defined(FEATURE_USB_KEYBOARD) && !defined(FEATURE_COMMAND_LINE_INTERFACE) && !defined(FEATURE_WINKEY_EMULATION) + #define number_of_memories byte(analog_buttons_number_of_buttons-1) +#else + #define number_of_memories byte(12) +#endif + +#ifdef FEATURE_CAPACITIVE_PADDLE_PINS + #define capacitance_threshold 2 +#endif //FEATURE_CAPACITIVE_PADDLE_PINS + +#ifdef FEATURE_LED_RING + #define led_ring_low_limit 10 + #define led_ring_high_limit 50 +#endif //FEATURE_LED_RING + +#ifdef FEATURE_QLF + #define qlf_dit_max 125 + #define qlf_dit_min 75 + #define qlf_dah_max 200 + #define qlf_dah_min 100 + #define qlf_on_by_default 0 +#endif //FEATURE_QLF + + +#ifdef FEATURE_WINKEY_EMULATION + #if defined(OPTION_WINKEY_UCXLOG_9600_BAUD) || defined(FEATURE_SO2R_BASE) + #define WINKEY_DEFAULT_BAUD 9600 + #else + #define WINKEY_DEFAULT_BAUD 1200 + #endif //OPTION_WINKEY_UCXLOG_9600_BAUD || FEATURE_SO2R_BASE +// alter these below to map alternate sidetones for Winkey interface protocol emulation +#ifdef OPTION_WINKEY_2_SUPPORT + #define WINKEY_SIDETONE_1 3759 + #define WINKEY_SIDETONE_2 1879 + #define WINKEY_SIDETONE_3 1252 + #define WINKEY_SIDETONE_4 940 + #define WINKEY_SIDETONE_5 752 + #define WINKEY_SIDETONE_6 625 + #define WINKEY_SIDETONE_7 535 + #define WINKEY_SIDETONE_8 469 + #define WINKEY_SIDETONE_9 417 + #define WINKEY_SIDETONE_10 375 +#else //OPTION_WINKEY_2_SUPPORT + #define WINKEY_SIDETONE_1 4000 + #define WINKEY_SIDETONE_2 2000 + #define WINKEY_SIDETONE_3 1333 + #define WINKEY_SIDETONE_4 1000 + #define WINKEY_SIDETONE_5 800 + #define WINKEY_SIDETONE_6 666 + #define WINKEY_SIDETONE_7 571 + #define WINKEY_SIDETONE_8 500 + #define WINKEY_SIDETONE_9 444 + #define WINKEY_SIDETONE_10 400 +#endif //OPTION_WINKEY_2_SUPPORT + +#define WINKEY_1_REPORT_VERSION_NUMBER 10 +#define WINKEY_2_REPORT_VERSION_NUMBER 23 + +// alter these to map to alternate hang time wordspace units +#define WINKEY_HANG_TIME_1_0 1.0 +#define WINKEY_HANG_TIME_1_33 1.33 +#define WINKEY_HANG_TIME_1_66 1.66 +#define WINKEY_HANG_TIME_2_0 2.0 + +#define WINKEY_RETURN_THIS_FOR_ADMIN_GET_CAL 0x16 +#define WINKEY_RETURN_THIS_FOR_ADMIN_PADDLE_A2D 0xEE +#define WINKEY_RETURN_THIS_FOR_ADMIN_SPEED_A2D 0x00 + +#endif //FEATURE_WINKEY_EMULATION + + + +#define PRIMARY_SERIAL_PORT &Serial +#define PRIMARY_SERIAL_PORT_BAUD 115200 // This applies only when the port is in Command Line Interface mode. In Winkey mode it will default to 1200. + +#ifdef FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT + #define SECONDARY_SERIAL_PORT &Serial1 + #define SECONDARY_SERIAL_PORT_BAUD 115200 +#endif + + +#define CW_DECODER_SCREEN_COLUMNS 120 // word wrap at this many columns +#define CW_DECODER_SPACE_PRINT_THRESH 4.5 // print space if no tone for this many element lengths +#define CW_DECODER_SPACE_DECODE_THRESH 2.0 // invoke character decode if no tone for this many element lengths +#define CW_DECODER_NOISE_FILTER 20 // ignore elements shorter than this (mS) + +#define STRAIGHT_KEY_ACTIVE_STATE LOW + +#ifdef FEATURE_DYNAMIC_DAH_TO_DIT_RATIO + #define DYNAMIC_DAH_TO_DIT_RATIO_LOWER_LIMIT_WPM 30 + #define DYNAMIC_DAH_TO_DIT_RATIO_LOWER_LIMIT_RATIO 300 // 300 = 3:1 ratio + #define DYNAMIC_DAH_TO_DIT_RATIO_UPPER_LIMIT_WPM 70 + #define DYNAMIC_DAH_TO_DIT_RATIO_UPPER_LIMIT_RATIO 240 // 240 = 2.4:1 ratio +#endif //FEATURE_DYNAMIC_DAH_TO_DIT_RATIO + +#if defined(FEATURE_COMPETITION_COMPRESSION_DETECTION) + #define COMPETITION_COMPRESSION_DETECTION_ARRAY_SIZE 16 + #define COMPETITION_COMPRESSION_DETECTION_TIME_INTERCHAR_LOWER_LIMIT 1 + #define COMPETITION_COMPRESSION_DETECTION_TIME_INTERCHAR_UPPER_LIMIT 6.0 + #define COMPETITION_COMPRESSION_DETECTION_AVERAGE_ALARM_THRESHOLD 3.0 +#endif //FEATURE_COMPETITION_COMPRESSION_DETECTION + +#if defined(FEATURE_SLEEP) + #define KEYER_AWAKE_PIN_AWAKE_STATE HIGH + #define KEYER_AWAKE_PIN_ASLEEP_STATE LOW +#endif + +#if defined(FEATURE_LCD_BACKLIGHT_AUTO_DIM) + #define keyer_power_led_awake_duty 255 // PWM duty cycle. 0 is 0%, 255 is 100% + #define keyer_power_led_asleep_duty 25 // 25 is quite dim. Use 0 for off +#endif + +#if defined(FEATURE_ETHERNET) + // #define FEATURE_ETHERNET_IP {192,168,1,178} // default IP address ("192.168.1.178") + // #define FEATURE_ETHERNET_MAC {0xDE,0xAD,0xBE,0xEF,0xFE,0xED} + #define FEATURE_ETHERNET_IP {192,168,1,179} // default IP address ("192.168.1.179") + #define FEATURE_ETHERNET_MAC {0xDE,0xAD,0xBE,0xEF,0xFE,0xEE} + #define FEATURE_ETHERNET_DNS {8,8,8,8} + + #define FEATURE_ETHERNET_GATEWAY {192,168,1,1} // default gateway + #define FEATURE_ETHERNET_SUBNET_MASK {255,255,255,0} // default subnet mask + #define FEATURE_ETHERNET_WEB_LISTENER_PORT 80 + #define FEATURE_UDP_SEND_BUFFER_SIZE 128 + #define FEATURE_UDP_RECEIVE_BUFFER_SIZE 128 +#endif //FEATURE_ETHERNET + +#define WEB_SERVER_CONTROL_TX_KEY_TIME_LIMIT_SECS 10 + +#define FEATURE_INTERNET_LINK_MAX_LINKS 2 +#define FEATURE_INTERNET_LINK_DEFAULT_RCV_UDP_PORT 8888 +#define FEATURE_INTERNET_LINK_BUFFER_TIME_MS 500 + +#if defined(FEATURE_4x4_KEYPAD)|| defined (FEATURE_3x4_KEYPAD) + #define KEYPAD_ROWS 4 //KeyPad Rows + #if defined(FEATURE_4x4_KEYPAD) + #define KEYPAD_COLS 4 //keypad Columns + #else + #define KEYPAD_COLS 3 + #endif + #define mem1 0 //Memory numbers for Keypad: Actual memory numbers start with "0" + #define mem2 1 + #define mem3 2 + #define mem4 3 + #define mem5 4 + #define mem6 5 + #define mem7 6 + #define mem8 7 + #define mem9 8 + #define mem10 9 + #define mem11 10 + #define mem12 11 +#endif //#if defined(FEATURE_4x4_KEYPAD)|| defined (FEATURE_3x4_KEYPAD) + +#define initial_sidetone_mode 1 // Sidetone mode, 0=OFF, 1=ON, 2=PADDLE_ONLY + +#define sd_card_spi_ss_line 4 + + +#if defined(OPTION_DFROBOT_LCD_COMMAND_BUTTONS) + + // For V1.1 board use these values + #define dfrobot_btnRIGHT_analog 50 + #define dfrobot_btnUP_analog 250 + #define dfrobot_btnDOWN_analog 450 + #define dfrobot_btnLEFT_analog 650 + #define dfrobot_btnSELECT_analog 850 + + // For V1.0 board use these values + // #define dfrobot_btnRIGHT_analog 50 + // #define dfrobot_btnUP_analog 195 + // #define dfrobot_btnDOWN_analog 380 + // #define dfrobot_btnLEFT_analog 555 + // #define dfrobot_btnSELECT_analog 790 + + // button to memory mappings (0 = command button, 1 = memory 1, 2 = memory 2, etc.) + #define dfrobot_btnRIGHT 2 + #define dfrobot_btnUP 1 + #define dfrobot_btnDOWN 3 + #define dfrobot_btnLEFT 4 + #define dfrobot_btnSELECT 0 + #define dfrobot_btnNONE 255 // do not change + +#endif + + +#define sequencer_pins_active_state HIGH +#define sequencer_pins_inactive_state LOW +#define ptt_line_active_state HIGH +#define ptt_line_inactive_state LOW +#define tx_key_line_active_state HIGH +#define tx_key_line_inactive_state LOW +#define ptt_input_pin_active_state LOW +#define ptt_input_pin_inactive_state HIGH +#define tx_inhibit_pin_active_state LOW +#define tx_inhibit_pin_inactive_state HIGH +#define tx_pause_pin_active_state LOW +#define tx_pause_pin_inactive_state HIGH +#define sidetone_line_active_state HIGH +#define sidetone_line_inactive_state LOW + +#if defined(ARDUINO_MAPLE_MINI) + #define button_value_factor 4095 +#else + #define button_value_factor 1023 +#endif + +#define farnsworth_timing_calibration 1.15 + +#define sidetone_volume_low_limit 10 +#define sidetone_volume_high_limit 500 + +#ifdef FEATURE_SO2R_BASE + #ifdef FEATURE_POTENTIOMETER + #ifdef FEATURE_SO2R_SWITCHES + #ifdef FEATURE_SO2R_ANTENNAS + #define SO2R_DEVICE_NAME "SO2R Mini PSA" + #else + #define SO2R_DEVICE_NAME "SO2R Mini PS" + #endif + #else + #ifdef FEATURE_SO2R_ANTENNAS + #define SO2R_DEVICE_NAME "SO2R Mini PA" + #else + #define SO2R_DEVICE_NAME "SO2R Mini P" + #endif + #endif + #else + #ifdef FEATURE_SO2R_SWITCHES + #ifdef FEATURE_SO2R_ANTENNAS + #define SO2R_DEVICE_NAME "SO2R Mini SA" + #else + #define SO2R_DEVICE_NAME "SO2R Mini S" + #endif + #else + #ifdef FEATURE_SO2R_ANTENNAS + #define SO2R_DEVICE_NAME "SO2R Mini A" + #else + #define SO2R_DEVICE_NAME "SO2R Mini" + #endif + #endif + #endif +#endif + +#define custom_startup_field "your custom text here" // an example could be callsign and name, eg. "AB1XYZ Bob", (or "Worlds best operator" which requires a 20 column display), string length shouldo be no more than the number of columns on the display + +#define command_mode_acknowledgement_character 'E' + +#if defined(FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT) + #define command_a_iambic_a "A" + #define command_b_iambic_b "B" + #define command_c_single_paddle "SINGLE" + #define command_d_ultimatic "ULT" + #define command_h_weight_dit_dah_ratio_default "R" + #define command_i_tx_on "TX ON" + #define command_i_tx_off "TX OFF" + #define command_k_dit_dah_buffers_on "ON" + #define command_k_dit_dah_buffers_off "OFF" + #define command_n_paddle_reverse "REV" + #define command_n_paddle_normal "NORM" + #define command_o_sidetone_off "ST OFF" + #define command_o_sidetone_paddle_only "ST PD ONLY" + #define command_o_sidetone_on "ST ON" + #define command_t_tune_mode "TUNE" + #define command_v_potentiometer_on "POT ON" + #define command_v_potentiometer_off "POT OFF" + + #define command_error "ERR" + +#endif //FEATURE_COMMAND_MODE_ENHANCED_CMD_ACKNOWLEDGEMENT + diff --git a/k3ng_keyer/keyer_stm32duino.h b/k3ng_keyer/keyer_stm32duino.h new file mode 100755 index 0000000..f6b6ce9 --- /dev/null +++ b/k3ng_keyer/keyer_stm32duino.h @@ -0,0 +1,39 @@ +/* keyer_stm32fduino.h + + + Use this file to enables clean compilation using the STM32duino board support for STM32 ARM + processors. This requires the installation of https://github.com/rogerclarkmelbourne/Arduino_STM32 + + For more information about STM32duino visit the primary support forum: http://www.stm32duino.com/ + +*/ + + void initialize_pins(void); + void initialize_keyer_state(void); + void initialize_potentiometer(void); + void initialize_rotary_encoder(void); + void initialize_default_modes(void); + void initialize_watchdog(void); + void initialize_ethernet_variables(void); + void check_eeprom_for_initialization(void); + void check_for_beacon_mode(void); + void check_for_debug_modes(void); + void initialize_analog_button_array(void); + void initialize_serial_ports(void); + void initialize_ps2_keyboard(void); + void initialize_usb(void); + void initialize_cw_keyboard(void); + void initialize_display(void); + void initialize_ethernet(void); + void initialize_udp(void); + void initialize_web_server(void); + void initialize_debug_startup(void); + + void check_paddles(void); + void service_dit_dah_buffers(void); + void service_send_buffer(byte); + void check_ptt_tail(void); + void check_for_dirty_configuration(void); + void tone(uint32_t, uint32_t, uint32_t); + void noTone(uint32_t); + void serial_status(PRIMARY_SERIAL_CLS*); diff --git a/k3ng_keyer/keyer_training_text_czech.h b/k3ng_keyer/keyer_training_text_czech.h new file mode 100755 index 0000000..488fb54 --- /dev/null +++ b/k3ng_keyer/keyer_training_text_czech.h @@ -0,0 +1,266 @@ +// keyer_training_text_czech.h +// Czech word list contributed by Martin, OK1RR +// English word list originally from gen_cw_words.pl - a program that generates words in a random order, +// intended to be used during morse code practice. +// Original gen_cw_words.pl code Copyright (C) 2015,2017 Andy Stewart (KB1OIQ) +// https://sourceforge.net/projects/kb1oiq-k1ig-wordsworth/ + +// two letter word array +const char s2_1[] PROGMEM = "DO"; +const char s2_2[] PROGMEM = "NA"; +const char s2_3[] PROGMEM = "PO"; +const char s2_4[] PROGMEM = "MY"; +const char s2_5[] PROGMEM = "TO"; +const char s2_6[] PROGMEM = "NE"; +const char s2_7[] PROGMEM = "VY"; +const char s2_8[] PROGMEM = "ZE"; +const char s2_9[] PROGMEM = "VE"; +const char s2_10[] PROGMEM = "JA"; +const char s2_11[] PROGMEM = "TY"; +const char s2_12[] PROGMEM = "BY"; +const char s2_13[] PROGMEM = "ON"; +const char s2_14[] PROGMEM = "SE"; +const char s2_15[] PROGMEM = "AZ"; +const char s2_16[] PROGMEM = "TU"; +const char s2_17[] PROGMEM = "TI"; +const char s2_18[] PROGMEM = "ZA"; +const char s2_19[] PROGMEM = "CI"; +const char s2_20[] PROGMEM = "CO"; +const char s2_21[] PROGMEM = "JI"; +const char s2_22[] PROGMEM = "MU"; +const char s2_23[] PROGMEM = "JE"; +const char s2_24[] PROGMEM = "UZ"; +const byte s2_size = 24; +const char* const s2_table[] PROGMEM = + {s2_1,s2_2,s2_3,s2_4,s2_5,s2_6,s2_7,s2_8,s2_9,s2_10, + s2_11,s2_12,s2_13,s2_14,s2_15,s2_16,s2_17,s2_18,s2_19,s2_20, + s2_21,s2_22,s2_23,s2_24}; + +// three letter word array +const char s3_1[] PROGMEM = "DVA"; +const char s3_2[] PROGMEM = "TRI"; +const char s3_3[] PROGMEM = "MUJ"; +const char s3_4[] PROGMEM = "NAS"; +const char s3_5[] PROGMEM = "ONA"; +const char s3_6[] PROGMEM = "ONI"; +const char s3_7[] PROGMEM = "ONO"; +const char s3_8[] PROGMEM = "VAS"; +const char s3_9[] PROGMEM = "VSE"; +const char s3_10[] PROGMEM = "POD"; +const char s3_11[] PROGMEM = "NAD"; +const char s3_12[] PROGMEM = "KDE"; +const char s3_13[] PROGMEM = "KDY"; +const char s3_14[] PROGMEM = "KAM"; +const char s3_15[] PROGMEM = "KUS"; +const char s3_16[] PROGMEM = "DEN"; +const char s3_17[] PROGMEM = "TAM"; +const char s3_18[] PROGMEM = "DAT"; +const char s3_19[] PROGMEM = "DAM"; +const char s3_20[] PROGMEM = "DAS"; +const char s3_21[] PROGMEM = "MIT"; +const char s3_22[] PROGMEM = "JIT"; +const char s3_23[] PROGMEM = "NEZ"; +const char s3_24[] PROGMEM = "ROK"; +const char s3_25[] PROGMEM = "PAK"; +const char s3_26[] PROGMEM = "DIK"; +const char s3_27[] PROGMEM = "DUM"; +const char s3_28[] PROGMEM = "PRO"; +const char s3_29[] PROGMEM = "PRI"; +const char s3_30[] PROGMEM = "OKO"; +const char s3_31[] PROGMEM = "CAS"; +const char s3_32[] PROGMEM = "CAJ"; +const char s3_33[] PROGMEM = "CIL"; +const char s3_34[] PROGMEM = "COP"; +const char s3_35[] PROGMEM = "MUZ"; +const char s3_36[] PROGMEM = "ASI"; +const char s3_37[] PROGMEM = "BEZ"; +const char s3_38[] PROGMEM = "BOJ"; +const char s3_39[] PROGMEM = "BUH"; +const byte s3_size = 39; +const char* const s3_table[] PROGMEM = + {s3_1,s3_2,s3_3,s3_4,s3_5,s3_6,s3_7,s3_8,s3_9,s3_10, + s3_11,s3_12,s3_13,s3_14,s3_15,s3_16,s3_17,s3_18,s3_19,s3_20, + s3_21,s3_22,s3_23,s3_24,s3_25,s3_26,s3_27,s3_28,s3_29,s3_30, + s3_31,s3_32,s3_33,s3_34,s3_35,s3_36,s3_37,s3_38,s3_39}; + +// four letter word array +const char s4_1[] PROGMEM = "TATO"; +const char s4_2[] PROGMEM = "TITO"; +const char s4_3[] PROGMEM = "TOTO"; +const char s4_4[] PROGMEM = "DOMA"; +const char s4_5[] PROGMEM = "STAT"; +const char s4_6[] PROGMEM = "TADY"; +const char s4_7[] PROGMEM = "DOBA"; +const char s4_8[] PROGMEM = "TAKE"; +const char s4_9[] PROGMEM = "PRED"; +const char s4_10[] PROGMEM = "SVET"; +const char s4_11[] PROGMEM = "BRZY"; +const char s4_12[] PROGMEM = "TVUJ"; +const char s4_13[] PROGMEM = "TATA"; +const char s4_14[] PROGMEM = "MAMA"; +const char s4_15[] PROGMEM = "PSIK"; +const char s4_16[] PROGMEM = "ZENA"; +const char s4_17[] PROGMEM = "DITE"; +const char s4_18[] PROGMEM = "DETI"; +const char s4_19[] PROGMEM = "KONE"; +const char s4_20[] PROGMEM = "DUHA"; +const char s4_21[] PROGMEM = "RUKA"; +const char s4_22[] PROGMEM = "NOHA"; +const char s4_23[] PROGMEM = "PRST"; +const char s4_24[] PROGMEM = "POLE"; +const char s4_25[] PROGMEM = "MORE"; +const char s4_26[] PROGMEM = "VITR"; +const char s4_27[] PROGMEM = "MLHA"; +const char s4_28[] PROGMEM = "VODA"; +const char s4_29[] PROGMEM = "VAHA"; +const char s4_30[] PROGMEM = "PIVO"; +const char s4_31[] PROGMEM = "HRAT"; +const char s4_32[] PROGMEM = "RADA"; +const char s4_33[] PROGMEM = "PANI"; +const byte s4_size = 33; +const char* const s4_table[] PROGMEM = + {s4_1,s4_2,s4_3,s4_4,s4_5,s4_6,s4_7,s4_8,s4_9,s4_10, + s4_11,s4_12,s4_13,s4_14,s4_15,s4_16,s4_17,s4_18,s4_19,s4_20, + s4_21,s4_22,s4_23,s4_24,s4_25,s4_26,s4_27,s4_28,s4_29,s4_30, + s4_31,s4_32,s4_33}; + + + +const char name_1[] PROGMEM = "ANDY"; +const char name_2[] PROGMEM = "JOSEF"; +const char name_3[] PROGMEM = "JIRI"; +const char name_4[] PROGMEM = "KAREL"; +const char name_5[] PROGMEM = "HONZA"; +const char name_6[] PROGMEM = "FRANTA"; +const char name_7[] PROGMEM = "PETR"; +const char name_8[] PROGMEM = "PAVEL"; +const char name_9[] PROGMEM = "JINDRA"; +const char name_10[] PROGMEM = "FILIP"; +const char name_11[] PROGMEM = "MILAN"; +const char name_12[] PROGMEM = "MARTIN"; +const char name_13[] PROGMEM = "DAVID"; +const char name_14[] PROGMEM = "TOMAS"; +const char name_15[] PROGMEM = "HANKA"; +const char name_16[] PROGMEM = "SASA"; +const char name_17[] PROGMEM = "IVAN"; +const char name_18[] PROGMEM = "BERT"; +const char name_19[] PROGMEM = "VASEK"; +const char name_20[] PROGMEM = "ALAN"; +const char name_21[] PROGMEM = "JIRKA"; +const char name_22[] PROGMEM = "JERRY"; +const char name_23[] PROGMEM = "PEPA"; +const char name_24[] PROGMEM = "JIRINA"; +const char name_25[] PROGMEM = "RISA"; +const char name_26[] PROGMEM = "DAN"; +const char name_27[] PROGMEM = "LUBOS"; +const char name_28[] PROGMEM = "TONDA"; +const char name_29[] PROGMEM = "LADA"; +const char name_30[] PROGMEM = "VLADA"; +const char name_31[] PROGMEM = "JENDA"; +const char name_32[] PROGMEM = "CYRIL"; +const char name_33[] PROGMEM = "BOHOUS"; +const char name_34[] PROGMEM = "KAMIL"; +const char name_35[] PROGMEM = "RENE"; +const char name_36[] PROGMEM = "PATRIK"; +const char name_37[] PROGMEM = "SIMON"; +const char name_38[] PROGMEM = "ALEX"; +const char name_39[] PROGMEM = "LOJZA"; +const char name_40[] PROGMEM = "MATEJ"; +const char name_41[] PROGMEM = "FERDA"; +const char name_42[] PROGMEM = "MARIE"; +const char name_43[] PROGMEM = "ZDENEK"; +const char name_44[] PROGMEM = "JULEK"; +const char name_45[] PROGMEM = "STANDA"; +const char name_46[] PROGMEM = "SLAVEK"; +const char name_47[] PROGMEM = "VILDA"; +const char name_48[] PROGMEM = "VERA"; +const char name_49[] PROGMEM = "GUSTA"; +const byte name_size = 49; +const char* const name_table[] PROGMEM = + {name_1,name_2,name_3,name_4,name_5,name_6,name_7,name_8,name_9,name_10, + name_11,name_12,name_13,name_14,name_15,name_16,name_17,name_18,name_19,name_20, + name_21,name_22,name_23,name_24,name_25,name_26,name_27,name_28,name_29,name_30, + name_31,name_32,name_33,name_34,name_35,name_36,name_37,name_38,name_39,name_40, + name_41,name_42,name_43,name_44,name_45,name_46,name_47,name_48,name_49}; + +// CW QSO word array +const char qso_1[] PROGMEM = "QRL?"; +const char qso_2[] PROGMEM = "QRM"; +const char qso_3[] PROGMEM = "QRN"; +const char qso_4[] PROGMEM = "QRS"; +const char qso_5[] PROGMEM = "QRT"; +const char qso_6[] PROGMEM = "QRZ"; +const char qso_7[] PROGMEM = "QSL"; +const char qso_8[] PROGMEM = "QSO"; +const char qso_9[] PROGMEM = "QSY"; +const char qso_10[] PROGMEM = "QTH"; +const char qso_11[] PROGMEM = "QRX"; +const char qso_12[] PROGMEM = "ABT"; +const char qso_13[] PROGMEM = "AGE"; +const char qso_14[] PROGMEM = "ANT"; +const char qso_15[] PROGMEM = "BEAM"; +const char qso_16[] PROGMEM = "BK"; +const char qso_17[] PROGMEM = "QRP"; +const char qso_18[] PROGMEM = "AGN"; +const char qso_19[] PROGMEM = "C"; +const char qso_20[] PROGMEM = "CL"; +const char qso_21[] PROGMEM = "CPY"; +const char qso_22[] PROGMEM = "CQ"; +const char qso_23[] PROGMEM = "CUL"; +const char qso_24[] PROGMEM = "DE"; +const char qso_25[] PROGMEM = "DX"; +const char qso_26[] PROGMEM = "ES"; +const char qso_27[] PROGMEM = "EL"; +const char qso_28[] PROGMEM = "FB"; +const char qso_29[] PROGMEM = "HI"; +const char qso_30[] PROGMEM = "HW?"; +const char qso_31[] PROGMEM = "HR"; +const char qso_32[] PROGMEM = "K"; +const char qso_33[] PROGMEM = "="; +const char qso_34[] PROGMEM = "<"; +const char qso_35[] PROGMEM = "%"; +const char qso_36[] PROGMEM = ">"; +const char qso_37[] PROGMEM = "LID"; +const char qso_38[] PROGMEM = "LOOP"; +const char qso_39[] PROGMEM = "NAME"; +const char qso_40[] PROGMEM = "OM"; +const char qso_41[] PROGMEM = "OP"; +const char qso_42[] PROGMEM = "PKT"; +const char qso_43[] PROGMEM = "PSE"; +const char qso_44[] PROGMEM = "R"; +const char qso_45[] PROGMEM = "RPT"; +const char qso_46[] PROGMEM = "RST"; +const char qso_47[] PROGMEM = "RIG"; +const char qso_48[] PROGMEM = "TEMP"; +const char qso_49[] PROGMEM = "TEST"; +const char qso_50[] PROGMEM = "TU"; +const char qso_51[] PROGMEM = "TKS"; +const char qso_52[] PROGMEM = "TNX"; +const char qso_53[] PROGMEM = "VERT"; +const char qso_54[] PROGMEM = "WATT"; +const char qso_55[] PROGMEM = "WX"; +const char qso_56[] PROGMEM = "YAGI"; +const char qso_57[] PROGMEM = "YRS"; +const char qso_58[] PROGMEM = "73"; +const char qso_59[] PROGMEM = "88"; +const char qso_60[] PROGMEM = "?"; +const char qso_61[] PROGMEM = "/"; +const char qso_62[] PROGMEM = "VY"; +const char qso_63[] PROGMEM = "YL"; +const char qso_64[] PROGMEM = "XYL"; +const char qso_65[] PROGMEM = "MY"; +const char qso_66[] PROGMEM = "UR"; +const char qso_67[] PROGMEM = "IS"; +const char qso_68[] PROGMEM = "QSB"; +const char qso_69[] PROGMEM = "QRQ"; +const char qso_70[] PROGMEM = "HVE"; +const char qso_71[] PROGMEM = "HPE"; +const char qso_72[] PROGMEM = "BEST"; +const byte qso_size = 72; +const char* const qso_table[] PROGMEM = + {qso_1,qso_2,qso_3,qso_4,qso_5,qso_6,qso_7,qso_8,qso_9,qso_10,qso_11,qso_12,qso_13,qso_14,qso_15,qso_16,qso_17,qso_18,qso_19,qso_20, + qso_21,qso_22,qso_23,qso_24,qso_25,qso_26,qso_27,qso_28,qso_29,qso_30,qso_31,qso_32,qso_33,qso_34,qso_35,qso_36,qso_37,qso_38,qso_39,qso_40, + qso_41,qso_42,qso_43,qso_44,qso_45,qso_46,qso_47,qso_48,qso_49,qso_50,qso_51,qso_52,qso_53,qso_54,qso_55,qso_56,qso_57,qso_58,qso_59,qso_60, + qso_61,qso_62,qso_63,qso_64,qso_65,qso_66,qso_67,qso_68,qso_69,qso_70,qso_71,qso_72}; + + diff --git a/k3ng_keyer/keyer_training_text_english.h b/k3ng_keyer/keyer_training_text_english.h new file mode 100755 index 0000000..bb9f35c --- /dev/null +++ b/k3ng_keyer/keyer_training_text_english.h @@ -0,0 +1,264 @@ +// English word list originally from gen_cw_words.pl - a program that generates words in a random order, +// intended to be used during morse code practice. +// Original gen_cw_words.pl code Copyright (C) 2015,2017 Andy Stewart (KB1OIQ) +// https://sourceforge.net/projects/kb1oiq-k1ig-wordsworth/ + +// two letter word array +const char s2_1[] PROGMEM = "TO"; +const char s2_2[] PROGMEM = "IN"; +const char s2_3[] PROGMEM = "IT"; +const char s2_4[] PROGMEM = "IS"; +const char s2_5[] PROGMEM = "BE"; +const char s2_6[] PROGMEM = "AS"; +const char s2_7[] PROGMEM = "AT"; +const char s2_8[] PROGMEM = "SO"; +const char s2_9[] PROGMEM = "WE"; +const char s2_10[] PROGMEM = "HE"; +const char s2_11[] PROGMEM = "BY"; +const char s2_12[] PROGMEM = "OR"; +const char s2_13[] PROGMEM = "ON"; +const char s2_14[] PROGMEM = "DO"; +const char s2_15[] PROGMEM = "IF"; +const char s2_16[] PROGMEM = "ME"; +const char s2_17[] PROGMEM = "MY"; +const char s2_18[] PROGMEM = "UP"; +const char s2_19[] PROGMEM = "AN"; +const char s2_20[] PROGMEM = "GO"; +const char s2_21[] PROGMEM = "NO"; +const char s2_22[] PROGMEM = "US"; +const char s2_23[] PROGMEM = "AM"; +const char s2_24[] PROGMEM = "OF"; +const byte s2_size = 24; +const char* const s2_table[] PROGMEM = + {s2_1,s2_2,s2_3,s2_4,s2_5,s2_6,s2_7,s2_8,s2_9,s2_10, + s2_11,s2_12,s2_13,s2_14,s2_15,s2_16,s2_17,s2_18,s2_19,s2_20, + s2_21,s2_22,s2_23,s2_24}; + +// three letter word array +const char s3_1[] PROGMEM = "THE"; +const char s3_2[] PROGMEM = "AND"; +const char s3_3[] PROGMEM = "FOR"; +const char s3_4[] PROGMEM = "ARE"; +const char s3_5[] PROGMEM = "BUT"; +const char s3_6[] PROGMEM = "NOT"; +const char s3_7[] PROGMEM = "YOU"; +const char s3_8[] PROGMEM = "ALL"; +const char s3_9[] PROGMEM = "ANY"; +const char s3_10[] PROGMEM = "CAN"; +const char s3_11[] PROGMEM = "HAD"; +const char s3_12[] PROGMEM = "HER"; +const char s3_13[] PROGMEM = "WAS"; +const char s3_14[] PROGMEM = "ONE"; +const char s3_15[] PROGMEM = "OUR"; +const char s3_16[] PROGMEM = "OUT"; +const char s3_17[] PROGMEM = "DAY"; +const char s3_18[] PROGMEM = "GET"; +const char s3_19[] PROGMEM = "HAS"; +const char s3_20[] PROGMEM = "HIM"; +const char s3_21[] PROGMEM = "HIS"; +const char s3_22[] PROGMEM = "HOW"; +const char s3_23[] PROGMEM = "MAN"; +const char s3_24[] PROGMEM = "NEW"; +const char s3_25[] PROGMEM = "NOW"; +const char s3_26[] PROGMEM = "OLD"; +const char s3_27[] PROGMEM = "SEE"; +const char s3_28[] PROGMEM = "TWO"; +const char s3_29[] PROGMEM = "WAY"; +const char s3_30[] PROGMEM = "WHO"; +const char s3_31[] PROGMEM = "BOY"; +const char s3_32[] PROGMEM = "DID"; +const char s3_33[] PROGMEM = "ITS"; +const char s3_34[] PROGMEM = "LET"; +const char s3_35[] PROGMEM = "PUT"; +const char s3_36[] PROGMEM = "SAY"; +const char s3_37[] PROGMEM = "SHE"; +const char s3_38[] PROGMEM = "TOO"; +const char s3_39[] PROGMEM = "USE"; +const byte s3_size = 39; +const char* const s3_table[] PROGMEM = + {s3_1,s3_2,s3_3,s3_4,s3_5,s3_6,s3_7,s3_8,s3_9,s3_10, + s3_11,s3_12,s3_13,s3_14,s3_15,s3_16,s3_17,s3_18,s3_19,s3_20, + s3_21,s3_22,s3_23,s3_24,s3_25,s3_26,s3_27,s3_28,s3_29,s3_30, + s3_31,s3_32,s3_33,s3_34,s3_35,s3_36,s3_37,s3_38,s3_39}; + +// four letter word array +const char s4_1[] PROGMEM = "THAT"; +const char s4_2[] PROGMEM = "WITH"; +const char s4_3[] PROGMEM = "HAVE"; +const char s4_4[] PROGMEM = "THIS"; +const char s4_5[] PROGMEM = "WILL"; +const char s4_6[] PROGMEM = "YOUR"; +const char s4_7[] PROGMEM = "FROM"; +const char s4_8[] PROGMEM = "THEY"; +const char s4_9[] PROGMEM = "KNOW"; +const char s4_10[] PROGMEM = "WANT"; +const char s4_11[] PROGMEM = "BEEN"; +const char s4_12[] PROGMEM = "GOOD"; +const char s4_13[] PROGMEM = "MUCH"; +const char s4_14[] PROGMEM = "SOME"; +const char s4_15[] PROGMEM = "TIME"; +const char s4_16[] PROGMEM = "VERY"; +const char s4_17[] PROGMEM = "WHEN"; +const char s4_18[] PROGMEM = "COME"; +const char s4_19[] PROGMEM = "HERE"; +const char s4_20[] PROGMEM = "JUST"; +const char s4_21[] PROGMEM = "LIKE"; +const char s4_22[] PROGMEM = "LONG"; +const char s4_23[] PROGMEM = "MAKE"; +const char s4_24[] PROGMEM = "MANY"; +const char s4_25[] PROGMEM = "MORE"; +const char s4_26[] PROGMEM = "ONLY"; +const char s4_27[] PROGMEM = "OVER"; +const char s4_28[] PROGMEM = "SUCH"; +const char s4_29[] PROGMEM = "TAKE"; +const char s4_30[] PROGMEM = "THAN"; +const char s4_31[] PROGMEM = "THEM"; +const char s4_32[] PROGMEM = "WELL"; +const char s4_33[] PROGMEM = "WERE"; +const byte s4_size = 33; +const char* const s4_table[] PROGMEM = + {s4_1,s4_2,s4_3,s4_4,s4_5,s4_6,s4_7,s4_8,s4_9,s4_10, + s4_11,s4_12,s4_13,s4_14,s4_15,s4_16,s4_17,s4_18,s4_19,s4_20, + s4_21,s4_22,s4_23,s4_24,s4_25,s4_26,s4_27,s4_28,s4_29,s4_30, + s4_31,s4_32,s4_33}; + + + +const char name_1[] PROGMEM = "ANDY"; +const char name_2[] PROGMEM = "BOB"; +const char name_3[] PROGMEM = "BILL"; +const char name_4[] PROGMEM = "JOHN"; +const char name_5[] PROGMEM = "GEORGE"; +const char name_6[] PROGMEM = "TOM"; +const char name_7[] PROGMEM = "RICH"; +const char name_8[] PROGMEM = "STEVE"; +const char name_9[] PROGMEM = "TIM"; +const char name_10[] PROGMEM = "SCOTT"; +const char name_11[] PROGMEM = "FRED"; +const char name_12[] PROGMEM = "DOUG"; +const char name_13[] PROGMEM = "DAVE"; +const char name_14[] PROGMEM = "GARY"; +const char name_15[] PROGMEM = "JIM"; +const char name_16[] PROGMEM = "CHRIS"; +const char name_17[] PROGMEM = "MIKE"; +const char name_18[] PROGMEM = "ED"; +const char name_19[] PROGMEM = "AL"; +const char name_20[] PROGMEM = "ALAN"; +const char name_21[] PROGMEM = "RON"; +const char name_22[] PROGMEM = "JERRY"; +const char name_23[] PROGMEM = "ART"; +const char name_24[] PROGMEM = "GREG"; +const char name_25[] PROGMEM = "RICK"; +const char name_26[] PROGMEM = "DAN"; +const char name_27[] PROGMEM = "KEN"; +const char name_28[] PROGMEM = "TONY"; +const char name_29[] PROGMEM = "CARL"; +const char name_30[] PROGMEM = "PHIL"; +const char name_31[] PROGMEM = "JOE"; +const char name_32[] PROGMEM = "JON"; +const char name_33[] PROGMEM = "LARRY"; +const char name_34[] PROGMEM = "DON"; +const char name_35[] PROGMEM = "DICK"; +const char name_36[] PROGMEM = "PAUL"; +const char name_37[] PROGMEM = "SERGE"; +const char name_38[] PROGMEM = "ALEX"; +const char name_39[] PROGMEM = "VLAD"; +const char name_40[] PROGMEM = "JEFF"; +const char name_41[] PROGMEM = "CHAS"; +const char name_42[] PROGMEM = "JACK"; +const char name_43[] PROGMEM = "GENE"; +const char name_44[] PROGMEM = "BERT"; +const char name_45[] PROGMEM = "OLEG"; +const char name_46[] PROGMEM = "PETE"; +const char name_47[] PROGMEM = "PAT"; +const char name_48[] PROGMEM = "JUAN"; +const char name_49[] PROGMEM = "GUS"; +const byte name_size = 49; +const char* const name_table[] PROGMEM = + {name_1,name_2,name_3,name_4,name_5,name_6,name_7,name_8,name_9,name_10, + name_11,name_12,name_13,name_14,name_15,name_16,name_17,name_18,name_19,name_20, + name_21,name_22,name_23,name_24,name_25,name_26,name_27,name_28,name_29,name_30, + name_31,name_32,name_33,name_34,name_35,name_36,name_37,name_38,name_39,name_40, + name_41,name_42,name_43,name_44,name_45,name_46,name_47,name_48,name_49}; + +// CW QSO word array +const char qso_1[] PROGMEM = "QRL?"; +const char qso_2[] PROGMEM = "QRM"; +const char qso_3[] PROGMEM = "QRN"; +const char qso_4[] PROGMEM = "QRS"; +const char qso_5[] PROGMEM = "QRT"; +const char qso_6[] PROGMEM = "QRZ"; +const char qso_7[] PROGMEM = "QSL"; +const char qso_8[] PROGMEM = "QSO"; +const char qso_9[] PROGMEM = "QSY"; +const char qso_10[] PROGMEM = "QTH"; +const char qso_11[] PROGMEM = "QRX"; +const char qso_12[] PROGMEM = "ABT"; +const char qso_13[] PROGMEM = "AGE"; +const char qso_14[] PROGMEM = "ANT"; +const char qso_15[] PROGMEM = "BEAM"; +const char qso_16[] PROGMEM = "BK"; +const char qso_17[] PROGMEM = "QRP"; +const char qso_18[] PROGMEM = "AGN"; +const char qso_19[] PROGMEM = "C"; +const char qso_20[] PROGMEM = "CL"; +const char qso_21[] PROGMEM = "CPY"; +const char qso_22[] PROGMEM = "CQ"; +const char qso_23[] PROGMEM = "CUL"; +const char qso_24[] PROGMEM = "DE"; +const char qso_25[] PROGMEM = "DX"; +const char qso_26[] PROGMEM = "ES"; +const char qso_27[] PROGMEM = "EL"; +const char qso_28[] PROGMEM = "FB"; +const char qso_29[] PROGMEM = "HI"; +const char qso_30[] PROGMEM = "HW?"; +const char qso_31[] PROGMEM = "HR"; +const char qso_32[] PROGMEM = "K"; +const char qso_33[] PROGMEM = "="; +const char qso_34[] PROGMEM = "<"; +const char qso_35[] PROGMEM = "%"; +const char qso_36[] PROGMEM = ">"; +const char qso_37[] PROGMEM = "LID"; +const char qso_38[] PROGMEM = "LOOP"; +const char qso_39[] PROGMEM = "NAME"; +const char qso_40[] PROGMEM = "OM"; +const char qso_41[] PROGMEM = "OP"; +const char qso_42[] PROGMEM = "PKT"; +const char qso_43[] PROGMEM = "PSE"; +const char qso_44[] PROGMEM = "R"; +const char qso_45[] PROGMEM = "RPT"; +const char qso_46[] PROGMEM = "RST"; +const char qso_47[] PROGMEM = "RIG"; +const char qso_48[] PROGMEM = "TEMP"; +const char qso_49[] PROGMEM = "TEST"; +const char qso_50[] PROGMEM = "TU"; +const char qso_51[] PROGMEM = "TKS"; +const char qso_52[] PROGMEM = "TNX"; +const char qso_53[] PROGMEM = "VERT"; +const char qso_54[] PROGMEM = "WATT"; +const char qso_55[] PROGMEM = "WX"; +const char qso_56[] PROGMEM = "YAGI"; +const char qso_57[] PROGMEM = "YRS"; +const char qso_58[] PROGMEM = "73"; +const char qso_59[] PROGMEM = "88"; +const char qso_60[] PROGMEM = "?"; +const char qso_61[] PROGMEM = "/"; +const char qso_62[] PROGMEM = "VY"; +const char qso_63[] PROGMEM = "YL"; +const char qso_64[] PROGMEM = "XYL"; +const char qso_65[] PROGMEM = "MY"; +const char qso_66[] PROGMEM = "UR"; +const char qso_67[] PROGMEM = "IS"; +const char qso_68[] PROGMEM = "QSB"; +const char qso_69[] PROGMEM = "QRQ"; +const char qso_70[] PROGMEM = "HVE"; +const char qso_71[] PROGMEM = "HPE"; +const char qso_72[] PROGMEM = "BEST"; +const byte qso_size = 72; +const char* const qso_table[] PROGMEM = + {qso_1,qso_2,qso_3,qso_4,qso_5,qso_6,qso_7,qso_8,qso_9,qso_10,qso_11,qso_12,qso_13,qso_14,qso_15,qso_16,qso_17,qso_18,qso_19,qso_20, + qso_21,qso_22,qso_23,qso_24,qso_25,qso_26,qso_27,qso_28,qso_29,qso_30,qso_31,qso_32,qso_33,qso_34,qso_35,qso_36,qso_37,qso_38,qso_39,qso_40, + qso_41,qso_42,qso_43,qso_44,qso_45,qso_46,qso_47,qso_48,qso_49,qso_50,qso_51,qso_52,qso_53,qso_54,qso_55,qso_56,qso_57,qso_58,qso_59,qso_60, + qso_61,qso_62,qso_63,qso_64,qso_65,qso_66,qso_67,qso_68,qso_69,qso_70,qso_71,qso_72}; + + diff --git a/k3ng_keyer/keyer_training_text_norsk.h b/k3ng_keyer/keyer_training_text_norsk.h new file mode 100755 index 0000000..6e9d29c --- /dev/null +++ b/k3ng_keyer/keyer_training_text_norsk.h @@ -0,0 +1,248 @@ +// keyer_training_text_norsk.h +// Norwegian word list contributed by Karl, LA3FY +// English word list originally from gen_cw_words.pl - a program that generates words in a random order, +// intended to be used during morse code practice. +// Original gen_cw_words.pl code Copyright (C) 2015,2017 Andy Stewart (KB1OIQ) +// https://sourceforge.net/projects/kb1oiq-k1ig-wordsworth/ + +// two letter word array +const char s2_1[] PROGMEM = "OG"; +const char s2_2[] PROGMEM = "PÅ"; +const char s2_3[] PROGMEM = "GÅ"; +const char s2_4[] PROGMEM = "ER"; +const char s2_5[] PROGMEM = "EN"; +const char s2_6[] PROGMEM = "AV"; +const char s2_7[] PROGMEM = "AT"; +const char s2_8[] PROGMEM = "DE"; +const char s2_9[] PROGMEM = "ET"; +const char s2_10[] PROGMEM = "SÅ"; +const char s2_11[] PROGMEM = "VI"; +const char s2_12[] PROGMEM = "DU"; +const char s2_13[] PROGMEM = "DA"; +const char s2_14[] PROGMEM = "UT"; +const char s2_15[] PROGMEM = "SA"; +const char s2_16[] PROGMEM = "NÅ"; +const char s2_17[] PROGMEM = "NY"; +const char s2_18[] PROGMEM = "HA"; +const char s2_19[] PROGMEM = "ÅR"; +const char s2_20[] PROGMEM = "FÅ"; +const char s2_21[] PROGMEM = "TO"; +const char s2_22[] PROGMEM = "SE"; +const byte s2_size = 22; +const char* const s2_table[] PROGMEM = + {s2_1,s2_2,s2_3,s2_4,s2_5,s2_6,s2_7,s2_8,s2_9,s2_10, + s2_11,s2_12,s2_13,s2_14,s2_15,s2_16,s2_17,s2_18,s2_19,s2_20, + s2_21,s2_22}; + +// three letter word array +const char s3_1[] PROGMEM = "DET"; +const char s3_2[] PROGMEM = "SOM"; +const char s3_3[] PROGMEM = "HAN"; +const char s3_4[] PROGMEM = "FOR"; +const char s3_5[] PROGMEM = "MED"; +const char s3_6[] PROGMEM = "VAR"; +const char s3_7[] PROGMEM = "DEN"; +const char s3_8[] PROGMEM = "HAR"; +const char s3_9[] PROGMEM = "JEG"; +const char s3_10[] PROGMEM = "MEN"; +const char s3_11[] PROGMEM = "SEG"; +const char s3_12[] PROGMEM = "HUN"; +const char s3_13[] PROGMEM = "VÅR"; +const char s3_14[] PROGMEM = "FRA"; +const char s3_15[] PROGMEM = "KAN"; +const char s3_16[] PROGMEM = "BLE"; +const char s3_17[] PROGMEM = "VIL"; +const char s3_18[] PROGMEM = "HAM"; +const char s3_19[] PROGMEM = "VED"; +const char s3_20[] PROGMEM = "NOE"; +const char s3_21[] PROGMEM = "MEG"; +const char s3_22[] PROGMEM = "MOT"; +const char s3_23[] PROGMEM = "OPP"; +const char s3_24[] PROGMEM = "DER"; +const char s3_25[] PROGMEM = "NÅR"; +const char s3_26[] PROGMEM = "INN"; +const char s3_27[] PROGMEM = "DEM"; +const char s3_28[] PROGMEM = "SIN"; +const char s3_29[] PROGMEM = "KOM"; +const char s3_30[] PROGMEM = "ENN"; +const char s3_31[] PROGMEM = "BLI"; +const char s3_32[] PROGMEM = "FØR"; +const char s3_33[] PROGMEM = "GÅR"; +const char s3_34[] PROGMEM = "HER"; +const char s3_35[] PROGMEM = "MER"; +const char s3_36[] PROGMEM = "HVA"; +const char s3_37[] PROGMEM = "ALT"; +const char s3_38[] PROGMEM = "OSS"; +const byte s3_size = 38; +const char* const s3_table[] PROGMEM = + {s3_1,s3_2,s3_3,s3_4,s3_5,s3_6,s3_7,s3_8,s3_9,s3_10, + s3_11,s3_12,s3_13,s3_14,s3_15,s3_16,s3_17,s3_18,s3_19,s3_20, + s3_21,s3_22,s3_23,s3_24,s3_25,s3_26,s3_27,s3_28,s3_29,s3_30, + s3_31,s3_32,s3_33,s3_34,s3_35,s3_36,s3_37,s3_38}; + +// four letter word array +const char s4_1[] PROGMEM = "IKKE"; +const char s4_2[] PROGMEM = "OVER"; +const char s4_3[] PROGMEM = "OGSÅ"; +const char s4_4[] PROGMEM = "BARE"; +const char s4_5[] PROGMEM = "VÆRE"; +const char s4_6[] PROGMEM = "BLIR"; +const char s4_7[] PROGMEM = "ALLE"; +const char s4_8[] PROGMEM = "NOEN"; +const char s4_9[] PROGMEM = "SELV"; +const char s4_10[] PROGMEM = "SIER"; +const char s4_11[] PROGMEM = "HANS"; +const char s4_12[] PROGMEM = "GIKK"; +const char s4_13[] PROGMEM = "FIKK"; +const char s4_14[] PROGMEM = "DASH"; +const char s4_15[] PROGMEM = "HVOR"; +const char s4_16[] PROGMEM = "HELE"; +const byte s4_size = 16; +const char* const s4_table[] PROGMEM = + {s4_1,s4_2,s4_3,s4_4,s4_5,s4_6,s4_7,s4_8,s4_9,s4_10, + s4_11,s4_12,s4_13,s4_14,s4_15,s4_16}; + +// common names + +const char name_1[] PROGMEM = "JAN"; +const char name_2[] PROGMEM = "PER"; +const char name_3[] PROGMEM = "BJØRN"; +const char name_4[] PROGMEM = "OLE"; +const char name_5[] PROGMEM = "KJELL"; +const char name_6[] PROGMEM = "LARS"; +const char name_7[] PROGMEM = "ARNE"; +const char name_8[] PROGMEM = "KNUT"; +const char name_9[] PROGMEM = "SVEIN"; +const char name_10[] PROGMEM = "HANS"; +const char name_11[] PROGMEM = "ODD"; +const char name_12[] PROGMEM = "TOR"; +const char name_13[] PROGMEM = "GEIR"; +const char name_14[] PROGMEM = "TERJE"; +const char name_15[] PROGMEM = "THOMAS"; +const char name_16[] PROGMEM = "MORTEN"; +const char name_17[] PROGMEM = "JOHN"; +const char name_18[] PROGMEM = "ERIK"; +const char name_19[] PROGMEM = "ANDERS"; +const char name_20[] PROGMEM = "RUNE"; +const char name_21[] PROGMEM = "MARTIN"; +const char name_22[] PROGMEM = "ANDREAS"; +const char name_23[] PROGMEM = "TROND"; +const char name_24[] PROGMEM = "TORE"; +const char name_25[] PROGMEM = "HARALD"; +const char name_26[] PROGMEM = "OLAV"; +const char name_27[] PROGMEM = "GUNNAR"; +const char name_28[] PROGMEM = "JON"; +const char name_29[] PROGMEM = "ROLF"; +const char name_30[] PROGMEM = "LEIF"; +const char name_31[] PROGMEM = "TOM"; +const char name_32[] PROGMEM = "STIAN"; +const char name_33[] PROGMEM = "KRISTIAN"; +const char name_34[] PROGMEM = "NILS"; +const char name_35[] PROGMEM = "ØYVIND"; +const char name_36[] PROGMEM = "HELGE"; +const char name_37[] PROGMEM = "ESPEN"; +const char name_38[] PROGMEM = "EINAR"; +const char name_39[] PROGMEM = "MARIUS"; +const char name_40[] PROGMEM = "KÅRE"; +const char name_41[] PROGMEM = "DANIEL"; +const char name_42[] PROGMEM = "MAGNUS"; +const char name_43[] PROGMEM = "FREDRIK"; +const char name_44[] PROGMEM = "CHRISTIAN"; +const char name_45[] PROGMEM = "STEINAR"; +const char name_46[] PROGMEM = "EIRIK"; +const char name_47[] PROGMEM = "HÅKON"; +const char name_48[] PROGMEM = "ØYSTEIN"; +const char name_49[] PROGMEM = "HENRIK"; +const char name_50[] PROGMEM = "KARL"; +const byte name_size = 50; +const char* const name_table[] PROGMEM = + {name_1,name_2,name_3,name_4,name_5,name_6,name_7,name_8,name_9,name_10, + name_11,name_12,name_13,name_14,name_15,name_16,name_17,name_18,name_19,name_20, + name_21,name_22,name_23,name_24,name_25,name_26,name_27,name_28,name_29,name_30, + name_31,name_32,name_33,name_34,name_35,name_36,name_37,name_38,name_39,name_40, + name_41,name_42,name_43,name_44,name_45,name_46,name_47,name_48,name_49,name_50}; + + +// CW QSO word array +const char qso_1[] PROGMEM = "QRL?"; +const char qso_2[] PROGMEM = "QRM"; +const char qso_3[] PROGMEM = "QRN"; +const char qso_4[] PROGMEM = "QRS"; +const char qso_5[] PROGMEM = "QRT"; +const char qso_6[] PROGMEM = "QRZ"; +const char qso_7[] PROGMEM = "QSL"; +const char qso_8[] PROGMEM = "QSO"; +const char qso_9[] PROGMEM = "QSY"; +const char qso_10[] PROGMEM = "QTH"; +const char qso_11[] PROGMEM = "QRX"; +const char qso_12[] PROGMEM = "ABT"; +const char qso_13[] PROGMEM = "AGE"; +const char qso_14[] PROGMEM = "ANT"; +const char qso_15[] PROGMEM = "BEAM"; +const char qso_16[] PROGMEM = "BK"; +const char qso_17[] PROGMEM = "QRP"; +const char qso_18[] PROGMEM = "AGN"; +const char qso_19[] PROGMEM = "C"; +const char qso_20[] PROGMEM = "CL"; +const char qso_21[] PROGMEM = "CPY"; +const char qso_22[] PROGMEM = "CQ"; +const char qso_23[] PROGMEM = "CUL"; +const char qso_24[] PROGMEM = "DE"; +const char qso_25[] PROGMEM = "DX"; +const char qso_26[] PROGMEM = "ES"; +const char qso_27[] PROGMEM = "EL"; +const char qso_28[] PROGMEM = "FB"; +const char qso_29[] PROGMEM = "HI"; +const char qso_30[] PROGMEM = "HW?"; +const char qso_31[] PROGMEM = "HR"; +const char qso_32[] PROGMEM = "K"; +const char qso_33[] PROGMEM = "="; +const char qso_34[] PROGMEM = "<"; +const char qso_35[] PROGMEM = "%"; +const char qso_36[] PROGMEM = ">"; +const char qso_37[] PROGMEM = "LID"; +const char qso_38[] PROGMEM = "LOOP"; +const char qso_39[] PROGMEM = "NAME"; +const char qso_40[] PROGMEM = "OM"; +const char qso_41[] PROGMEM = "OP"; +const char qso_42[] PROGMEM = "PKT"; +const char qso_43[] PROGMEM = "PSE"; +const char qso_44[] PROGMEM = "R"; +const char qso_45[] PROGMEM = "RPT"; +const char qso_46[] PROGMEM = "RST"; +const char qso_47[] PROGMEM = "RIG"; +const char qso_48[] PROGMEM = "TEMP"; +const char qso_49[] PROGMEM = "TEST"; +const char qso_50[] PROGMEM = "TU"; +const char qso_51[] PROGMEM = "TKS"; +const char qso_52[] PROGMEM = "TNX"; +const char qso_53[] PROGMEM = "VERT"; +const char qso_54[] PROGMEM = "WATT"; +const char qso_55[] PROGMEM = "WX"; +const char qso_56[] PROGMEM = "YAGI"; +const char qso_57[] PROGMEM = "YRS"; +const char qso_58[] PROGMEM = "73"; +const char qso_59[] PROGMEM = "88"; +const char qso_60[] PROGMEM = "?"; +const char qso_61[] PROGMEM = "/"; +const char qso_62[] PROGMEM = "VY"; +const char qso_63[] PROGMEM = "YL"; +const char qso_64[] PROGMEM = "XYL"; +const char qso_65[] PROGMEM = "MY"; +const char qso_66[] PROGMEM = "UR"; +const char qso_67[] PROGMEM = "IS"; +const char qso_68[] PROGMEM = "QSB"; +const char qso_69[] PROGMEM = "QRQ"; +const char qso_70[] PROGMEM = "HVE"; +const char qso_71[] PROGMEM = "HPE"; +const char qso_72[] PROGMEM = "BEST"; +const byte qso_size = 72; +const char* const qso_table[] PROGMEM = + {qso_1,qso_2,qso_3,qso_4,qso_5,qso_6,qso_7,qso_8,qso_9,qso_10,qso_11,qso_12,qso_13,qso_14,qso_15,qso_16,qso_17,qso_18,qso_19,qso_20, + qso_21,qso_22,qso_23,qso_24,qso_25,qso_26,qso_27,qso_28,qso_29,qso_30,qso_31,qso_32,qso_33,qso_34,qso_35,qso_36,qso_37,qso_38,qso_39,qso_40, + qso_41,qso_42,qso_43,qso_44,qso_45,qso_46,qso_47,qso_48,qso_49,qso_50,qso_51,qso_52,qso_53,qso_54,qso_55,qso_56,qso_57,qso_58,qso_59,qso_60, + qso_61,qso_62,qso_63,qso_64,qso_65,qso_66,qso_67,qso_68,qso_69,qso_70,qso_71,qso_72}; + + + + diff --git a/k3ng_keyer/src/buttonarray/buttonarray.cpp b/k3ng_keyer/src/buttonarray/buttonarray.cpp new file mode 100755 index 0000000..6b8c1cb --- /dev/null +++ b/k3ng_keyer/src/buttonarray/buttonarray.cpp @@ -0,0 +1,151 @@ +#include "Arduino.h" +#include "buttonarray.h" + +/* contributed by W6IPA */ + +void Button::InitLimits(uint8_t step){ + /* + + typical button values: + + 0: -56 - 46 + 1: 47 - 131 + 2: 132 - 203 + 3: 203 - 264 + + */ + + int32_t button_value; + int32_t lower_button_value; + int32_t higher_button_value; + + button_value = uint32_t(max_value * (float(step * r2_value)/float((step * r2_value) + r1_value))); + lower_button_value = uint32_t(max_value * (float((step-1) * r2_value)/float(((step-1) * r2_value) + r1_value))); + higher_button_value = uint32_t(max_value * (float((step+1) * r2_value)/float(((step+1) * r2_value) + r1_value))); + low_limit_= (button_value - ((button_value - lower_button_value)/2)); + high_limit_= (button_value + ((higher_button_value - button_value)/2)); + step_ = step; +} + +void Button::InitLimits(uint8_t step, int32_t low_limit, int32_t high_limit){ + low_limit_ = low_limit; + high_limit_ = high_limit; + step_ = step; +} + +bool Button::Pressed(int32_t analog_reading){ + if ((analog_reading > low_limit_) && (analog_reading <= high_limit_)) { + return true; + } + return false; +} +int32_t Button::high_limit(){ + return high_limit_; +} + +int32_t Button::low_limit(){ + return low_limit_; +} + +// Add all buttons in incremental order +void ButtonArray::AddAll(){ + size_t index; + if (reversed_) { + index = nb_buttons_ - 1; + } + for(size_t i = 0; i < nb_buttons_; i++) + { + if (reversed_) { + Add(i, index); + index --; + } else { + Add(i, i); + } + } +} + + +// Adds a single button to the array +// Takes a step (rank in the resistor ladder), and the index in the button array. +void ButtonArray::Add(uint8_t step, uint8_t index){ + Button button; + button.InitLimits(step); + button_array_[index] = button; + high_limit_ = max(button.high_limit() , high_limit_); +} + +void ButtonArray::Add(uint8_t step, uint8_t index, int32_t low_limit, int32_t high_limit){ + Button button; + button.InitLimits(step, low_limit, high_limit); + button_array_[index] = button; + high_limit_ = max(high_limit, high_limit_); +} + +int32_t ButtonArray::high_limit(){ + return high_limit_; +} + +int8_t ButtonArray::ReadButtons(){ + uint32_t analog_read_temp = 0; + uint32_t analog_line_read_average = 0; + //uint8_t number_of_samples = 0; + + for (byte x = 0; x < NUMBER_OF_BUTTON_READS_TO_AVERAGE; x++){ + analog_read_temp = analogRead(pin_); + if (analog_read_temp <= high_limit_){ + analog_line_read_average = (analog_line_read_average + analog_read_temp) / 2; + //analog_line_read_average = analog_line_read_average + analog_read_temp; + //number_of_samples++; + } + } + //analog_line_read_average = analog_line_read_average / number_of_samples; + for (size_t x = 0; x < nb_buttons_; x++) { + Button button = button_array_[x]; + if (button.Pressed(analog_line_read_average)) { + last_pressed_ms = millis(); + return x; + } + } + return -1; +} +// returns true if analog reading indicate a pressed button +bool ButtonArray::AnyPressed() { + if (analogRead(pin_) <= high_limit_) { + return true; + } + return false; +} +// returns the index of the pressed button or -1 if none pressed +int8_t ButtonArray::Pressed() { + if (AnyPressed() && ((millis() - last_pressed_ms) > DEBOUNCE_MS )){ + return ReadButtons(); + } + return -1; +} +// returns true if button at the provided index is pressed +bool ButtonArray::Pressed(uint8_t index) { + uint32_t analog_read_temp =0; + analog_read_temp = analogRead(pin_); + if ((analog_read_temp <= high_limit_) && ( index < nb_buttons_)) { + Button button = button_array_[index]; + return button.Pressed(analog_read_temp); + } + return false; +} +// Return true as long as the indicated button is held and the deadline not reached. +bool ButtonArray::Held(uint8_t index, uint32_t deadline) { + if (AnyPressed() && + (millis() < deadline) && + (ReadButtons() == index)) { + return true; + } + return false; +} + +// Return true as long as the indicated button is held +bool ButtonArray::Held(uint8_t index) { + if (AnyPressed() && (ReadButtons() == index)) { + return true; + } + return false; +} diff --git a/k3ng_keyer/src/buttonarray/buttonarray.h b/k3ng_keyer/src/buttonarray/buttonarray.h new file mode 100755 index 0000000..4c8c2ec --- /dev/null +++ b/k3ng_keyer/src/buttonarray/buttonarray.h @@ -0,0 +1,59 @@ +#ifndef buttonarray_h +#define buttonarray_h +#include "Arduino.h" +#ifndef MAX_ARRAY_BUTTONS + #define MAX_ARRAY_BUTTONS 13 +#endif +#define DEBOUNCE_MS 200 +#define NUMBER_OF_BUTTON_READS_TO_AVERAGE 19 + +#if defined(ARDUINO_ARCH_ESP32) || defined(ARDUINO_GENERIC_STM32F103C) + #define max_value 4095 +#else + #define max_value 1023 +#endif + +#define r1_value 10 +#define r2_value 1 + +/* contributed by W6IPA */ + +class Button { + private: + int32_t low_limit_; + int32_t high_limit_; + uint8_t step_; + public: + Button(){}; + void InitLimits(uint8_t step); + void InitLimits(uint8_t step, int32_t low_limit, int32_t high_limit); + bool Pressed(int32_t analog_reading); + int32_t high_limit(); + int32_t low_limit(); +}; + +class ButtonArray { + private: + Button button_array_[MAX_ARRAY_BUTTONS]; + uint8_t pin_; + uint8_t nb_buttons_; + int32_t high_limit_; + bool reversed_; + int8_t ReadButtons(); + bool AnyPressed(); + + public: + uint32_t last_pressed_ms; + ButtonArray(uint8_t pin, uint8_t nb, bool reversed): pin_(pin), nb_buttons_(nb), reversed_(reversed){}; + void AddAll(); + void Add(uint8_t step, uint8_t index); + void Add(uint8_t step, uint8_t index, int32_t low_limit, int32_t high_limit); + int32_t high_limit(); + int8_t Pressed(); + bool Pressed(uint8_t index); + bool Held(uint8_t index); + bool Held(uint8_t index, uint32_t deadline ); +}; + + +#endif diff --git a/k3ng_keyer/src/buttonarray/buttonarray.ino b/k3ng_keyer/src/buttonarray/buttonarray.ino new file mode 100755 index 0000000..08ab0b6 --- /dev/null +++ b/k3ng_keyer/src/buttonarray/buttonarray.ino @@ -0,0 +1,50 @@ +// Button Array test + +#include "buttonarray.h" + +#define ARRAY_PIN A3 +#define NB_BUTTONS 2 +#define ENCODER_PIN A1 + + +int button_array_high_limit[NB_BUTTONS]; +int button_array_low_limit[NB_BUTTONS]; +long button_depress_time; + + + +ButtonArray button_array(ARRAY_PIN, NB_BUTTONS, false); +ButtonArray encoder(ENCODER_PIN, 1, false); + +void setup() { + Serial.begin(115200); + delay(1000); + button_array.AddAll(); + encoder.AddAll(); +} + +void loop() { + + int analogbuttontemp = button_array.Pressed(); + if (encoder.Pressed() == 0) { + Serial.println("encoder pressed "); + } + if (analogbuttontemp >= 0 ) { + // button pressed. + + button_depress_time = button_array.last_pressed_ms; + + while (button_array.Held(analogbuttontemp, button_depress_time + 1000)) { + + } + if (millis() - button_depress_time < 400) { + Serial.print("button pressed "); + Serial.println(analogbuttontemp); + } else { + Serial.print("button "); + Serial.print(analogbuttontemp); + Serial.print(" held for "); + Serial.println(millis() - button_depress_time); + } + } +} diff --git a/keyer.png b/keyer.png new file mode 100755 index 0000000000000000000000000000000000000000..2c9f7fc8a36690170a12b95d37fcb84af5757688 GIT binary patch literal 153496 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

NTwI5&8v; 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�QzCKhe?2}Dwj92lN+ zEG;2f1co{`0N*Y_0#E{y5JHG3C1IXYGgKnq(^51qcvc0)h2~y6!qz?brmMKfvgIdL z*05X8lO+g(+dL}NZfucucvLG)tbT3&wHC0ae#&bJ=3);Zr;P8q`%b+7 z?a#YOLvm}%Y-ZP?bLzn2qf@&jUikz8u|<}W7*P~#dNnEnAuBFPGjN*lUH9IB_r3jH z$jD7+B}Gl}8Og>vNb6j(ucH@DkD@4psrZPLnn+gbYF>hcnr%~peA2Z}w&4`}OH%*GVK?{jzOqu^k@SV5scKvf8|hP_SiLl9AQIN5@^=BbS{3yxyEjEa9{#}%rt3s#<; zTFrgjb7ZsMB6PN`;1~cHrXryOm?09a;MB3p1)Ft64I;ya+(Atc5Tl(E_|{mnlYC~Ex+2cWg>pWUf{?P) z#iaw60?OGKu%V7n3Z~FTtLZe^h#dk#Tpp@g!0P(i+GlQ(j>@rMiU4QBUYMa-%=WRa zpS|x(P^b_%2qJe@w6kM5!e-;h%jl(^8FgkRHqRi5HWsA>Wd@f81C)+hXxmK1H+cX; z2k8v+=*TyG&iP<*zEBmVde>PQvr>)GvTaF&1`}Y4Kwy!v1QT+$++?m&63g!riDudD ze3RuF5jVguhyo89W0c7{!Y)|xE7?22VATJ)WbjE5q9ArXLhNujo={RYrgF3uGE;yC zfr@>`wp9x4pOuZmQ0f2VMo}teSzCWyw~~c)m20Md>^SsCb}nx@Kdrizr&reB(9zV^ z6S`zaXCr7SF{p?|y%r$ zK?53?NE{Z73+e_>mBZnucjsM^+V_wpfm3QOb(JGSE*DW3Ny2lgnHE_COI*z8$-ehW zn*gzmUaBa?e1kJ3NFXcNuoOdc$hIP7J1`H&vlSEow?qK7G10c31Qy#CffyXw#yy&$ zC7~OP^HWM{(13arjsyd8>&BCXp(5yq4W2xCY>SL;G@_(LP>g^xaIHHLn<)HX6sDR> zjKZbinU(cOV0US>REjzw{7H(+TNampk&E79O=$+r?&jvJ!RF_$hH;=Mx*z}vx?uo` zt;Kj{Z9s0DcUIU;;9^3=5FoKPcQ>u!^br~i8Sgs_j;bex_@+fX*Tzu%VC^Eg8g=#I z)sdu{OxKoHn_+id5_LuzWHJM&VuU*@H~><%^|MpygP(FaWg8=%=_)Xztw0Mk94o`D zYW;wWrYEBM(7{I2Jk0=!JCbU9H??WA zX1f5+D4Lpo3VY|GjvIv)M%OyFDl_m%5aG~9UY79GdQN`O1mal231&8q?OXdb5*2)V}2R_SHVXQaEjsauL7yKJUH1jY-I*qKMI-*1bZ zt211q#pRUwh@9;pDn%A5!fISKj7k)rNx(`KtOZa zuk6dkks{T^HH>Nx)FG^)kfV_hV#)C|HZ^ z+Tm-CWTh0O1aPRh2vD*`xfob%Uj~P&6%ud^iH`ekWGu3?CJD2u%)W9%mUJ~sK5TDtJp~>Sp+{-q%1b`KeZCQbtgszLLj*KBfZMNo$7gX82 zu7q4%V784Z6?7pqd4Y03K`l--aU(6>CU`# zWF=fum!Al)qELAGZ@MOV)&fI>5SD_Nb8g-7y0a^#SeQLYgZ_yb=%jVBmFSrPNH5@W zL_$FbRTQg$*!aWj-ZU#rl-+ygXipF(8g?(4s>=~-JdWa;A0zjfh^^6-)NlZ< z#l-Wx?VqW!h1Vffi)Ev)c2|X_@lpv0-Z{d-VixhZ6gS4MSP46JMWq$`oDx{2jaCG4 zhq~%jBZea~FP4$M{WAaJHI1Pbh?M}8my9Vb3%GAq0-c4uH-F;=)2N)Ux=B;0GgGMr z?AiAxp6==0U9m0Df(?nS^H-T#Ip+oQD78~U`?)ncsTQyblxh`YYPBPEe-?1Vh-v|; z@nTfFpOwa~i1;+%Mo&RJIa3jNz?@BC4zW(9uE=$TS+-?@gIT0rU_gKDV&_k#xO_t= zQ%|nyo>tr-ng}&nv=9(j@zvK~!>2#}Y3zs5TIi=VPKJ$m0;C#&tuicg$+&)U4d&bw zwxKJ=G4U0u%YY4WcYv%4a4jD;NkEZx=CTKb8C`kVXsI-vMro>I50!F{ z;aToTbJ1HWFPa z7Dbv%EB?kzUKOpU6z5tLfn6`;huIl2*}(C9Mx<(q5-zb=yk-1gtG^F05~f>ZR%JYK zSR9KPXr=_1*;9=yt|ytwf^(>ESW!KHnNY;6H#gY)9 zi>ki4$zX|~{We|S`r<+>;BLi>VO}r_V!O#u9qF~;`oTk7+#Arx4s)6fDX#z<83B*{ zgv+TQ_*f$)q38_Zjg2iL#%~Z_fIwMJ8X`jId)r`%jS?c}`x-)jHd1KoQk>V)P61k0 z?WE>6<2V9UIXdipm1@|+H83tO36GA?sGs_!crgphSkcyxo{&JQ#$TwPCAr-;5u}&u zM&-B#oe}NWO_kgB<*5@Mbt}a+H+*3TlLNLj04q3tL%eF!y?qZITR^+prg9MD*eranrxAvEusW#nkb#d2?YVN@Rh0W$i3{olmg z_ZGtC>Sws^Wc5R`qQeb0EMDCBDSp_(Px4Wb+49)yJ=JV447^9x^${7~R!f2iO1&*B zi4QI`Wo>aeyxO+v8l=TyA4i}+1I6VjPRtn}rV;+MxvkYNMu=BIhisaXIe8bbd?^k1 zf`f}{ek#Nk6&hl2Gw6jRRL8O?5g`O*SA17H0o4pRf2HvsoV8k$0G3>OYO^+b2Y^-V zNChkH6kNt4!xZY7X6XzWmK9Zjhxr{^HA*Zx!rN>-5jq14x)&4|kJjvy;- z$azFbeK1>F-*S0fv{p^9)NW>Sga%l=jjd3|1pw4l@}^okWW^hBM*OVl39ByEc{25( zuOVgZEuXGB53FNfbfs4JPhGu(1iMASn(5sVd5so9uKcy1&+Rih<8l$LzGiCOM9ogE zhYnVsWAiw6A7}Mw7C7)rT+W`Iwt?~yRhf{hvA2IG)gnS2PpMrWk!@oY4&IW(3bY(B zXV0V(z6~|y+gn`Tit$^@!VAd70^>_LZ^?K$7TWfs7M%6ZsW?pg;IIUb6A@xqA|#qV z$d$0!Z~VUNd=7Wd5|zf!QKnVsn1Rp+5^K#!I}>hI4gyu;RNHi!(2E#I)7(gb<|X1@ z7+zqDgQVmc%9ZHH)$vOOirjFlnlK4x53jd%&>g>g*xu2 zv<^U#{q*+LB!}jHR_lZLY^*w;Du`ppP*x{$#Z}fthJ%kU*?fVJGfL8S51idc4stga zKwA03p3NZc+E!Pp)l3~V+u3}A8k*q`AK1G>@~vKTd_!CBDlLVWSlZS|X(8vWEo0CA zUWz~TPNZw#aBkYF+EKy9hO*MH)kYY~MqWf|g{J|8F3eo+RXyvVT45x7Td(k~E++IA zyZ^Q&Sgm%8hlpY2UazWPB?uUS88a`ax+yxvv@)t^Cks3NQ7r~@awV$>g2ZOJT>lKy z0Z~hwQ`Aiow{Zc&20i(Rg&GC{GN&-ECmOZy%pUYrLP4FK84g~vwblsMt*D>GZH8V@ zpJq^v+76R3VcVT6Vv2LM6AWajhf6Q9dbldrp>`-#S)yvLWUi3qvI43-2u!nAB&j$| zS>1>Z`0`>J1TL%0ZCcEbh}yp+cXXx|jl3FH)iKVDpR?<&WHH35-60fIoU*=xla!XC z;H~acC-luXZQh5N*gAfk{qmCexw~GWzBy>=9>5z=uKx|$Y z_Fye;XayOW)Lp>bCL+z~yJ0m>=%O#GRCeW-l4wqPWPlsPT;aTU5-q3XTD?xiTIfk(7dzCp zIwebJFiOi3D7M57k*`TgEX5V2kJS4*mEP!*n%^ z`?|X!Ds4nqiOss^UOzw-j$P37Cx}}xn)m-6H2wp%=OVg{7zm@?^Z7sQs zk-$0eaSesc$H~1`>y6D`iyTB2Zq^HD7|kkr#m3gK;$rDa@#{#`Dxi#`o1B*#xaP%A z-2$L>Wnx{11M7-4U4Z?;E{;#su8W#^H?3DTZTwwuw2G@NOO(1w*7lvz3SXnD?p|R) z4bxi1Z&dD=Cjr`ltFqKt|I$pcY$=K4NdR@jXgCTtAoe_49XR*o*FW;m|1()!-pUK6 zZi_1J@)QAZ<-m>wU|oGl>!MUEbEtk4&Jjso1nNV^EFRcx@gd3i38pXdr*C-ykl?2u5yg|ONjsrt4mPy+#_MzNN~nNFdN@J5w6?ZQ67dQ{Yv zj9~$=8⪙QREM`tIA|~KSbyIAa9ken;xEvY3h5d z|IWK6cEzw2+=T;G=>%27qaM~y{<;>@+Nu~B1mi1a`$AeiI4xV&HD`7i8`rJYTbnrj zneSZIGvHcm_s!hyO8c_3(X87J-b1YxUnFg=aN3l^mB^`>V16bc^hlhbFwIELw(6^X zE42#{w`2en6Zq}cKKb4N;PqnywGhyR5>uj>y2ew4PHS1B&*UAC%jg#@f+|0Sc_})6f6aMraj|XO zpk@C426ZW~{vG2?fm%E4HG6is3EkcRwK(xg9bDJV%O(oCk`-eqTxZ`|eH|XQRgYcu zRKN8bsGFa8I^og7N4Wd;do4)4{@jwY7r~{dowuu4gq{tnS-s} +#include +#include + +// FOR Arduino Due +#if !defined(_BV) +# define _BV(bit) (1 << (bit)) +#endif + + +// When the display powers up, it is configured as follows: +// +// 1. Display clear +// 2. Function set: +// DL = 1; 8-bit interface data +// N = 0; 1-line display +// F = 0; 5x8 dot character font +// 3. Display on/off control: +// D = 0; Display off +// C = 0; Cursor off +// B = 0; Blinking off +// 4. Entry mode set: +// I/D = 1; Increment by 1 +// S = 0; No shift +// +// Note, however, that resetting the Arduino doesn't reset the LCD, so we +// can't assume that its in that state when a sketch starts (and the +// Adafruit_LiquidCrystal constructor is called). + +Adafruit_LiquidCrystal::Adafruit_LiquidCrystal(uint8_t rs, uint8_t rw, uint8_t enable, + uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3, + uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7) +{ + init(0, rs, rw, enable, d0, d1, d2, d3, d4, d5, d6, d7); +} + +Adafruit_LiquidCrystal::Adafruit_LiquidCrystal(uint8_t rs, uint8_t enable, + uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3, + uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7) +{ + init(0, rs, 255, enable, d0, d1, d2, d3, d4, d5, d6, d7); +} + +Adafruit_LiquidCrystal::Adafruit_LiquidCrystal(uint8_t rs, uint8_t rw, uint8_t enable, + uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3) +{ + init(1, rs, rw, enable, d0, d1, d2, d3, 0, 0, 0, 0); +} + +Adafruit_LiquidCrystal::Adafruit_LiquidCrystal(uint8_t rs, uint8_t enable, + uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3) +{ + init(1, rs, 255, enable, d0, d1, d2, d3, 0, 0, 0, 0); +} + +Adafruit_LiquidCrystal::Adafruit_LiquidCrystal(uint8_t i2caddr) { + _i2cAddr = i2caddr; + + _displayfunction = LCD_4BITMODE | LCD_1LINE | LCD_5x8DOTS; + + // the I/O expander pinout + _rs_pin = 1; + _rw_pin = 255; + _enable_pin = 2; + _data_pins[0] = 3; // really d4 + _data_pins[1] = 4; // really d5 + _data_pins[2] = 5; // really d6 + _data_pins[3] = 6; // really d7 + + // we can't begin() yet :( +} + + +Adafruit_LiquidCrystal::Adafruit_LiquidCrystal(uint8_t data, uint8_t clock, uint8_t latch ) { + _i2cAddr = 255; + + _displayfunction = LCD_4BITMODE | LCD_1LINE | LCD_5x8DOTS; + + // the SPI expander pinout + _rs_pin = 1; + _rw_pin = 255; + _enable_pin = 2; + _data_pins[0] = 6; // really d4 + _data_pins[1] = 5; // really d5 + _data_pins[2] = 4; // really d6 + _data_pins[3] = 3; // really d7 + + _SPIdata = data; + _SPIclock = clock; + _SPIlatch = latch; + + _SPIbuff = 0; + + // we can't begin() yet :( +} + + +void Adafruit_LiquidCrystal::init(uint8_t fourbitmode, uint8_t rs, uint8_t rw, uint8_t enable, + uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3, + uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7) +{ + _rs_pin = rs; + _rw_pin = rw; + _enable_pin = enable; + + _data_pins[0] = d0; + _data_pins[1] = d1; + _data_pins[2] = d2; + _data_pins[3] = d3; + _data_pins[4] = d4; + _data_pins[5] = d5; + _data_pins[6] = d6; + _data_pins[7] = d7; + + _i2cAddr = 255; + _SPIclock = _SPIdata = _SPIlatch = 255; + + if (fourbitmode) + _displayfunction = LCD_4BITMODE | LCD_1LINE | LCD_5x8DOTS; + else + _displayfunction = LCD_8BITMODE | LCD_1LINE | LCD_5x8DOTS; +} + +void Adafruit_LiquidCrystal::begin(uint8_t cols, uint8_t lines, uint8_t dotsize) { + // check if i2c + if (_i2cAddr != 255) { + _i2c.begin(_i2cAddr); + + _i2c.pinMode(7, OUTPUT); // backlight + _i2c.digitalWrite(7, HIGH); // backlight + + for (uint8_t i=0; i<4; i++) + _pinMode(_data_pins[i], OUTPUT); + + _i2c.pinMode(_rs_pin, OUTPUT); + _i2c.pinMode(_enable_pin, OUTPUT); + } else if (_SPIclock != 255) { + pinMode(_SPIdata, OUTPUT); + pinMode(_SPIclock, OUTPUT); + pinMode(_SPIlatch, OUTPUT); + _SPIbuff = 0x80; // backlight + } else { + pinMode(_rs_pin, OUTPUT); + // we can save 1 pin by not using RW. Indicate by passing 255 instead of pin# + if (_rw_pin != 255) { + pinMode(_rw_pin, OUTPUT); + } + pinMode(_enable_pin, OUTPUT); + + } + + + + if (lines > 1) { + _displayfunction |= LCD_2LINE; + } + _numlines = lines; + _currline = 0; + + // for some 1 line displays you can select a 10 pixel high font + if ((dotsize != 0) && (lines == 1)) { + _displayfunction |= LCD_5x10DOTS; + } + + // SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION! + // according to datasheet, we need at least 40ms after power rises above 2.7V + // before sending commands. Arduino can turn on way befer 4.5V so we'll wait 50 + delayMicroseconds(50000); + // Now we pull both RS and R/W low to begin commands + _digitalWrite(_rs_pin, LOW); + _digitalWrite(_enable_pin, LOW); + if (_rw_pin != 255) { + _digitalWrite(_rw_pin, LOW); + } + + //put the LCD into 4 bit or 8 bit mode + if (! (_displayfunction & LCD_8BITMODE)) { + // this is according to the hitachi HD44780 datasheet + // figure 24, pg 46 + + // we start in 8bit mode, try to set 4 bit mode + write4bits(0x03); + delayMicroseconds(4500); // wait min 4.1ms + + // second try + write4bits(0x03); + delayMicroseconds(4500); // wait min 4.1ms + + // third go! + write4bits(0x03); + delayMicroseconds(150); + + // finally, set to 8-bit interface + write4bits(0x02); + } else { + // this is according to the hitachi HD44780 datasheet + // page 45 figure 23 + + // Send function set command sequence + command(LCD_FUNCTIONSET | _displayfunction); + delayMicroseconds(4500); // wait more than 4.1ms + + // second try + command(LCD_FUNCTIONSET | _displayfunction); + delayMicroseconds(150); + + // third go + command(LCD_FUNCTIONSET | _displayfunction); + } + + // finally, set # lines, font size, etc. + command(LCD_FUNCTIONSET | _displayfunction); + + // turn the display on with no cursor or blinking default + _displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF; + display(); + + // clear it off + clear(); + + // Initialize to default text direction (for romance languages) + _displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT; + // set the entry mode + command(LCD_ENTRYMODESET | _displaymode); + +} + +/********** high level commands, for the user! */ +void Adafruit_LiquidCrystal::clear() +{ + command(LCD_CLEARDISPLAY); // clear display, set cursor position to zero + delayMicroseconds(2000); // this command takes a long time! +} + +void Adafruit_LiquidCrystal::home() +{ + command(LCD_RETURNHOME); // set cursor position to zero + delayMicroseconds(2000); // this command takes a long time! +} + +void Adafruit_LiquidCrystal::setCursor(uint8_t col, uint8_t row) +{ + int row_offsets[] = { 0x00, 0x40, 0x14, 0x54 }; + if ( row > _numlines ) { + row = _numlines-1; // we count rows starting w/0 + } + + command(LCD_SETDDRAMADDR | (col + row_offsets[row])); +} + +// Turn the display on/off (quickly) +void Adafruit_LiquidCrystal::noDisplay() { + _displaycontrol &= ~LCD_DISPLAYON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} +void Adafruit_LiquidCrystal::display() { + _displaycontrol |= LCD_DISPLAYON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} + +// Turns the underline cursor on/off +void Adafruit_LiquidCrystal::noCursor() { + _displaycontrol &= ~LCD_CURSORON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} +void Adafruit_LiquidCrystal::cursor() { + _displaycontrol |= LCD_CURSORON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} + +// Turn on and off the blinking cursor +void Adafruit_LiquidCrystal::noBlink() { + _displaycontrol &= ~LCD_BLINKON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} +void Adafruit_LiquidCrystal::blink() { + _displaycontrol |= LCD_BLINKON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} + +// These commands scroll the display without changing the RAM +void Adafruit_LiquidCrystal::scrollDisplayLeft(void) { + command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT); +} +void Adafruit_LiquidCrystal::scrollDisplayRight(void) { + command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT); +} + +// This is for text that flows Left to Right +void Adafruit_LiquidCrystal::leftToRight(void) { + _displaymode |= LCD_ENTRYLEFT; + command(LCD_ENTRYMODESET | _displaymode); +} + +// This is for text that flows Right to Left +void Adafruit_LiquidCrystal::rightToLeft(void) { + _displaymode &= ~LCD_ENTRYLEFT; + command(LCD_ENTRYMODESET | _displaymode); +} + +// This will 'right justify' text from the cursor +void Adafruit_LiquidCrystal::autoscroll(void) { + _displaymode |= LCD_ENTRYSHIFTINCREMENT; + command(LCD_ENTRYMODESET | _displaymode); +} + +// This will 'left justify' text from the cursor +void Adafruit_LiquidCrystal::noAutoscroll(void) { + _displaymode &= ~LCD_ENTRYSHIFTINCREMENT; + command(LCD_ENTRYMODESET | _displaymode); +} + +// Allows us to fill the first 8 CGRAM locations +// with custom characters +void Adafruit_LiquidCrystal::createChar(uint8_t location, uint8_t charmap[]) { + location &= 0x7; // we only have 8 locations 0-7 + command(LCD_SETCGRAMADDR | (location << 3)); + for (int i=0; i<8; i++) { + write(charmap[i]); + } +} + +/*********** mid level commands, for sending data/cmds */ + +inline void Adafruit_LiquidCrystal::command(uint8_t value) { + send(value, LOW); +} + +#if ARDUINO >= 100 +inline size_t Adafruit_LiquidCrystal::write(uint8_t value) { + send(value, HIGH); + return 1; +} +#else +inline void Adafruit_LiquidCrystal::write(uint8_t value) { + send(value, HIGH); +} +#endif + +/************ low level data pushing commands **********/ + +// little wrapper for i/o writes +void Adafruit_LiquidCrystal::_digitalWrite(uint8_t p, uint8_t d) { + if (_i2cAddr != 255) { + // an i2c command + _i2c.digitalWrite(p, d); + } else if (_SPIclock != 255) { + if (d == HIGH) + _SPIbuff |= (1 << p); + else + _SPIbuff &= ~(1 << p); + + digitalWrite(_SPIlatch, LOW); + shiftOut(_SPIdata, _SPIclock, MSBFIRST,_SPIbuff); + digitalWrite(_SPIlatch, HIGH); + } else { + // straightup IO + digitalWrite(p, d); + } +} + +// Allows to set the backlight, if the LCD backpack is used +void Adafruit_LiquidCrystal::setBacklight(uint8_t status) { + // check if i2c or SPI + if ((_i2cAddr != 255) || (_SPIclock != 255)) { + _digitalWrite(7, status); // backlight is on pin 7 + } +} + +// little wrapper for i/o directions +void Adafruit_LiquidCrystal::_pinMode(uint8_t p, uint8_t d) { + if (_i2cAddr != 255) { + // an i2c command + _i2c.pinMode(p, d); + } else if (_SPIclock != 255) { + // nothing! + } else { + // straightup IO + pinMode(p, d); + } +} + +// write either command or data, with automatic 4/8-bit selection +void Adafruit_LiquidCrystal::send(uint8_t value, boolean mode) { + _digitalWrite(_rs_pin, mode); + + // if there is a RW pin indicated, set it low to Write + if (_rw_pin != 255) { + _digitalWrite(_rw_pin, LOW); + } + + if (_displayfunction & LCD_8BITMODE) { + write8bits(value); + } else { + write4bits(value>>4); + write4bits(value); + } +} + +void Adafruit_LiquidCrystal::pulseEnable(void) { + _digitalWrite(_enable_pin, LOW); + delayMicroseconds(1); + _digitalWrite(_enable_pin, HIGH); + delayMicroseconds(1); // enable pulse must be >450ns + _digitalWrite(_enable_pin, LOW); + delayMicroseconds(100); // commands need > 37us to settle +} + +void Adafruit_LiquidCrystal::write4bits(uint8_t value) { + if (_i2cAddr != 255) { + uint8_t out = 0; + + out = _i2c.readGPIO(); + + + // speed up for i2c since its sluggish + for (int i = 0; i < 4; i++) { + out &= ~_BV(_data_pins[i]); + out |= ((value >> i) & 0x1) << _data_pins[i]; + } + + // make sure enable is low + out &= ~ _BV(_enable_pin); + + _i2c.writeGPIO(out); + + // pulse enable + delayMicroseconds(1); + out |= _BV(_enable_pin); + _i2c.writeGPIO(out); + delayMicroseconds(1); + out &= ~_BV(_enable_pin); + _i2c.writeGPIO(out); + delayMicroseconds(100); + } else { + for (int i = 0; i < 4; i++) { + _pinMode(_data_pins[i], OUTPUT); + _digitalWrite(_data_pins[i], (value >> i) & 0x01); + } + pulseEnable(); + } +} + +void Adafruit_LiquidCrystal::write8bits(uint8_t value) { + for (int i = 0; i < 8; i++) { + _pinMode(_data_pins[i], OUTPUT); + _digitalWrite(_data_pins[i], (value >> i) & 0x01); + } + + pulseEnable(); +} diff --git a/libraries/Adafruit_LiquidCrystal/Adafruit_LiquidCrystal.h b/libraries/Adafruit_LiquidCrystal/Adafruit_LiquidCrystal.h new file mode 100755 index 0000000..3ed6f33 --- /dev/null +++ b/libraries/Adafruit_LiquidCrystal/Adafruit_LiquidCrystal.h @@ -0,0 +1,124 @@ +#ifndef Adafruit_LiquidCrystal_h +#define Adafruit_LiquidCrystal_h + +#include "Arduino.h" +#include +#include "Print.h" +#include "utility/Adafruit_MCP23008.h" + +// commands +#define LCD_CLEARDISPLAY 0x01 +#define LCD_RETURNHOME 0x02 +#define LCD_ENTRYMODESET 0x04 +#define LCD_DISPLAYCONTROL 0x08 +#define LCD_CURSORSHIFT 0x10 +#define LCD_FUNCTIONSET 0x20 +#define LCD_SETCGRAMADDR 0x40 +#define LCD_SETDDRAMADDR 0x80 + +// flags for display entry mode +#define LCD_ENTRYRIGHT 0x00 +#define LCD_ENTRYLEFT 0x02 +#define LCD_ENTRYSHIFTINCREMENT 0x01 +#define LCD_ENTRYSHIFTDECREMENT 0x00 + +// flags for display on/off control +#define LCD_DISPLAYON 0x04 +#define LCD_DISPLAYOFF 0x00 +#define LCD_CURSORON 0x02 +#define LCD_CURSOROFF 0x00 +#define LCD_BLINKON 0x01 +#define LCD_BLINKOFF 0x00 + +// flags for display/cursor shift +#define LCD_DISPLAYMOVE 0x08 +#define LCD_CURSORMOVE 0x00 +#define LCD_MOVERIGHT 0x04 +#define LCD_MOVELEFT 0x00 + +// flags for function set +#define LCD_8BITMODE 0x10 +#define LCD_4BITMODE 0x00 +#define LCD_2LINE 0x08 +#define LCD_1LINE 0x00 +#define LCD_5x10DOTS 0x04 +#define LCD_5x8DOTS 0x00 + +class Adafruit_LiquidCrystal : public Print { +public: + Adafruit_LiquidCrystal(uint8_t rs, uint8_t enable, + uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3, + uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7); + Adafruit_LiquidCrystal(uint8_t rs, uint8_t rw, uint8_t enable, + uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3, + uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7); + Adafruit_LiquidCrystal(uint8_t rs, uint8_t rw, uint8_t enable, + uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3); + Adafruit_LiquidCrystal(uint8_t rs, uint8_t enable, + uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3); + + Adafruit_LiquidCrystal(uint8_t i2cAddr); + Adafruit_LiquidCrystal(uint8_t data, uint8_t clock, uint8_t latch); + + void init(uint8_t fourbitmode, uint8_t rs, uint8_t rw, uint8_t enable, + uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3, + uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7); + + void begin(uint8_t cols, uint8_t rows, uint8_t charsize = LCD_5x8DOTS); + + void clear(); + void home(); + + void noDisplay(); + void display(); + void noBlink(); + void blink(); + void noCursor(); + void cursor(); + void scrollDisplayLeft(); + void scrollDisplayRight(); + void leftToRight(); + void rightToLeft(); + void autoscroll(); + void noAutoscroll(); + + // only if using backpack + void setBacklight(uint8_t status); + + void createChar(uint8_t, uint8_t[]); + void setCursor(uint8_t, uint8_t); +#if ARDUINO >= 100 + virtual size_t write(uint8_t); +#else + virtual void write(uint8_t); +#endif + void command(uint8_t); +private: + void send(uint8_t value, boolean mode); + void write4bits(uint8_t); + void write8bits(uint8_t); + void pulseEnable(); + void _digitalWrite(uint8_t, uint8_t); + void _pinMode(uint8_t, uint8_t); + + uint8_t _rs_pin; // LOW: command. HIGH: character. + uint8_t _rw_pin; // LOW: write to LCD. HIGH: read from LCD. + uint8_t _enable_pin; // activated by a HIGH pulse. + uint8_t _data_pins[8]; + + uint8_t _displayfunction; + uint8_t _displaycontrol; + uint8_t _displaymode; + + uint8_t _initialized; + + uint8_t _numlines,_currline; + + uint8_t _SPIclock, _SPIdata, _SPIlatch; + uint8_t _SPIbuff; + + uint8_t _i2cAddr; + Adafruit_MCP23008 _i2c; +}; + +#endif diff --git a/libraries/Adafruit_LiquidCrystal/README.md b/libraries/Adafruit_LiquidCrystal/README.md new file mode 100755 index 0000000..99584c2 --- /dev/null +++ b/libraries/Adafruit_LiquidCrystal/README.md @@ -0,0 +1,37 @@ +# Adafruit_LiquidCrystal + +This library has been renamed Adafruit_LiquidCrystal so as not to conflict with LiquidCrystal. Also, it now works with tiny85's if you have Adafruit AVR board pkg 1.4.3+ + + + +## Compatibility + +MCU | Tested Works | Doesn't Work | Not Tested | Notes +----------------- | :----------: | :----------: | :---------: | ----- +Atmega328 @ 16MHz | X | | | +Atmega328 @ 12MHz | X | | | For SPI, Pro Trinket has no pin 2, can move to pin 5. +Atmega32u4 @ 16MHz | X | | | +Atmega32u4 @ 8MHz | X | | | +ESP8266 | X | | | For SPI, ESP8266 has no pin 3, moved to pin 5. +Atmega2560 @ 16MHz | X | | | +ATSAM3X8E | X | | | +ATSAM21D | | X | | I2C works, use SDA and SCL pins. For SPI, LCD blinks once on start up. +ATtiny85 @ 16MHz | | X | | Use TinyLiquidCrystal libray instead: https://github.com/adafruit/TinyLiquidCrystal + +I2C uses SDA & SCL pins 0 & 2 +ATtiny85 @ 8MHz | | X | | Use TinyLiquidCrystal libray instead: https://github.com/adafruit/TinyLiquidCrystal + +I2C uses SDA & SCL pins 0 & 2 + + * ATmega328 @ 16MHz : Arduino UNO, Adafruit Pro Trinket 5V, Adafruit Metro 328, Adafruit Metro Mini + * ATmega328 @ 12MHz : Adafruit Pro Trinket 3V + * ATmega32u4 @ 16MHz : Arduino Leonardo, Arduino Micro, Arduino Yun, Teensy 2.0 + * ATmega32u4 @ 8MHz : Adafruit Flora, Bluefruit Micro + * ESP8266 : Adafruit Huzzah + * ATmega2560 @ 16MHz : Arduino Mega + * ATSAM3X8E : Arduino Due + * ATSAM21D : Arduino Zero, M0 Pro + * ATtiny85 @ 16MHz : Adafruit Trinket 5V + * ATtiny85 @ 8MHz : Adafruit Gemma, Arduino Gemma, Adafruit Trinket 3V + + diff --git a/libraries/Adafruit_LiquidCrystal/keywords.txt b/libraries/Adafruit_LiquidCrystal/keywords.txt new file mode 100755 index 0000000..6c67c51 --- /dev/null +++ b/libraries/Adafruit_LiquidCrystal/keywords.txt @@ -0,0 +1,38 @@ +####################################### +# Syntax Coloring Map For LiquidCrystal +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +Adafruit_LiquidCrystal KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +begin KEYWORD2 +clear KEYWORD2 +home KEYWORD2 +print KEYWORD2 +setCursor KEYWORD2 +cursor KEYWORD2 +noCursor KEYWORD2 +blink KEYWORD2 +noBlink KEYWORD2 +display KEYWORD2 +noDisplay KEYWORD2 +autoscroll KEYWORD2 +noAutoscroll KEYWORD2 +leftToRight KEYWORD2 +rightToLeft KEYWORD2 +scrollDisplayLeft KEYWORD2 +scrollDisplayRight KEYWORD2 +createChar KEYWORD2 +setBacklight KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### + diff --git a/libraries/Adafruit_LiquidCrystal/library.properties b/libraries/Adafruit_LiquidCrystal/library.properties new file mode 100755 index 0000000..071b6d5 --- /dev/null +++ b/libraries/Adafruit_LiquidCrystal/library.properties @@ -0,0 +1,9 @@ +name=Adafruit LiquidCrystal +version=1.0.0 +author=Adafruit +maintainer=Adafruit +sentence=Fork of LiquidCrystal HD44780-compatible LCD driver library, now with support for ATtiny85. +paragraph=Fork of LiquidCrystal HD44780-compatible LCD driver library, now with support for ATtiny85. +category=Display +url=https://github.com/adafruit/LiquidCrystal/ +architectures=* diff --git a/libraries/Adafruit_LiquidCrystal/utility/Adafruit_MCP23008.cpp b/libraries/Adafruit_LiquidCrystal/utility/Adafruit_MCP23008.cpp new file mode 100755 index 0000000..c85dfe4 --- /dev/null +++ b/libraries/Adafruit_LiquidCrystal/utility/Adafruit_MCP23008.cpp @@ -0,0 +1,175 @@ +/*************************************************** + This is a library for the MCP23008 i2c port expander + + These displays use I2C to communicate, 2 pins are required to + interface + Adafruit invests time and resources providing this open source code, + please support Adafruit and open-source hardware by purchasing + products from Adafruit! + + Written by Limor Fried/Ladyada for Adafruit Industries. + BSD license, all text above must be included in any redistribution + ****************************************************/ + +#if ARDUINO >= 100 + #include "Arduino.h" +#else + #include "WProgram.h" +#endif +#include +#include "Adafruit_MCP23008.h" + + +//////////////////////////////////////////////////////////////////////////////// +// RTC_DS1307 implementation + +void Adafruit_MCP23008::begin(uint8_t addr) { + addr &= 7; + + i2caddr = addr; + + Wire.begin(); + + // set defaults! + Wire.beginTransmission(MCP23008_ADDRESS | i2caddr); +#if ARDUINO >= 100 + Wire.write((byte)MCP23008_IODIR); + Wire.write((byte)0xFF); // all inputs + Wire.write((byte)0x00); + Wire.write((byte)0x00); + Wire.write((byte)0x00); + Wire.write((byte)0x00); + Wire.write((byte)0x00); + Wire.write((byte)0x00); + Wire.write((byte)0x00); + Wire.write((byte)0x00); + Wire.write((byte)0x00); +#else + Wire.send(MCP23008_IODIR); + Wire.send(0xFF); // all inputs + Wire.send(0x00); + Wire.send(0x00); + Wire.send(0x00); + Wire.send(0x00); + Wire.send(0x00); + Wire.send(0x00); + Wire.send(0x00); + Wire.send(0x00); + Wire.send(0x00); +#endif + Wire.endTransmission(); + +} + +void Adafruit_MCP23008::begin(void) { + begin(0); +} + +void Adafruit_MCP23008::pinMode(uint8_t p, uint8_t d) { + uint8_t iodir; + + + // only 8 bits! + if (p > 7) + return; + + iodir = read8(MCP23008_IODIR); + + // set the pin and direction + if (d == INPUT) { + iodir |= 1 << p; + } else { + iodir &= ~(1 << p); + } + + // write the new IODIR + write8(MCP23008_IODIR, iodir); +} + +uint8_t Adafruit_MCP23008::readGPIO(void) { + // read the current GPIO input + return read8(MCP23008_GPIO); +} + +void Adafruit_MCP23008::writeGPIO(uint8_t gpio) { + write8(MCP23008_GPIO, gpio); +} + + +void Adafruit_MCP23008::digitalWrite(uint8_t p, uint8_t d) { + uint8_t gpio; + + // only 8 bits! + if (p > 7) + return; + + // read the current GPIO output latches + gpio = readGPIO(); + + // set the pin and direction + if (d == HIGH) { + gpio |= 1 << p; + } else { + gpio &= ~(1 << p); + } + + // write the new GPIO + writeGPIO(gpio); +} + +void Adafruit_MCP23008::pullUp(uint8_t p, uint8_t d) { + uint8_t gppu; + + // only 8 bits! + if (p > 7) + return; + + gppu = read8(MCP23008_GPPU); + // set the pin and direction + if (d == HIGH) { + gppu |= 1 << p; + } else { + gppu &= ~(1 << p); + } + // write the new GPIO + write8(MCP23008_GPPU, gppu); +} + +uint8_t Adafruit_MCP23008::digitalRead(uint8_t p) { + // only 8 bits! + if (p > 7) + return 0; + + // read the current GPIO + return (readGPIO() >> p) & 0x1; +} + +uint8_t Adafruit_MCP23008::read8(uint8_t addr) { + Wire.beginTransmission(MCP23008_ADDRESS | i2caddr); +#if ARDUINO >= 100 + Wire.write((byte)addr); +#else + Wire.send(addr); +#endif + Wire.endTransmission(); + Wire.requestFrom(MCP23008_ADDRESS | i2caddr, 1); + +#if ARDUINO >= 100 + return Wire.read(); +#else + return Wire.receive(); +#endif +} + + +void Adafruit_MCP23008::write8(uint8_t addr, uint8_t data) { + Wire.beginTransmission(MCP23008_ADDRESS | i2caddr); +#if ARDUINO >= 100 + Wire.write((byte)addr); + Wire.write((byte)data); +#else + Wire.send(addr); + Wire.send(data); +#endif + Wire.endTransmission(); +} diff --git a/libraries/Adafruit_LiquidCrystal/utility/Adafruit_MCP23008.h b/libraries/Adafruit_LiquidCrystal/utility/Adafruit_MCP23008.h new file mode 100755 index 0000000..fe13d4d --- /dev/null +++ b/libraries/Adafruit_LiquidCrystal/utility/Adafruit_MCP23008.h @@ -0,0 +1,50 @@ +/*************************************************** + This is a library for the MCP23008 i2c port expander + + These displays use I2C to communicate, 2 pins are required to + interface + Adafruit invests time and resources providing this open source code, + please support Adafruit and open-source hardware by purchasing + products from Adafruit! + + Written by Limor Fried/Ladyada for Adafruit Industries. + BSD license, all text above must be included in any redistribution + ****************************************************/ + +#ifndef _ADAFRUIT_MCP23008_H +#define _ADAFRUIT_MCP23008_H +// Don't forget the Wire library +class Adafruit_MCP23008 { +public: + void begin(uint8_t addr); + void begin(void); + + void pinMode(uint8_t p, uint8_t d); + void digitalWrite(uint8_t p, uint8_t d); + void pullUp(uint8_t p, uint8_t d); + uint8_t digitalRead(uint8_t p); + uint8_t readGPIO(void); + void writeGPIO(uint8_t); + + private: + uint8_t i2caddr; + uint8_t read8(uint8_t addr); + void write8(uint8_t addr, uint8_t data); +}; + +#define MCP23008_ADDRESS 0x20 + +// registers +#define MCP23008_IODIR 0x00 +#define MCP23008_IPOL 0x01 +#define MCP23008_GPINTEN 0x02 +#define MCP23008_DEFVAL 0x03 +#define MCP23008_INTCON 0x04 +#define MCP23008_IOCON 0x05 +#define MCP23008_GPPU 0x06 +#define MCP23008_INTF 0x07 +#define MCP23008_INTCAP 0x08 +#define MCP23008_GPIO 0x09 +#define MCP23008_OLAT 0x0A + +#endif diff --git a/libraries/Adafruit_MCP23017/Adafruit_MCP23017.cpp b/libraries/Adafruit_MCP23017/Adafruit_MCP23017.cpp new file mode 100755 index 0000000..cb0bee4 --- /dev/null +++ b/libraries/Adafruit_MCP23017/Adafruit_MCP23017.cpp @@ -0,0 +1,230 @@ +/*************************************************** + This is a library for the MCP23017 i2c port expander + + These displays use I2C to communicate, 2 pins are required to + interface + Adafruit invests time and resources providing this open source code, + please support Adafruit and open-source hardware by purchasing + products from Adafruit! + + Written by Limor Fried/Ladyada for Adafruit Industries. + BSD license, all text above must be included in any redistribution + ****************************************************/ + +#include +#include +#include "Adafruit_MCP23017.h" + +#if ARDUINO >= 100 + #include "Arduino.h" +#else + #include "WProgram.h" +#endif + +// minihelper +static inline void wiresend(uint8_t x) { +#if ARDUINO >= 100 + Wire.write((uint8_t)x); +#else + Wire.send(x); +#endif +} + +static inline uint8_t wirerecv(void) { +#if ARDUINO >= 100 + return Wire.read(); +#else + return Wire.receive(); +#endif +} + +//////////////////////////////////////////////////////////////////////////////// + +void Adafruit_MCP23017::begin(uint8_t addr) { + if (addr > 7) { + addr = 7; + } + i2caddr = addr; + + Wire.begin(); + + + // set defaults! + Wire.beginTransmission(MCP23017_ADDRESS | i2caddr); + wiresend(MCP23017_IODIRA); + wiresend(0xFF); // all inputs on port A + Wire.endTransmission(); + + Wire.beginTransmission(MCP23017_ADDRESS | i2caddr); + wiresend(MCP23017_IODIRB); + wiresend(0xFF); // all inputs on port B + Wire.endTransmission(); +} + + +void Adafruit_MCP23017::begin(void) { + begin(0); +} + +void Adafruit_MCP23017::pinMode(uint8_t p, uint8_t d) { + uint8_t iodir; + uint8_t iodiraddr; + + // only 16 bits! + if (p > 15) + return; + + if (p < 8) + iodiraddr = MCP23017_IODIRA; + else { + iodiraddr = MCP23017_IODIRB; + p -= 8; + } + + // read the current IODIR + Wire.beginTransmission(MCP23017_ADDRESS | i2caddr); + wiresend(iodiraddr); + Wire.endTransmission(); + + Wire.requestFrom(MCP23017_ADDRESS | i2caddr, 1); + iodir = wirerecv(); + + // set the pin and direction + if (d == INPUT) { + iodir |= 1 << p; + } else { + iodir &= ~(1 << p); + } + + // write the new IODIR + Wire.beginTransmission(MCP23017_ADDRESS | i2caddr); + wiresend(iodiraddr); + wiresend(iodir); + Wire.endTransmission(); +} + +uint16_t Adafruit_MCP23017::readGPIOAB() { + uint16_t ba = 0; + uint8_t a; + + // read the current GPIO output latches + Wire.beginTransmission(MCP23017_ADDRESS | i2caddr); + wiresend(MCP23017_GPIOA); + Wire.endTransmission(); + + Wire.requestFrom(MCP23017_ADDRESS | i2caddr, 2); + a = wirerecv(); + ba = wirerecv(); + ba <<= 8; + ba |= a; + + return ba; +} + +void Adafruit_MCP23017::writeGPIOAB(uint16_t ba) { + Wire.beginTransmission(MCP23017_ADDRESS | i2caddr); + wiresend(MCP23017_GPIOA); + wiresend(ba & 0xFF); + wiresend(ba >> 8); + Wire.endTransmission(); +} + +void Adafruit_MCP23017::digitalWrite(uint8_t p, uint8_t d) { + uint8_t gpio; + uint8_t gpioaddr, olataddr; + + // only 16 bits! + if (p > 15) + return; + + if (p < 8) { + olataddr = MCP23017_OLATA; + gpioaddr = MCP23017_GPIOA; + } else { + olataddr = MCP23017_OLATB; + gpioaddr = MCP23017_GPIOB; + p -= 8; + } + + // read the current GPIO output latches + Wire.beginTransmission(MCP23017_ADDRESS | i2caddr); + wiresend(olataddr); + Wire.endTransmission(); + + Wire.requestFrom(MCP23017_ADDRESS | i2caddr, 1); + gpio = wirerecv(); + + // set the pin and direction + if (d == HIGH) { + gpio |= 1 << p; + } else { + gpio &= ~(1 << p); + } + + // write the new GPIO + Wire.beginTransmission(MCP23017_ADDRESS | i2caddr); + wiresend(gpioaddr); + wiresend(gpio); + Wire.endTransmission(); +} + +void Adafruit_MCP23017::pullUp(uint8_t p, uint8_t d) { + uint8_t gppu; + uint8_t gppuaddr; + + // only 16 bits! + if (p > 15) + return; + + if (p < 8) + gppuaddr = MCP23017_GPPUA; + else { + gppuaddr = MCP23017_GPPUB; + p -= 8; + } + + + // read the current pullup resistor set + Wire.beginTransmission(MCP23017_ADDRESS | i2caddr); + wiresend(gppuaddr); + Wire.endTransmission(); + + Wire.requestFrom(MCP23017_ADDRESS | i2caddr, 1); + gppu = wirerecv(); + + // set the pin and direction + if (d == HIGH) { + gppu |= 1 << p; + } else { + gppu &= ~(1 << p); + } + + // write the new GPIO + Wire.beginTransmission(MCP23017_ADDRESS | i2caddr); + wiresend(gppuaddr); + wiresend(gppu); + Wire.endTransmission(); +} + +uint8_t Adafruit_MCP23017::digitalRead(uint8_t p) { + uint8_t gpioaddr; + + // only 16 bits! + if (p > 15) + return 0; + + if (p < 8) + gpioaddr = MCP23017_GPIOA; + else { + gpioaddr = MCP23017_GPIOB; + p -= 8; + } + + // read the current GPIO + Wire.beginTransmission(MCP23017_ADDRESS | i2caddr); + wiresend(gpioaddr); + Wire.endTransmission(); + + Wire.requestFrom(MCP23017_ADDRESS | i2caddr, 1); + return (wirerecv() >> p) & 0x1; +} diff --git a/libraries/Adafruit_MCP23017/Adafruit_MCP23017.h b/libraries/Adafruit_MCP23017/Adafruit_MCP23017.h new file mode 100755 index 0000000..0e9ff5c --- /dev/null +++ b/libraries/Adafruit_MCP23017/Adafruit_MCP23017.h @@ -0,0 +1,63 @@ +/*************************************************** + This is a library for the MCP23017 i2c port expander + + These displays use I2C to communicate, 2 pins are required to + interface + Adafruit invests time and resources providing this open source code, + please support Adafruit and open-source hardware by purchasing + products from Adafruit! + + Written by Limor Fried/Ladyada for Adafruit Industries. + BSD license, all text above must be included in any redistribution + ****************************************************/ + +#ifndef _Adafruit_MCP23017_H_ +#define _Adafruit_MCP23017_H_ + +// Don't forget the Wire library +class Adafruit_MCP23017 { +public: + void begin(uint8_t addr); + void begin(void); + + void pinMode(uint8_t p, uint8_t d); + void digitalWrite(uint8_t p, uint8_t d); + void pullUp(uint8_t p, uint8_t d); + uint8_t digitalRead(uint8_t p); + + void writeGPIOAB(uint16_t); + uint16_t readGPIOAB(); + + private: + uint8_t i2caddr; +}; + +#define MCP23017_ADDRESS 0x20 + +// registers +#define MCP23017_IODIRA 0x00 +#define MCP23017_IPOLA 0x02 +#define MCP23017_GPINTENA 0x04 +#define MCP23017_DEFVALA 0x06 +#define MCP23017_INTCONA 0x08 +#define MCP23017_IOCONA 0x0A +#define MCP23017_GPPUA 0x0C +#define MCP23017_INTFA 0x0E +#define MCP23017_INTCAPA 0x10 +#define MCP23017_GPIOA 0x12 +#define MCP23017_OLATA 0x14 + + +#define MCP23017_IODIRB 0x01 +#define MCP23017_IPOLB 0x03 +#define MCP23017_GPINTENB 0x05 +#define MCP23017_DEFVALB 0x07 +#define MCP23017_INTCONB 0x09 +#define MCP23017_IOCONB 0x0B +#define MCP23017_GPPUB 0x0D +#define MCP23017_INTFB 0x0F +#define MCP23017_INTCAPB 0x11 +#define MCP23017_GPIOB 0x13 +#define MCP23017_OLATB 0x15 + +#endif diff --git a/libraries/Adafruit_RGBLCDShield/Adafruit_RGBLCDShield.cpp b/libraries/Adafruit_RGBLCDShield/Adafruit_RGBLCDShield.cpp new file mode 100755 index 0000000..ba0dee7 --- /dev/null +++ b/libraries/Adafruit_RGBLCDShield/Adafruit_RGBLCDShield.cpp @@ -0,0 +1,431 @@ +/*************************************************** + This is a library for the Adafruit RGB 16x2 LCD Shield + Pick one up at the Adafruit shop! + ---------> http://http://www.adafruit.com/products/714 + + The shield uses I2C to communicate, 2 pins are required to + interface + Adafruit invests time and resources providing this open source code, + please support Adafruit and open-source hardware by purchasing + products from Adafruit! + + Written by Limor Fried/Ladyada for Adafruit Industries. + BSD license, all text above must be included in any redistribution + ****************************************************/ + + +#include "Adafruit_RGBLCDShield.h" + +#include +#include +#include +#include + +#if ARDUINO >= 100 + #include "Arduino.h" +#else + #include "WProgram.h" +#endif + +// When the display powers up, it is configured as follows: +// +// 1. Display clear +// 2. Function set: +// DL = 1; 8-bit interface data +// N = 0; 1-line display +// F = 0; 5x8 dot character font +// 3. Display on/off control: +// D = 0; Display off +// C = 0; Cursor off +// B = 0; Blinking off +// 4. Entry mode set: +// I/D = 1; Increment by 1 +// S = 0; No shift +// +// Note, however, that resetting the Arduino doesn't reset the LCD, so we +// can't assume that its in that state when a sketch starts (and the +// RGBLCDShield constructor is called). + +Adafruit_RGBLCDShield::Adafruit_RGBLCDShield() { + _i2cAddr = 0; + + _displayfunction = LCD_4BITMODE | LCD_1LINE | LCD_5x8DOTS; + + // the I/O expander pinout + _rs_pin = 15; + _rw_pin = 14; + _enable_pin = 13; + _data_pins[0] = 12; // really d4 + _data_pins[1] = 11; // really d5 + _data_pins[2] = 10; // really d6 + _data_pins[3] = 9; // really d7 + + _button_pins[0] = 0; + _button_pins[1] = 1; + _button_pins[2] = 2; + _button_pins[3] = 3; + _button_pins[4] = 4; + // we can't begin() yet :( +} + + + + +void Adafruit_RGBLCDShield::init(uint8_t fourbitmode, uint8_t rs, uint8_t rw, uint8_t enable, + uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3, + uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7) +{ + _rs_pin = rs; + _rw_pin = rw; + _enable_pin = enable; + + _data_pins[0] = d0; + _data_pins[1] = d1; + _data_pins[2] = d2; + _data_pins[3] = d3; + _data_pins[4] = d4; + _data_pins[5] = d5; + _data_pins[6] = d6; + _data_pins[7] = d7; + + _i2cAddr = 255; + + _pinMode(_rs_pin, OUTPUT); + // we can save 1 pin by not using RW. Indicate by passing 255 instead of pin# + if (_rw_pin != 255) { + _pinMode(_rw_pin, OUTPUT); + } + _pinMode(_enable_pin, OUTPUT); + + + if (fourbitmode) + _displayfunction = LCD_4BITMODE | LCD_1LINE | LCD_5x8DOTS; + else + _displayfunction = LCD_8BITMODE | LCD_1LINE | LCD_5x8DOTS; + + begin(16, 1); +} + +void Adafruit_RGBLCDShield::begin(uint8_t cols, uint8_t lines, uint8_t dotsize) { + // check if i2c + if (_i2cAddr != 255) { + //_i2c.begin(_i2cAddr); + Wire.begin(); + _i2c.begin(); + + _i2c.pinMode(8, OUTPUT); + _i2c.pinMode(6, OUTPUT); + _i2c.pinMode(7, OUTPUT); + setBacklight(0x7); + + if (_rw_pin) + _i2c.pinMode(_rw_pin, OUTPUT); + + _i2c.pinMode(_rs_pin, OUTPUT); + _i2c.pinMode(_enable_pin, OUTPUT); + for (uint8_t i=0; i<4; i++) + _i2c.pinMode(_data_pins[i], OUTPUT); + + for (uint8_t i=0; i<5; i++) { + _i2c.pinMode(_button_pins[i], INPUT); + _i2c.pullUp(_button_pins[i], 1); + } + } + + if (lines > 1) { + _displayfunction |= LCD_2LINE; + } + _numlines = lines; + _currline = 0; + + // for some 1 line displays you can select a 10 pixel high font + if ((dotsize != 0) && (lines == 1)) { + _displayfunction |= LCD_5x10DOTS; + } + + // SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION! + // according to datasheet, we need at least 40ms after power rises above 2.7V + // before sending commands. Arduino can turn on way befer 4.5V so we'll wait 50 + delayMicroseconds(50000); + // Now we pull both RS and R/W low to begin commands + _digitalWrite(_rs_pin, LOW); + _digitalWrite(_enable_pin, LOW); + if (_rw_pin != 255) { + _digitalWrite(_rw_pin, LOW); + } + + //put the LCD into 4 bit or 8 bit mode + if (! (_displayfunction & LCD_8BITMODE)) { + // this is according to the hitachi HD44780 datasheet + // figure 24, pg 46 + + // we start in 8bit mode, try to set 4 bit mode + write4bits(0x03); + delayMicroseconds(4500); // wait min 4.1ms + + // second try + write4bits(0x03); + delayMicroseconds(4500); // wait min 4.1ms + + // third go! + write4bits(0x03); + delayMicroseconds(150); + + // finally, set to 8-bit interface + write4bits(0x02); + } else { + // this is according to the hitachi HD44780 datasheet + // page 45 figure 23 + + // Send function set command sequence + command(LCD_FUNCTIONSET | _displayfunction); + delayMicroseconds(4500); // wait more than 4.1ms + + // second try + command(LCD_FUNCTIONSET | _displayfunction); + delayMicroseconds(150); + + // third go + command(LCD_FUNCTIONSET | _displayfunction); + } + + // finally, set # lines, font size, etc. + command(LCD_FUNCTIONSET | _displayfunction); + + // turn the display on with no cursor or blinking default + _displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF; + display(); + + // clear it off + clear(); + + // Initialize to default text direction (for romance languages) + _displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT; + // set the entry mode + command(LCD_ENTRYMODESET | _displaymode); + +} + +/********** high level commands, for the user! */ +void Adafruit_RGBLCDShield::clear() +{ + command(LCD_CLEARDISPLAY); // clear display, set cursor position to zero + delayMicroseconds(2000); // this command takes a long time! +} + +void Adafruit_RGBLCDShield::home() +{ + command(LCD_RETURNHOME); // set cursor position to zero + delayMicroseconds(2000); // this command takes a long time! +} + +void Adafruit_RGBLCDShield::setCursor(uint8_t col, uint8_t row) +{ + int row_offsets[] = { 0x00, 0x40, 0x14, 0x54 }; + if ( row > _numlines ) { + row = _numlines-1; // we count rows starting w/0 + } + + command(LCD_SETDDRAMADDR | (col + row_offsets[row])); +} + +// Turn the display on/off (quickly) +void Adafruit_RGBLCDShield::noDisplay() { + _displaycontrol &= ~LCD_DISPLAYON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} +void Adafruit_RGBLCDShield::display() { + _displaycontrol |= LCD_DISPLAYON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} + +// Turns the underline cursor on/off +void Adafruit_RGBLCDShield::noCursor() { + _displaycontrol &= ~LCD_CURSORON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} +void Adafruit_RGBLCDShield::cursor() { + _displaycontrol |= LCD_CURSORON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} + +// Turn on and off the blinking cursor +void Adafruit_RGBLCDShield::noBlink() { + _displaycontrol &= ~LCD_BLINKON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} +void Adafruit_RGBLCDShield::blink() { + _displaycontrol |= LCD_BLINKON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} + +// These commands scroll the display without changing the RAM +void Adafruit_RGBLCDShield::scrollDisplayLeft(void) { + command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT); +} +void Adafruit_RGBLCDShield::scrollDisplayRight(void) { + command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT); +} + +// This is for text that flows Left to Right +void Adafruit_RGBLCDShield::leftToRight(void) { + _displaymode |= LCD_ENTRYLEFT; + command(LCD_ENTRYMODESET | _displaymode); +} + +// This is for text that flows Right to Left +void Adafruit_RGBLCDShield::rightToLeft(void) { + _displaymode &= ~LCD_ENTRYLEFT; + command(LCD_ENTRYMODESET | _displaymode); +} + +// This will 'right justify' text from the cursor +void Adafruit_RGBLCDShield::autoscroll(void) { + _displaymode |= LCD_ENTRYSHIFTINCREMENT; + command(LCD_ENTRYMODESET | _displaymode); +} + +// This will 'left justify' text from the cursor +void Adafruit_RGBLCDShield::noAutoscroll(void) { + _displaymode &= ~LCD_ENTRYSHIFTINCREMENT; + command(LCD_ENTRYMODESET | _displaymode); +} + +// Allows us to fill the first 8 CGRAM locations +// with custom characters +void Adafruit_RGBLCDShield::createChar(uint8_t location, uint8_t charmap[]) { + location &= 0x7; // we only have 8 locations 0-7 + command(LCD_SETCGRAMADDR | (location << 3)); + for (int i=0; i<8; i++) { + write(charmap[i]); + } + command(LCD_SETDDRAMADDR); // unfortunately resets the location to 0,0 +} + +/*********** mid level commands, for sending data/cmds */ + +inline void Adafruit_RGBLCDShield::command(uint8_t value) { + send(value, LOW); +} + +#if ARDUINO >= 100 +inline size_t Adafruit_RGBLCDShield::write(uint8_t value) { + send(value, HIGH); + return 1; +} +#else +inline void Adafruit_RGBLCDShield::write(uint8_t value) { + send(value, HIGH); +} +#endif + +/************ low level data pushing commands **********/ + +// little wrapper for i/o writes +void Adafruit_RGBLCDShield::_digitalWrite(uint8_t p, uint8_t d) { + if (_i2cAddr != 255) { + // an i2c command + _i2c.digitalWrite(p, d); + } else { + // straightup IO + digitalWrite(p, d); + } +} + +// Allows to set the backlight, if the LCD backpack is used +void Adafruit_RGBLCDShield::setBacklight(uint8_t status) { + // check if i2c or SPI + _i2c.digitalWrite(8, ~(status >> 2) & 0x1); + _i2c.digitalWrite(7, ~(status >> 1) & 0x1); + _i2c.digitalWrite(6, ~status & 0x1); +} + +// little wrapper for i/o directions +void Adafruit_RGBLCDShield::_pinMode(uint8_t p, uint8_t d) { + if (_i2cAddr != 255) { + // an i2c command + _i2c.pinMode(p, d); + } else { + // straightup IO + pinMode(p, d); + } +} + +// write either command or data, with automatic 4/8-bit selection +void Adafruit_RGBLCDShield::send(uint8_t value, uint8_t mode) { + _digitalWrite(_rs_pin, mode); + + // if there is a RW pin indicated, set it low to Write + if (_rw_pin != 255) { + _digitalWrite(_rw_pin, LOW); + } + + if (_displayfunction & LCD_8BITMODE) { + write8bits(value); + } else { + write4bits(value>>4); + write4bits(value); + } +} + +void Adafruit_RGBLCDShield::pulseEnable(void) { + _digitalWrite(_enable_pin, LOW); + delayMicroseconds(1); + _digitalWrite(_enable_pin, HIGH); + delayMicroseconds(1); // enable pulse must be >450ns + _digitalWrite(_enable_pin, LOW); + delayMicroseconds(100); // commands need > 37us to settle +} + +void Adafruit_RGBLCDShield::write4bits(uint8_t value) { + if (_i2cAddr != 255) { + uint16_t out = 0; + + out = _i2c.readGPIOAB(); + + // speed up for i2c since its sluggish + for (int i = 0; i < 4; i++) { + out &= ~_BV(_data_pins[i]); + out |= ((value >> i) & 0x1) << _data_pins[i]; + } + + // make sure enable is low + out &= ~ _BV(_enable_pin); + + _i2c.writeGPIOAB(out); + + // pulse enable + delayMicroseconds(1); + out |= _BV(_enable_pin); + _i2c.writeGPIOAB(out); + delayMicroseconds(1); + out &= ~_BV(_enable_pin); + _i2c.writeGPIOAB(out); + delayMicroseconds(100); + + } else { + for (int i = 0; i < 4; i++) { + _pinMode(_data_pins[i], OUTPUT); + _digitalWrite(_data_pins[i], (value >> i) & 0x01); + } + pulseEnable(); + } +} + +void Adafruit_RGBLCDShield::write8bits(uint8_t value) { + for (int i = 0; i < 8; i++) { + _pinMode(_data_pins[i], OUTPUT); + _digitalWrite(_data_pins[i], (value >> i) & 0x01); + } + + pulseEnable(); +} + +uint8_t Adafruit_RGBLCDShield::readButtons(void) { + uint8_t reply = 0x1F; + + for (uint8_t i=0; i<5; i++) { + reply &= ~((_i2c.digitalRead(_button_pins[i])) << i); + } + return reply; +} diff --git a/libraries/Adafruit_RGBLCDShield/Adafruit_RGBLCDShield.h b/libraries/Adafruit_RGBLCDShield/Adafruit_RGBLCDShield.h new file mode 100755 index 0000000..b265d7f --- /dev/null +++ b/libraries/Adafruit_RGBLCDShield/Adafruit_RGBLCDShield.h @@ -0,0 +1,132 @@ +/*************************************************** + This is a library for the Adafruit RGB 16x2 LCD Shield + Pick one up at the Adafruit shop! + ---------> http://http://www.adafruit.com/products/714 + + The shield uses I2C to communicate, 2 pins are required to + interface + Adafruit invests time and resources providing this open source code, + please support Adafruit and open-source hardware by purchasing + products from Adafruit! + + Written by Limor Fried/Ladyada for Adafruit Industries. + BSD license, all text above must be included in any redistribution + ****************************************************/ + +#ifndef Adafruit_RGBLCDShield_h +#define Adafruit_RGBLCDShield_h + +#include +#include "Print.h" +#include + +// commands +#define LCD_CLEARDISPLAY 0x01 +#define LCD_RETURNHOME 0x02 +#define LCD_ENTRYMODESET 0x04 +#define LCD_DISPLAYCONTROL 0x08 +#define LCD_CURSORSHIFT 0x10 +#define LCD_FUNCTIONSET 0x20 +#define LCD_SETCGRAMADDR 0x40 +#define LCD_SETDDRAMADDR 0x80 + +// flags for display entry mode +#define LCD_ENTRYRIGHT 0x00 +#define LCD_ENTRYLEFT 0x02 +#define LCD_ENTRYSHIFTINCREMENT 0x01 +#define LCD_ENTRYSHIFTDECREMENT 0x00 + +// flags for display on/off control +#define LCD_DISPLAYON 0x04 +#define LCD_DISPLAYOFF 0x00 +#define LCD_CURSORON 0x02 +#define LCD_CURSOROFF 0x00 +#define LCD_BLINKON 0x01 +#define LCD_BLINKOFF 0x00 + +// flags for display/cursor shift +#define LCD_DISPLAYMOVE 0x08 +#define LCD_CURSORMOVE 0x00 +#define LCD_MOVERIGHT 0x04 +#define LCD_MOVELEFT 0x00 + +// flags for function set +#define LCD_8BITMODE 0x10 +#define LCD_4BITMODE 0x00 +#define LCD_2LINE 0x08 +#define LCD_1LINE 0x00 +#define LCD_5x10DOTS 0x04 +#define LCD_5x8DOTS 0x00 + +#define BUTTON_UP 0x08 +#define BUTTON_DOWN 0x04 +#define BUTTON_LEFT 0x10 +#define BUTTON_RIGHT 0x02 +#define BUTTON_SELECT 0x01 + + +class Adafruit_RGBLCDShield : public Print { +public: + Adafruit_RGBLCDShield(); + + void init(uint8_t fourbitmode, uint8_t rs, uint8_t rw, uint8_t enable, + uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3, + uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7); + + void begin(uint8_t cols, uint8_t rows, uint8_t charsize = LCD_5x8DOTS); + + void clear(); + void home(); + + void noDisplay(); + void display(); + void noBlink(); + void blink(); + void noCursor(); + void cursor(); + void scrollDisplayLeft(); + void scrollDisplayRight(); + void leftToRight(); + void rightToLeft(); + void autoscroll(); + void noAutoscroll(); + + // only if using backpack + void setBacklight(uint8_t status); + + void createChar(uint8_t, uint8_t[]); + void setCursor(uint8_t, uint8_t); +#if ARDUINO >= 100 + virtual size_t write(uint8_t); +#else + virtual void write(uint8_t); +#endif + void command(uint8_t); + uint8_t readButtons(); + +private: + void send(uint8_t, uint8_t); + void write4bits(uint8_t); + void write8bits(uint8_t); + void pulseEnable(); + void _digitalWrite(uint8_t, uint8_t); + void _pinMode(uint8_t, uint8_t); + + uint8_t _rs_pin; // LOW: command. HIGH: character. + uint8_t _rw_pin; // LOW: write to LCD. HIGH: read from LCD. + uint8_t _enable_pin; // activated by a HIGH pulse. + uint8_t _data_pins[8]; + uint8_t _button_pins[5]; + uint8_t _displayfunction; + uint8_t _displaycontrol; + uint8_t _displaymode; + + uint8_t _initialized; + + uint8_t _numlines,_currline; + + uint8_t _i2cAddr; + Adafruit_MCP23017 _i2c; +}; + +#endif diff --git a/libraries/BasicTerm/BasicTerm.cpp b/libraries/BasicTerm/BasicTerm.cpp new file mode 100755 index 0000000..e88e53c --- /dev/null +++ b/libraries/BasicTerm/BasicTerm.cpp @@ -0,0 +1,137 @@ +/* + * BasicTerm.cpp + * Provides basic ANSI/VT220 terminal control over a serial interface + * Copyright 2011 Trannie Carter + * Licensed for use under the terms of the GNU Lesser General Public License v3 + */ + +#include "BasicTerm.h" + +BasicTerm::BasicTerm(Stream *ser) { + serial = ser; +} + +int BasicTerm::available(void) { + return serial->available(); +} + +int BasicTerm::peek(void) { + return serial->peek(); +} + +int BasicTerm::read(void) { + return serial->read(); +} + +void BasicTerm::flush(void) { + serial->flush(); +} + +size_t BasicTerm::write(uint8_t c) { + return serial->write(c); +} + +void BasicTerm::init(void) { + serial->print(F("\x1b\x63")); +} + +void BasicTerm::cls(void) { + serial->print(F("\x1b[2J")); +} + +void BasicTerm::position(uint8_t row, uint8_t col) { + serial->print(F("\x1b[")); + serial->print((uint8_t)row + 1); + serial->print(F(";")); + serial->print((uint8_t)col + 1); + serial->print(F("H")); +} + +void BasicTerm::show_cursor(boolean show) { + if(show) { + serial->print(F("\x1b[?25h")); + } else { + serial->print(F("\x1b[?25l")); + } +} + +int16_t BasicTerm::get_key(void) { + int16_t key; + uint16_t when; + + key = serial->read(); + + if(key == 0x1b) { /* escape sequence */ + when = millis(); + while(serial->available() < 2) { + if(((uint16_t) millis() - when) > 1000) { + return key; + } + } + + key = serial->read(); + + switch(key) { + case '[': + key = serial->read(); + switch(key) { + case 'A': + return BT_KEY_UP; + case 'B': + return BT_KEY_DOWN; + case 'C': + return BT_KEY_RIGHT; + case 'D': + return BT_KEY_LEFT; + default: + return key; + } + break; + case 'O': + key = serial->read(); + switch(key) { + case 'P': + return BT_KEY_F(1); + case 'Q': + return BT_KEY_F(2); + case 'R': + return BT_KEY_F(3); + case 'S': + return BT_KEY_F(4); + default: + return key; + } + break; + default: + return key; + } + } + + return key; +} + +void BasicTerm::set_attribute(uint8_t attr) { + if(attr & BT_REVERSE) { + serial->print(F("\x1b[7m")); + } + if(attr & BT_UNDERLINE) { + serial->print(F("\x1b[4m")); + } + if(attr & BT_BOLD) { + serial->print(F("\x1b[1m")); + } + if(attr & BT_BLINK) { + serial->print(F("\x1b[5m")); + } + if(attr == BT_NORMAL) { + serial->print(F("\x1b[0m")); + } +} + +void BasicTerm::set_color(uint8_t fg, uint8_t bg) { + serial->print(F("\x1b[")); + serial->print(30 + fg); + serial->print(";"); + serial->print(40 + bg); + serial->print("m"); +} diff --git a/libraries/BasicTerm/BasicTerm.h b/libraries/BasicTerm/BasicTerm.h new file mode 100755 index 0000000..d52695b --- /dev/null +++ b/libraries/BasicTerm/BasicTerm.h @@ -0,0 +1,60 @@ +/* + * BasicTerm.h + * Provides basic ANSI/VT220 terminal control over a serial interface + * Copyright 2011 Trannie Carter + * Licensed for use under the terms of the GNU Lesser General Public License v3 + */ + +#ifndef BASICTERM_H +#define BASICTERM_H + +#include + +class BasicTerm : public Stream { + +#define BT_NORMAL 0 +#define BT_BOLD 1 +#define BT_UNDERLINE 2 +#define BT_BLINK 4 +#define BT_REVERSE 8 + +#define BT_BLACK 0 +#define BT_RED 1 +#define BT_GREEN 2 +#define BT_YELLOW 3 +#define BT_BLUE 4 +#define BT_MAGENTA 5 +#define BT_CYAN 6 +#define BT_WHITE 7 + +#define BT_KEY_UNKNOWN 0401 +#define BT_KEY_DOWN 0402 +#define BT_KEY_UP 0403 +#define BT_KEY_LEFT 0404 +#define BT_KEY_RIGHT 0405 +#define BT_KEY_F0 0410 +#define BT_KEY_F(n) (BT_KEY_F0 + (n)) + + private: + Stream *serial; + + public: + BasicTerm(Stream *); + void init(void); + void cls(void); + void position(uint8_t, uint8_t); + void show_cursor(boolean); + void set_attribute(uint8_t); + void set_color(uint8_t, uint8_t); + int16_t get_key(void); + + virtual int available(void); + virtual int peek(void); + virtual int read(void); + virtual void flush(void); + virtual size_t write(uint8_t); + + using Print::write; +}; + +#endif diff --git a/libraries/E24C1024/E24C1024.cpp b/libraries/E24C1024/E24C1024.cpp new file mode 100755 index 0000000..516b1d7 --- /dev/null +++ b/libraries/E24C1024/E24C1024.cpp @@ -0,0 +1,84 @@ +/* + E24C1024.cpp + AT24C1024 Library for Arduino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 US + + This library is based on several projects: + + The Arduino EEPROM library found here: + http://arduino.cc/en/Reference/EEPROM + + The 24C256 library found here: + http://www.arduino.cc/playground/Code/I2CEEPROM + + The 24C512 library found here: + http://www.arduino.cc/playground/Code/I2CEEPROM24LC512 + + Our project page is here: + http://www.arduino.cc/playground/Code/I2CEEPROM24C1024 + + From the datasheet: + + The AT24C1024B provides 1,048,576 bits of serial electrically + erasable and programmable read only memory (EEPROM) organized + as 131,072 words of 8 bits each. The device’s cascadable + feature allows up to four devices to share a common two-wire + bus. + + http://www.atmel.com/dyn/resources/prod_documents/doc5194.pdf + +*/ + +// Note: This is written for Arduino versions before 1.0. If you are using Arduino 1.0 and above +// then you need to change Wire.Send to Wire.write and Wire.receive to Wire.read + +#if defined(ARDUINO) && ARDUINO >= 100 +#include "Arduino.h" +#else +#include "WProgram.h" +#endif + +#include +#include "E24C1024.h" + +E24C1024::E24C1024(void) +{ + Wire.begin(); +} + +void E24C1024::write(unsigned long dataAddress, uint8_t data) +{ + Wire.beginTransmission((uint8_t)((0x500000 | dataAddress) >> 16)); // B1010xxx + Wire.write((uint8_t)((dataAddress & WORD_MASK) >> 8)); // MSB + Wire.write((uint8_t)(dataAddress & 0xFF)); // LSB + Wire.write(data); + Wire.endTransmission(); + delay(5); +} + +uint8_t E24C1024::read(unsigned long dataAddress) +{ + uint8_t data = 0x00; + Wire.beginTransmission((uint8_t)((0x500000 | dataAddress) >> 16)); // B1010xxx + Wire.write((uint8_t)((dataAddress & WORD_MASK) >> 8)); // MSB + Wire.write((uint8_t)(dataAddress & 0xFF)); // LSB + Wire.endTransmission(); + Wire.requestFrom(0x50,1); + if (Wire.available()) data = Wire.read(); + return data; +} + +E24C1024 EEPROM1024; diff --git a/libraries/E24C1024/E24C1024.h b/libraries/E24C1024/E24C1024.h new file mode 100755 index 0000000..e81d900 --- /dev/null +++ b/libraries/E24C1024/E24C1024.h @@ -0,0 +1,68 @@ +#ifndef E24C1024_h +#define E24C1024_h +/* + E24C1024.h + AT24C1024 Library for Arduino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 US + + This library is based on several projects: + + The Arduino EEPROM library found here: + http://arduino.cc/en/Reference/EEPROM + + The 24C256 library found here: + http://www.arduino.cc/playground/Code/I2CEEPROM + + The 24C512 library found here: + http://www.arduino.cc/playground/Code/I2CEEPROM24LC512 + + Our project page is here: + http://www.arduino.cc/playground/Code/I2CEEPROM24C1024 + + From the datasheet: + + The AT24C1024B provides 1,048,576 bits of serial electrically + erasable and programmable read only memory (EEPROM) organized + as 131,072 words of 8 bits each. The device’s cascadable + feature allows up to four devices to share a common two-wire + bus. + + http://www.atmel.com/dyn/resources/prod_documents/doc5194.pdf + +*/ + + +#if defined(ARDUINO) && ARDUINO >= 100 +#include "Arduino.h" +#else +#include "WProgram.h" +#endif + +#include +#define FULL_MASK 0x7FFFF +#define DEVICE_MASK 0x7F0000 +#define WORD_MASK 0xFFFF +class E24C1024 +{ + public: + E24C1024(); + static void write(unsigned long, uint8_t); + static uint8_t read(unsigned long); +}; + +extern E24C1024 EEPROM1024; + +#endif diff --git a/libraries/Goertzel/goertzel.cpp b/libraries/Goertzel/goertzel.cpp new file mode 100755 index 0000000..9209d42 --- /dev/null +++ b/libraries/Goertzel/goertzel.cpp @@ -0,0 +1,136 @@ +#include "goertzel.h" + +/* + + + Goertzel formula audio detector + + This code comes from http://www.skovholm.com/cwdecoder , http://www.skovholm.com/decoder11.ino + + Hjalmar skovholm Hansen, Oz1jhm + + + + +*/ + +#if defined(ARDUINO) && ARDUINO >= 100 +#include "Arduino.h" +#else +#include "WProgram.h" +#endif + + + +float coeff = 0; +float Q1 = 0; +float Q2 = 0; +float sine = 0; +float cosine = 0; +//float sampling_freq = GOERTZ_SAMPLING_FREQ; +//float target_freq = GOERTZ_TARGET_FREQ; +//float n = GOERTZ_SAMPLES_FLOAT; +int testData[GOERTZ_SAMPLES]; +int audioInPin = 0; +int realstate = LOW; +int realstatebefore = LOW; +int filteredstate = LOW; +unsigned long laststarttime = 0; +int magnitudelimit = 100; +float magnitude = 0; + +////////////////////////////// +// Noise Blanker time which // +// shall be computed so // +// this is initial // +////////////////////////////// + +int nbtime = GOERTZ_NOISE_BLANKER_INITIAL_MS; + + +Goertzdetector::Goertzdetector(){} + + +void Goertzdetector::init(int _audioInPin){ + // The basic goertzel calculation + + int k = 0; + float omega = 0; + //k = (int) (0.5 + ((n * target_freq) / sampling_freq)); + k = (int) (0.5 + (((float)GOERTZ_SAMPLES * (float)GOERTZ_TARGET_FREQ) / (float)GOERTZ_SAMPLING_FREQ)); + //omega = (2.0 * PI * k) / n; + omega = (2.0 * PI * k) / (float) GOERTZ_SAMPLES; + sine = sin(omega); + cosine = cos(omega); + coeff = 2.0 * cosine; + + audioInPin = _audioInPin; + +} + +int Goertzdetector::detecttone(){ + + + // The basic where we get the tone + + + for (char index = 0; index < GOERTZ_SAMPLES; index++){ + testData[index] = analogRead(audioInPin); + } + + for (char index = 0; index < GOERTZ_SAMPLES; index++){ + float Q0; + Q0 = coeff * Q1 - Q2 + (float) testData[index]; + Q2 = Q1; + Q1 = Q0; + } + + float magnitudeSquared = (Q1*Q1)+(Q2*Q2)-Q1*Q2*coeff; // we do only need the real part // + + magnitude = sqrt(magnitudeSquared); + + Q2 = 0; + Q1 = 0; + + + /////////////////////////////////////////////////////////// + // here we will try to set the magnitude limit automatic // + /////////////////////////////////////////////////////////// + + + if (magnitude > GOERTZ_MAGNITUDE_LIMIT_LOW){ + magnitudelimit = (magnitudelimit + ((magnitude - magnitudelimit) / GOERTZ_MOVING_AVERAGE_FILTER)); /// moving average filter + } else { + magnitudelimit = GOERTZ_MAGNITUDE_LIMIT_LOW; + } + + //////////////////////////////////// + // now we check for the magnitude // + //////////////////////////////////// + + if (magnitude > ((float) magnitudelimit * (float) GOERTZ_MAGNITUDE_THRESHOLD)){ + realstate = HIGH; + } else { + realstate = LOW; + } + + ///////////////////////////////////////////////////// + // here we clean up the state with a noise blanker // + ///////////////////////////////////////////////////// + + if (realstate != realstatebefore){ + laststarttime = millis(); + } + + if ((millis()-laststarttime) > nbtime){ + if (realstate != filteredstate){ + filteredstate = realstate; + } + } + + + realstatebefore = realstate; + + return(filteredstate); + + } diff --git a/libraries/Goertzel/goertzel.h b/libraries/Goertzel/goertzel.h new file mode 100755 index 0000000..c6075ad --- /dev/null +++ b/libraries/Goertzel/goertzel.h @@ -0,0 +1,81 @@ +#ifndef GOERTZEL_H +#define GOERTZEL_H + + +/* + + + Goertzel formula audio detector + + This code comes from http://www.skovholm.com/cwdecoder , http://www.skovholm.com/decoder11.ino + + Hjalmar skovholm Hansen, OZ1JHM + + + Notes from the original code author, OZ1JHM (with edits from Goody K3NG) + + GOERTZ_SAMPLING_FREQ will be 8928 on a 16 mhz without any prescaler etc. + because we need the tone in the center of the bins + you can set GOERTZ_TARGET_FREQ (the tone) to 496, 558, 744 or 992 + then GOERTZ_SAMPLES_INT the number of samples which give the bandwidth + which can be (8928 / GOERTZ_TARGET_FREQ) * 1 or 2 or 3 or 4 etc + init is 8928/558 = 16 * 4 = 64 samples + + try to take GOERTZ_SAMPLES = 96 or 128 ;o) + + 48 will give you a bandwidth around 186 hz + 64 will give you a bandwidth around 140 hz + 96 will give you a bandwidth around 94 hz + 128 will give you a bandwidth around 70 hz + + BUT remember that a high GOERTZ_SAMPLES will take a lot of time so you have to find a compromise + +*/ + +#if defined(_VARIANT_ARDUINO_DUE_X_) + // Arduino Due (84 Mhz clock) + #define GOERTZ_SAMPLING_FREQ 46872.0 + #define GOERTZ_SAMPLES 252 //168 //84 +#else + // Arduino Uno, Mega (16 Mhz clock) + #define GOERTZ_SAMPLING_FREQ 8928.0 + #define GOERTZ_SAMPLES 48 //48 //64 +#endif + +#define GOERTZ_NOISE_BLANKER_INITIAL_MS 6 +#define GOERTZ_TARGET_FREQ 558.0 + + + +#define GOERTZ_MAGNITUDE_LIMIT_LOW 100 +#define GOERTZ_MAGNITUDE_THRESHOLD 0.6 //0.6 +#define GOERTZ_MOVING_AVERAGE_FILTER 6 + + +class Goertzdetector { + + public: + Goertzdetector(); + void init(int _audioInPin); + int detecttone(); + int testData[GOERTZ_SAMPLES]; + float magnitude; + int magnitudelimit; + int magnitudelimit_low; + + private: + + float coeff; + float Q1; + float Q2; + float sine; + float cosine; + int audioInPin; + int realstate; + int realstatebefore; + int filteredstate; + unsigned long laststarttime; + +}; + +#endif //GOERTZEL_H diff --git a/libraries/K3NG_PS2Keyboard/K3NG_PS2Keyboard.cpp b/libraries/K3NG_PS2Keyboard/K3NG_PS2Keyboard.cpp new file mode 100755 index 0000000..281a8f1 --- /dev/null +++ b/libraries/K3NG_PS2Keyboard/K3NG_PS2Keyboard.cpp @@ -0,0 +1,538 @@ +/* + K3NG_PS2Keyboard.cpp - K3NG_PS2Keyboard library + Copyright (c) 2007 Free Software Foundation. All right reserved. + Written by Christian Weichel + + + - Modified 2011 Anthony Good anthony dot good at gmail dot com + - Amateur Radio Operator K3NG + - Modified for additional key codes to be returned and CTRL key combinations + + + ** Mostly rewritten Paul Stoffregen 2010, 2011 + ** Modified for use beginning with Arduino 13 by L. Abraham Smith, * + ** Modified for easy interrup pin assignement on method begin(datapin,irq_pin). Cuningan ** + + for more information you can read the original wiki in arduino.cc + at http://www.arduino.cc/playground/Main/PS2Keyboard + or http://www.pjrc.com/teensy/td_libs_PS2Keyboard.html + + Version 2.1 (May 2011) + - timeout to recover from misaligned input + - compatibility with Arduino "new-extension" branch + - TODO: send function, proposed by Scott Penrose, scooterda at me dot com + + Version 2.0 (June 2010) + - Buffering added, many scan codes can be captured without data loss + if your sketch is busy doing other work + - Shift keys supported, completely rewritten scan code to ascii + - Slow linear search replaced with fast indexed table lookups + - Support for Teensy, Arduino Mega, and Sanguino added + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +// K3NG Version 2017.05.12.01 + +#if !defined(ARDUINO_SAM_DUE) + #include + #include + #include +#endif +#if defined(ARDUINO) && ARDUINO >= 100 + #include "Arduino.h" // for attachInterrupt, FALLING +#else + #include "WProgram.h" +#endif +#include "K3NG_PS2Keyboard.h" + +#define BUFFER_SIZE 45 +static volatile uint8_t buffer[BUFFER_SIZE]; +static volatile uint8_t head, tail; +static uint8_t ps2Keyboard_DataPin; +static char ps2Keyboard_CharBuffer=0; + +// The ISR for the external interrupt +void k3ng_ps2interrupt(void) +{ + static uint8_t bitcount=0; + static uint8_t incoming=0; + static uint32_t prev_ms=0; + uint32_t now_ms; + uint8_t n, val; + + val = digitalRead(ps2Keyboard_DataPin); + now_ms = millis(); + if (now_ms - prev_ms > 250) { + bitcount = 0; + incoming = 0; + } + prev_ms = now_ms; + n = bitcount - 1; + if (n <= 7) { + incoming |= (val << n); + } + bitcount++; + if (bitcount == 11) { + uint8_t i = head + 1; + if (i >= BUFFER_SIZE) i = 0; + if (i != tail) { + buffer[i] = incoming; + head = i; + } + bitcount = 0; + incoming = 0; + } +} + +static inline uint8_t get_scan_code(void) +{ + uint8_t c, i; + + i = tail; + if (i == head) return 0; + i++; + if (i >= BUFFER_SIZE) i = 0; + c = buffer[i]; + tail = i; + return c; +} + +// http://www.quadibloc.com/comp/scan.htm +// http://www.computer-engineering.org/ps2keyboard/scancodes2.html + +#define BREAK 0x01 +#define MODIFIER 0x02 +#define SHIFT_L 0x04 +#define SHIFT_R 0x08 +#define CTRL 0x10 +#define ALT 0x20 + + +// These arrays provide a simple key map, to turn scan codes into ASCII +// output. If a non-US keyboard is used, these may need to be modified +// for the desired output. +// + +#ifdef OPTION_PS2_KEYBOARD_US +const PROGMEM unsigned char scan2ascii_noshift[] = { + 0, PS2_F9, 0, PS2_F5, PS2_F3, PS2_F1, PS2_F2, PS2_F12, + 0, PS2_F10, PS2_F8, PS2_F6, PS2_F4, PS2_TAB, '`', 0, + 0, PS2_LEFT_ALT /*Lalt*/, 0 /*Lshift*/, 0, 0 /*PS2_LEFT_CTRL*/ /*Lctrl*/, 'q', '1', 0, + 0, 0, 'z', 's', 'a', 'w', '2', 0, + 0, 'c', 'x', 'd', 'e', '4', '3', 0, + 0, ' ', 'v', 'f', 't', 'r', '5', 0, + 0, 'n', 'b', 'h', 'g', 'y', '6', 0, + 0, 0, 'm', 'j', 'u', '7', '8', 0, + 0, ',', 'k', 'i', 'o', '0', '9', 0, + 0, '.', '/', 'l', ';', 'p', '-', 0, + 0, 0, '\'', 0, '[', '=', 0, 0, + 0 /*CapsLock*/, 0 /*Rshift*/, PS2_ENTER /*Enter*/, ']', 0, '\\', 0, 0, + 0, 0, 0, 0, 0, 0, PS2_BACKSPACE, 0, + 0, '1', 0, '4', '7', 0, 0, 0, + '0', '.', '2', '5', '6', '8', PS2_ESC, 0 /*NumLock*/, + PS2_F11, '+', '3', '-', '*', '9', PS2_SCROLL, 0, + 0, 0, 0, PS2_F7 }; +const PROGMEM unsigned char scan2ascii_shift[] = { + 0, PS2_F9_SHIFT, 0, PS2_F5_SHIFT, PS2_F3_SHIFT, PS2_F1_SHIFT, PS2_F2_SHIFT, PS2_F12_SHIFT, + 0, PS2_F10_SHIFT, PS2_F8_SHIFT, PS2_F6_SHIFT, PS2_F4_SHIFT, PS2_TAB_SHIFT, '~', 0, + 0, 0 /*Lalt*/, 0 /*Lshift*/, 0, 0 /*Lctrl*/, 'Q', '!', 0, + 0, 0, 'Z', 'S', 'A', 'W', '@', 0, + 0, 'C', 'X', 'D', 'E', '$', '#', 0, + 0, ' ', 'V', 'F', 'T', 'R', '%', 0, + 0, 'N', 'B', 'H', 'G', 'Y', '^', 0, + 0, 0, 'M', 'J', 'U', '&', '*', 0, + 0, '<', 'K', 'I', 'O', ')', '(', 0, + 0, '>', '?', 'L', ':', 'P', '_', 0, + 0, 0, '"', 0, '{', '+', 0, 0, + 0 /*CapsLock*/, 0 /*Rshift*/, PS2_ENTER_SHIFT /*Enter*/, '}', 0, '|', 0, 0, + 0, 0, 0, 0, 0, 0, PS2_BACKSPACE_SHIFT, 0, + 0, '1', 0, '4', '7', 0, 0, 0, + '0', '.', '2', '5', '6', '8', PS2_ESC, 0 /*NumLock*/, + PS2_F11_SHIFT, '+', '3', '-', '*', '9', PS2_SCROLL_SHIFT, 0, + 0, 0, 0, PS2_F7_SHIFT }; +const PROGMEM unsigned char scan2ascii_ctrl[] = { + 0, PS2_F9_CTRL, 0, PS2_F5_CTRL, PS2_F3_CTRL, PS2_F1_CTRL, PS2_F2_CTRL, PS2_F12_CTRL, + 0, PS2_F10_CTRL, PS2_F8_CTRL, PS2_F6_CTRL, PS2_F4_CTRL, PS2_TAB_SHIFT, '~', 0, + 0, 0 /*Lalt*/, 0 /*Lshift*/, 0, 0 /*Lctrl*/, 'Q', '!', 0, + 0, 0, PS2_Z_CTRL, 'S', PS2_A_CTRL, PS2_W_CTRL, '@', 0, + 0, PS2_C_CTRL /*'C'*/, 'X', PS2_D_CTRL, PS2_E_CTRL, '$', '#', 0, + 0, ' ', 'V', 'F', PS2_T_CTRL, 'R', '%', 0, + 0, PS2_N_CTRL, PS2_B_CTRL, PS2_H_CTRL, PS2_G_CTRL, 'Y', '^', 0, + 0, 0, PS2_M_CTRL, 'J', PS2_U_CTRL, '&', '*', 0, + 0, '<', 'K', PS2_I_CTRL, PS2_O_CTRL, ')', '(', 0, + 0, '>', '?', 'L', ':', 'P', '_', 0, + 0, 0, '"', 0, '{', '+', 0, 0, + 0 /*CapsLock*/, 0 /*Rshift*/, PS2_ENTER_SHIFT /*Enter*/, '}', 0, '|', 0, 0, + 0, 0, 0, 0, 0, 0, PS2_BACKSPACE_SHIFT, 0, + 0, '1', 0, '4', '7', 0, 0, 0, + '0', '.', '2', '5', '6', '8', PS2_ESC, 0 /*NumLock*/, + PS2_F11_SHIFT, '+', '3', '-', '*', '9', PS2_SCROLL_SHIFT, 0, + 0, 0, 0, PS2_F7_SHIFT }; +const PROGMEM unsigned char scan2ascii_alt[] = { + 0, PS2_F9_ALT, 0, PS2_F5_ALT, PS2_F3_ALT, PS2_F1_ALT, PS2_F2_ALT, PS2_F12_ALT, + 0, PS2_F10_ALT, PS2_F8_ALT, PS2_F6_ALT, PS2_F4_ALT, PS2_TAB_SHIFT, '~', 0, + 0, 0 /*Lalt*/, 0 /*Lshift*/, 0, 0 /*Lctrl*/, 'Q', '!', 0, + 0, 0, PS2_Z_CTRL, 'S', PS2_A_CTRL, PS2_W_CTRL, '@', 0, + 0, 'C', 'X', PS2_D_CTRL, PS2_E_CTRL, '$', '#', 0, + 0, ' ', 'V', 'F', PS2_T_CTRL, 'R', '%', 0, + 0, PS2_N_CTRL, PS2_B_CTRL, PS2_H_CTRL, PS2_G_CTRL, 'Y', '^', 0, + 0, 0, PS2_M_CTRL, 'J', PS2_U_CTRL, '&', '*', 0, + 0, '<', 'K', PS2_I_CTRL, PS2_O_CTRL, ')', '(', 0, + 0, '>', '?', 'L', ':', 'P', '_', 0, + 0, 0, '"', 0, '{', '+', 0, 0, + 0 /*CapsLock*/, 0 /*Rshift*/, PS2_ENTER /*Enter*/, '}', 0, '|', 0, 0, + 0, 0, 0, 0, 0, 0, PS2_BACKSPACE_SHIFT, 0, + 0, '1', 0, '4', '7', 0, 0, 0, + '0', '.', '2', '5', '6', '8', PS2_ESC, 0 /*NumLock*/, + PS2_F11_ALT, '+', '3', '-', '*', '9', PS2_SCROLL_SHIFT, 0, + 0, 0, 0, PS2_F7_ALT }; +#endif //OPTION_PS2_KEYBOARD_US + + //-------------------------------------------------- + +#ifdef OPTION_PS2_KEYBOARD_GERMAN + // without shift +const PROGMEM unsigned char scan2ascii_noshift[] = + {0, PS2_F9, 0, PS2_F5, PS2_F3, PS2_F1, PS2_F2, PS2_F12, + 0, PS2_F10, PS2_F8, PS2_F6, PS2_F4, PS2_TAB, '^', 0, + 0, 0 /*Lalt*/, 0 /*Lshift*/, 0, 0 /*Lctrl*/, 'q', '1', 0, + 0, 0, 'y', 's', 'a', 'w', '2', 0, + 0, 'c', 'x', 'd', 'e', '4', '3', 0, + 0, ' ', 'v', 'f', 't', 'r', '5', 0, + 0, 'n', 'b', 'h', 'g', 'z', '6', 0, + 0, 0, 'm', 'j', 'u', '7', '8', 0, + 0, ',', 'k', 'i', 'o', '0', '9', 0, + 0, '.', '-', 'l', PS2_o_DIAERESIS, 'p', PS2_SHARP_S, 0, + 0, 0, PS2_a_DIAERESIS, 0, PS2_u_DIAERESIS, '\'', 0, 0, + 0 /*CapsLock*/, 0 /*Rshift*/, PS2_ENTER /*Enter*/, '+', 0, '#', 0, 0, + 0, '<', 0, 0, 0, 0, PS2_BACKSPACE, 0, + 0, '1', 0, '4', '7', 0, 0, 0, + '0', '.', '2', '5', '6', '8', PS2_ESC, 0 /*NumLock*/, + PS2_F11, '+', '3', '-', '*', '9', PS2_SCROLL, 0, + 0, 0, 0, PS2_F7 }; + // with shift +const PROGMEM unsigned char scan2ascii_shift[] = + {0, PS2_F9_SHIFT, 0, PS2_F5_SHIFT, PS2_F3_SHIFT, PS2_F1_SHIFT, PS2_F2_SHIFT, PS2_F12_SHIFT, + 0, PS2_F10_SHIFT, PS2_F8_SHIFT, PS2_F6_SHIFT, PS2_F4_SHIFT, PS2_TAB_SHIFT, PS2_DEGREE_SIGN, 0, + 0, 0 /*Lalt*/, 0 /*Lshift*/, 0, 0 /*Lctrl*/, 'Q', '!', 0, + 0, 0, 'Y', 'S', 'A', 'W', '"', 0, + 0, 'C', 'X', 'D', 'E', '$', PS2_SECTION_SIGN, 0, + 0, ' ', 'V', 'F', 'T', 'R', '%', 0, + 0, 'N', 'B', 'H', 'G', 'Z', '&', 0, + 0, 0, 'M', 'J', 'U', '/', '(', 0, + 0, ';', 'K', 'I', 'O', '=', ')', 0, + 0, ':', '_', 'L', PS2_O_DIAERESIS, 'P', '?', 0, + 0, 0, PS2_A_DIAERESIS, 0, PS2_U_DIAERESIS, '`', 0, 0, + 0 /*CapsLock*/, 0 /*Rshift*/, PS2_ENTER /*Enter*/, '*', 0, '\'', 0, 0, + 0, '>', 0, 0, 0, 0, PS2_BACKSPACE, 0, + 0, '1', 0, '4', '7', 0, 0, 0, + '0', '.', '2', '5', '6', '8', PS2_ESC, 0 /*NumLock*/, + PS2_F11_SHIFT, '+', '3', '-', '*', '9', PS2_SCROLL, 0, + 0, 0, 0, PS2_F7_SHIFT }; +const PROGMEM unsigned char scan2ascii_ctrl[] = // this needs to be updated!!! (it is copy of US) + {0, PS2_F9_CTRL, 0, PS2_F5_CTRL, PS2_F3_CTRL, PS2_F1_CTRL, PS2_F2_CTRL, PS2_F12_CTRL, + 0, PS2_F10_CTRL, PS2_F8_CTRL, PS2_F6_CTRL, PS2_F4_CTRL, PS2_TAB_SHIFT, '~', 0, + 0, 0 /*Lalt*/, 0 /*Lshift*/, 0, 0 /*Lctrl*/, 'Q', '!', 0, + 0, 0, PS2_Z_CTRL, 'S', PS2_A_CTRL, PS2_W_CTRL, '@', 0, + 0, PS2_C_CTRL /*'C'*/, 'X', PS2_D_CTRL, PS2_E_CTRL, '$', '#', 0, + 0, ' ', 'V', 'F', PS2_T_CTRL, 'R', '%', 0, + 0, PS2_N_CTRL, PS2_B_CTRL, PS2_H_CTRL, PS2_G_CTRL, 'Y', '^', 0, + 0, 0, PS2_M_CTRL, 'J', PS2_U_CTRL, '&', '*', 0, + 0, '<', 'K', PS2_I_CTRL, PS2_O_CTRL, ')', '(', 0, + 0, '>', '?', 'L', ':', 'P', '_', 0, + 0, 0, '"', 0, '{', '+', 0, 0, + 0 /*CapsLock*/, 0 /*Rshift*/, PS2_ENTER_SHIFT /*Enter*/, '}', 0, '|', 0, 0, + 0, 0, 0, 0, 0, 0, PS2_BACKSPACE_SHIFT, 0, + 0, '1', 0, '4', '7', 0, 0, 0, + '0', '.', '2', '5', '6', '8', PS2_ESC, 0 /*NumLock*/, + PS2_F11_CTRL, '+', '3', '-', '*', '9', PS2_SCROLL_SHIFT, 0, + 0, 0, 0, PS2_F7_CTRL}; + // with altgr +const PROGMEM unsigned char scan2ascii_alt[] = + {0, PS2_F9_ALT, 0, PS2_F5_ALT, PS2_F3_ALT, PS2_F1_ALT, PS2_F2_ALT, PS2_F12_ALT, + 0, PS2_F10_ALT, PS2_F8_ALT, PS2_F6_ALT, PS2_F4_ALT, PS2_TAB, 0, 0, + 0, 0 /*Lalt*/, 0 /*Lshift*/, 0, 0 /*Lctrl*/, '@', 0, 0, + 0, 0, 0, 0, 0, 0, PS2_SUPERSCRIPT_TWO, 0, + 0, 0, 0, 0, PS2_CURRENCY_SIGN, 0, PS2_SUPERSCRIPT_THREE, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, PS2_MICRO_SIGN, 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 /*CapsLock*/, 0 /*Rshift*/, PS2_ENTER /*Enter*/, '~', 0, '#', 0, 0, + 0, '|', 0, 0, 0, 0, PS2_BACKSPACE, 0, + 0, '1', 0, '4', '7', 0, 0, 0, + '0', '.', '2', '5', '6', '8', PS2_ESC, 0 /*NumLock*/, + PS2_F11_ALT, '+', '3', '-', '*', '9', PS2_SCROLL, 0, + 0, 0, 0, PS2_F7_ALT}; +#endif //OPTION_PS2_KEYBOARD_GERMAN + + //-------------------------------------------------- + +#ifdef OPTION_PS2_KEYBOARD_FRENCH +const PROGMEM unsigned char scan2ascii_noshift[] = + {0, PS2_F9, 0, PS2_F5, PS2_F3, PS2_F1, PS2_F2, PS2_F12, + 0, PS2_F10, PS2_F8, PS2_F6, PS2_F4, PS2_TAB, PS2_SUPERSCRIPT_TWO, 0, + 0, 0 /*Lalt*/, 0 /*Lshift*/, 0, 0 /*Lctrl*/, 'a', '&', 0, + 0, 0, 'w', 's', 'q', 'z', PS2_e_ACUTE, 0, + 0, 'c', 'x', 'd', 'e', '\'', '"', 0, + 0, ' ', 'v', 'f', 't', 'r', '(', 0, + 0, 'n', 'b', 'h', 'g', 'y', '-', 0, + 0, 0, ',', 'j', 'u', PS2_e_GRAVE, '_', 0, + 0, ';', 'k', 'i', 'o', PS2_a_GRAVE, PS2_c_CEDILLA, 0, + 0, ':', '!', 'l', 'm', 'p', ')', 0, + 0, 0, PS2_u_GRAVE, 0, '^', '=', 0, 0, + 0 /*CapsLock*/, 0 /*Rshift*/, PS2_ENTER /*Enter*/, '$', 0, '*', 0, 0, + 0, '<', 0, 0, 0, 0, PS2_BACKSPACE, 0, + 0, '1', 0, '4', '7', 0, 0, 0, + '0', '.', '2', '5', '6', '8', PS2_ESC, 0 /*NumLock*/, + PS2_F11, '+', '3', '-', '*', '9', PS2_SCROLL, 0, + 0, 0, 0, PS2_F7 }; + + // with shift +const PROGMEM unsigned char scan2ascii_shift[] = + {0, PS2_F9_SHIFT, 0, PS2_F5_SHIFT, PS2_F3_SHIFT, PS2_F1_SHIFT, PS2_F2_SHIFT, PS2_F12_SHIFT, + 0, PS2_F10_SHIFT, PS2_F8_SHIFT, PS2_F6_SHIFT, PS2_F4_SHIFT, PS2_TAB_SHIFT, 0, 0, + 0, 0 /*Lalt*/, 0 /*Lshift*/, 0, 0 /*Lctrl*/, 'A', '1', 0, + 0, 0, 'W', 'S', 'Q', 'Z', '2', 0, + 0, 'C', 'X', 'D', 'E', '4', '3', 0, + 0, ' ', 'V', 'F', 'T', 'R', '5', 0, + 0, 'N', 'B', 'H', 'G', 'Y', '6', 0, + 0, 0, '?', 'J', 'U', '7', '8', 0, + 0, '.', 'K', 'I', 'O', '0', '9', 0, + 0, '/', PS2_SECTION_SIGN, 'L', 'M', 'P', PS2_DEGREE_SIGN, 0, + 0, 0, '%', 0, PS2_DIAERESIS, '+', 0, 0, + 0 /*CapsLock*/, 0 /*Rshift*/, PS2_ENTER /*Enter*/, PS2_POUND_SIGN, 0, PS2_MICRO_SIGN, 0, 0, + 0, '>', 0, 0, 0, 0, PS2_BACKSPACE, 0, + 0, '1', 0, '4', '7', 0, 0, 0, + '0', '.', '2', '5', '6', '8', PS2_ESC, 0 /*NumLock*/, + PS2_F11_SHIFT, '+', '3', '-', '*', '9', PS2_SCROLL, 0, + 0, 0, 0, PS2_F7_SHIFT}; +const PROGMEM unsigned char scan2ascii_ctrl[] = // this needs to be updated!!! (it is copy of US) + {0, PS2_F9_CTRL, 0, PS2_F5_CTRL, PS2_F3_CTRL, PS2_F1_CTRL, PS2_F2_CTRL, PS2_F12_CTRL, + 0, PS2_F10_CTRL, PS2_F8_CTRL, PS2_F6_CTRL, PS2_F4_CTRL, PS2_TAB, '~', 0, + 0, 0 /*Lalt*/, 0 /*Lshift*/, 0, 0 /*Lctrl*/, 'Q', '!', 0, + 0, 0, PS2_Z_CTRL, 'S', PS2_A_CTRL, PS2_W_CTRL, '@', 0, + 0, PS2_C_CTRL /*'C'*/, 'X', PS2_D_CTRL, PS2_E_CTRL, '$', '#', 0, + 0, ' ', 'V', 'F', PS2_T_CTRL, 'R', '%', 0, + 0, PS2_N_CTRL, PS2_B_CTRL, PS2_H_CTRL, PS2_G_CTRL, 'Y', '^', 0, + 0, 0, PS2_M_CTRL, 'J', PS2_U_CTRL, '&', '*', 0, + 0, '<', 'K', PS2_I_CTRL, PS2_O_CTRL, ')', '(', 0, + 0, '>', '?', 'L', ':', 'P', '_', 0, + 0, 0, '"', 0, '{', '+', 0, 0, + 0 /*CapsLock*/, 0 /*Rshift*/, PS2_ENTER_SHIFT /*Enter*/, '}', 0, '|', 0, 0, + 0, 0, 0, 0, 0, 0, PS2_BACKSPACE_SHIFT, 0, + 0, '1', 0, '4', '7', 0, 0, 0, + '0', '.', '2', '5', '6', '8', PS2_ESC, 0 /*NumLock*/, + PS2_F11_CTRL, '+', '3', '-', '*', '9', PS2_SCROLL_SHIFT, 0, + 0, 0, 0, PS2_F7_CTRL}; +const PROGMEM unsigned char scan2ascii_alt[] = + // with altgr + {0, PS2_F9_ALT, 0, PS2_F5_ALT, PS2_F3_ALT, PS2_F1_ALT, PS2_F2_ALT, PS2_F12_ALT, + 0, PS2_F10_ALT, PS2_F8_ALT, PS2_F6_ALT, PS2_F4_ALT, PS2_TAB, 0, 0, + 0, 0 /*Lalt*/, 0 /*Lshift*/, 0, 0 /*Lctrl*/, '@', 0, 0, + 0, 0, 0, 0, 0, 0, '~', 0, + 0, 0, 0, 0, 0 /*PS2_EURO_SIGN*/, '{', '#', 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, 0, '@', '^', 0, + 0, 0, 0, 0, 0, 0, ']', 0, + 0, 0, 0, 0, 0, 0, '}', 0, + 0 /*CapsLock*/, 0 /*Rshift*/, PS2_ENTER /*Enter*/, '¤', 0, '#', 0, 0, + 0, '|', 0, 0, 0, 0, PS2_BACKSPACE, 0, + 0, '1', 0, '4', '7', 0, 0, 0, + '0', '.', '2', '5', '6', '8', PS2_ESC, 0 /*NumLock*/, + PS2_F11_ALT, '+', '3', '-', '*', '9', PS2_SCROLL, 0, + 0, 0, 0, PS2_F7_ALT}; + + +#endif //OPTION_PS2_KEYBOARD_FRENCH + +//-------------------------------------------------- + +static char get_ascii_code(void) +{ + static uint8_t state=0; + uint8_t s; + char c; + + while (1) { + s = get_scan_code(); + if (!s) return 0; + if (s == 0xF0) { + state |= BREAK; + } else if (s == 0xE0) { + state |= MODIFIER; + } else { + if (state & BREAK) { + if (s == 0x12) { + state &= ~SHIFT_L; + } else if (s == 0x59) { + state &= ~SHIFT_R; + //start K3NG modification + } else if (s == 0x14) { + state &= ~CTRL; + } else if (s == 0x11) { + state &= ~ALT; + } + // end K3NG modification + state &= ~(BREAK | MODIFIER); + continue; + } + if (s == 0x12) { + state |= SHIFT_L; + continue; + } else if (s == 0x59) { + state |= SHIFT_R; + continue; + } else if (s == 0x14) { + state |= CTRL; + continue; + } else if (s == 0x11) { + state |= ALT; + continue; + } + c = 0; + if (state & MODIFIER) { + switch (s) { + case 0x70: c = PS2_INSERT; break; + case 0x6C: c = PS2_HOME; break; + case 0x7D: c = PS2_PAGEUP; break; + case 0x71: c = PS2_DELETE; break; + case 0x69: c = PS2_END; break; + case 0x7A: c = PS2_PAGEDOWN; break; + case 0x75: c = PS2_UPARROW; break; + case 0x6B: c = PS2_LEFTARROW; break; + case 0x72: c = PS2_DOWNARROW; break; + case 0x74: c = PS2_RIGHTARROW; break; + case 0x4A: c = '/'; break; + case 0x5A: c = PS2_ENTER; break; + default: break; + } + } else if (state & (SHIFT_L | SHIFT_R)) { + if (s < sizeof(scan2ascii_shift)) + c = pgm_read_byte(scan2ascii_shift + s); + //start K3NG modification + } else if ((state & CTRL)) { + if (s < sizeof(scan2ascii_ctrl)) + c = pgm_read_byte(scan2ascii_ctrl + s); + } else if ((state & ALT)) { + if (s < sizeof(scan2ascii_alt)) + c = pgm_read_byte(scan2ascii_alt + s); + //end K3NG modification + } else { + if (s < sizeof(scan2ascii_noshift)) + c = pgm_read_byte(scan2ascii_noshift + s); + } + state &= ~(BREAK | MODIFIER); + if (c) return c; + } + } +} + +bool K3NG_PS2Keyboard::available() { + if (ps2Keyboard_CharBuffer) return true; + ps2Keyboard_CharBuffer = get_ascii_code(); + if (ps2Keyboard_CharBuffer) return true; + return false; +} + +int K3NG_PS2Keyboard::read() { + char result; + + result = ps2Keyboard_CharBuffer; + if (result) { + ps2Keyboard_CharBuffer = 0; + } else { + result = get_ascii_code(); + } + if (!result) return -1; + return result; +} + +K3NG_PS2Keyboard::K3NG_PS2Keyboard() { + // nothing to do here, begin() does it all +} + +void K3NG_PS2Keyboard::begin(uint8_t dataPin, uint8_t irq_pin) { + uint8_t irq_num=0; + + ps2Keyboard_DataPin = dataPin; + + // initialize the pins +#ifdef INPUT_PULLUP + pinMode(irq_pin, INPUT_PULLUP); + pinMode(dataPin, INPUT_PULLUP); +#else + pinMode(irq_pin, INPUT); + digitalWrite(irq_pin, HIGH); + pinMode(dataPin, INPUT); + digitalWrite(dataPin, HIGH); +#endif + + switch(irq_pin) { + #ifdef CORE_INT0_PIN + case CORE_INT0_PIN: + irq_num = 0; + break; + #endif + #ifdef CORE_INT1_PIN + case CORE_INT1_PIN: + irq_num = 1; + break; + #endif + #ifdef CORE_INT2_PIN + case CORE_INT2_PIN: + irq_num = 2; + break; + #endif + #ifdef CORE_INT3_PIN + case CORE_INT3_PIN: + irq_num = 3; + break; + #endif + #ifdef CORE_INT4_PIN + case CORE_INT4_PIN: + irq_num = 4; + break; + #endif + #ifdef CORE_INT5_PIN + case CORE_INT5_PIN: + irq_num = 5; + break; + #endif + #ifdef CORE_INT6_PIN + case CORE_INT6_PIN: + irq_num = 6; + break; + #endif + #ifdef CORE_INT7_PIN + case CORE_INT7_PIN: + irq_num = 7; + break; + #endif + default: + irq_num = 0; + break; + } + head = 0; + tail = 0; + attachInterrupt(irq_num, k3ng_ps2interrupt, FALLING); +} + + diff --git a/libraries/K3NG_PS2Keyboard/K3NG_PS2Keyboard.h b/libraries/K3NG_PS2Keyboard/K3NG_PS2Keyboard.h new file mode 100755 index 0000000..429d3bd --- /dev/null +++ b/libraries/K3NG_PS2Keyboard/K3NG_PS2Keyboard.h @@ -0,0 +1,538 @@ +/* + K3NG_PS2Keyboard.h - K3NG_PS2Keyboard library + Copyright (c) 2007 Free Software Foundation. All right reserved. + Originally written by Christian Weichel + + - Forked from version 2.1 2015-01-28 by Anthony Good K3NG + + - Modified by Anthony Good / anthony dot good at gmail dot com + - August 2011 + - Amateur Radio Operator K3NG + - Modified for additional key codes to be returned and CTRL key combinations + - http://radioartisan.wordpress.com + + ** Mostly rewritten Paul Stoffregen , June 2010 + ** Modified for use with Arduino 13 by L. Abraham Smith, * + ** Modified for easy interrup pin assignement on method begin(datapin,irq_pin). Cuningan ** + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +K3NG Updates + + 2015101401 + Fixed issues with CTRL and ALT key combinations with German and French keyboards + + +*/ + + + + + +// K3NG Version 2017.05.12.01 + +#ifndef K3NG_PS2Keyboard_h +#define K3NG_PS2Keyboard_h + +#define OPTION_PS2_KEYBOARD_US +// #define OPTION_PS2_KEYBOARD_GERMAN +// #define OPTION_PS2_KEYBOARD_FRENCH + +#if !defined(ARDUINO_SAM_DUE) + #include + #include + #include +#endif + +// Every call to read() returns a single byte for each +// keystroke. These configure what byte will be returned +// for each "special" key. To ignore a key, use zero. +#define PS2_TAB 9 +#define PS2_ENTER 13 +#define PS2_BACKSPACE 127 +#define PS2_ESC 27 +#define PS2_INSERT 128 +#define PS2_DELETE 127 +#define PS2_HOME 129 +#define PS2_END 156 +#define PS2_PAGEUP 25 +#define PS2_PAGEDOWN 26 +#define PS2_UPARROW 11 +#define PS2_LEFTARROW 8 +#define PS2_DOWNARROW 10 +#define PS2_RIGHTARROW 21 +#define PS2_F1 130 +#define PS2_F2 131 +#define PS2_F3 132 +#define PS2_F4 133 +#define PS2_F5 134 +#define PS2_F6 135 +#define PS2_F7 136 +#define PS2_F8 137 +#define PS2_F9 138 +#define PS2_F10 139 +#define PS2_F11 140 +#define PS2_F12 141 +#define PS2_SCROLL 142 + +#define PS2_F1_SHIFT 143 +#define PS2_F2_SHIFT 144 +#define PS2_F3_SHIFT 145 +#define PS2_F4_SHIFT 146 +#define PS2_F5_SHIFT 147 +#define PS2_F6_SHIFT 148 +#define PS2_F7_SHIFT 149 +#define PS2_F8_SHIFT 150 +#define PS2_F9_SHIFT 151 +#define PS2_F10_SHIFT 152 +#define PS2_F11_SHIFT 153 +#define PS2_F12_SHIFT 154 +#define PS2_TAB_SHIFT 9 +#define PS2_ENTER_SHIFT 13 +#define PS2_BACKSPACE_SHIFT 157 + +#define PS2_LEFT_ALT 158 +//#define PS2_LEFT_CTRL 159 +#define PS2_SCROLL_SHIFT 160 + +#if defined(OPTION_PS2_KEYBOARD_US) +#define PS2_A_CTRL 162 +#define PS2_B_CTRL 163 +#define PS2_C_CTRL 164 +#define PS2_D_CTRL 165 +#define PS2_E_CTRL 166 +#define PS2_F_CTRL 167 +#define PS2_G_CTRL 168 +#define PS2_H_CTRL 169 +#define PS2_I_CTRL 170 +#define PS2_J_CTRL 171 +#define PS2_K_CTRL 172 +#define PS2_L_CTRL 173 +#define PS2_M_CTRL 174 +#define PS2_N_CTRL 175 +#define PS2_O_CTRL 176 +#define PS2_P_CTRL 177 +#define PS2_Q_CTRL 178 +#define PS2_R_CTRL 179 +#define PS2_S_CTRL 180 +#define PS2_T_CTRL 181 +#define PS2_U_CTRL 182 +#define PS2_V_CTRL 183 +#define PS2_W_CTRL 184 +#define PS2_X_CTRL 185 +#define PS2_Y_CTRL 186 +#define PS2_Z_CTRL 187 +#define PS2_F1_CTRL 188 +#define PS2_F2_CTRL 189 +#define PS2_F3_CTRL 190 +#define PS2_F4_CTRL 191 +#define PS2_F5_CTRL 192 +#define PS2_F6_CTRL 193 +#define PS2_F7_CTRL 194 +#define PS2_F8_CTRL 195 +#define PS2_F9_CTRL 196 +#define PS2_F10_CTRL 197 +#define PS2_F11_CTRL 198 +#define PS2_F12_CTRL 199 +#define PS2_F1_ALT 200 +#define PS2_F2_ALT 201 +#define PS2_F3_ALT 202 +#define PS2_F4_ALT 203 +#define PS2_F5_ALT 204 +#define PS2_F6_ALT 205 +#define PS2_F7_ALT 206 +#define PS2_F8_ALT 207 +#define PS2_F9_ALT 208 +#define PS2_F10_ALT 209 +#define PS2_F11_ALT 210 +#define PS2_F12_ALT 211 +#endif //OPTION_PS2_KEYBOARD_US + + +// ----------------- added K3NG + +#if defined(OPTION_PS2_KEYBOARD_GERMAN) + +#define PS2_A_CTRL 162 +#define PS2_B_CTRL 163 +#define PS2_C_CTRL 200 //164 +#define PS2_D_CTRL 165 +#define PS2_E_CTRL 166 +#define PS2_F_CTRL 201 //167 +#define PS2_G_CTRL 168 +#define PS2_H_CTRL 169 +#define PS2_I_CTRL 170 +#define PS2_J_CTRL 171 +#define PS2_K_CTRL 172 +#define PS2_L_CTRL 173 +#define PS2_M_CTRL 174 +#define PS2_N_CTRL 175 +#define PS2_O_CTRL 202 //176 +#define PS2_P_CTRL 177 +#define PS2_Q_CTRL 203 //178 +#define PS2_R_CTRL 204 //179 +#define PS2_S_CTRL 180 +#define PS2_T_CTRL 205 //181 +#define PS2_U_CTRL 182 +#define PS2_V_CTRL 183 +#define PS2_W_CTRL 184 +#define PS2_X_CTRL 185 +#define PS2_Y_CTRL 186 +#define PS2_Z_CTRL 187 +#define PS2_F1_CTRL 188 +#define PS2_F2_CTRL 189 +#define PS2_F3_CTRL 190 +#define PS2_F4_CTRL 191 +#define PS2_F5_CTRL 192 +#define PS2_F6_CTRL 193 +#define PS2_F7_CTRL 194 +#define PS2_F8_CTRL 195 +#define PS2_F9_CTRL 206 //196 +#define PS2_F10_CTRL 197 +#define PS2_F11_CTRL 198 +#define PS2_F12_CTRL 199 +#define PS2_F1_ALT 229 +#define PS2_F2_ALT 230 +#define PS2_F3_ALT 231 +#define PS2_F4_ALT 232 +#define PS2_F5_ALT 233 +#define PS2_F6_ALT 234 +#define PS2_F7_ALT 235 +#define PS2_F8_ALT 236 +#define PS2_F9_ALT 237 +#define PS2_F10_ALT 238 +#define PS2_F11_ALT 239 +#define PS2_F12_ALT 240 + +//#define PS2_INVERTED_EXCLAMATION 161 // ¡ +//#define PS2_CENT_SIGN 162 // ¢ +//#define PS2_POUND_SIGN 163 // £ +#define PS2_CURRENCY_SIGN 164 // ¤ +//#define PS2_YEN_SIGN 165 // Â¥ +//#define PS2_BROKEN_BAR 166 // ¦ +#define PS2_SECTION_SIGN 167 // § +//#define PS2_DIAERESIS 168 // ¨ +//#define PS2_COPYRIGHT_SIGN 169 // © +//#define PS2_FEMININE_ORDINAL 170 // ª +//#define PS2_LEFT_DOUBLE_ANGLE_QUOTE 171 // « +//#define PS2_NOT_SIGN 172 // ¬ +//#define PS2_HYPHEN 173 +//#define PS2_REGISTERED_SIGN 174 // ® +//#define PS2_MACRON 175 // ¯ +#define PS2_DEGREE_SIGN 176 // ° +//#define PS2_PLUS_MINUS_SIGN 177 // ± +#define PS2_SUPERSCRIPT_TWO 178 // ² +#define PS2_SUPERSCRIPT_THREE 179 // ³ +//#define PS2_ACUTE_ACCENT 180 // ´ +#define PS2_MICRO_SIGN 181 // µ +//#define PS2_PILCROW_SIGN 182 // ¶ +//#define PS2_MIDDLE_DOT 183 // · +//#define PS2_CEDILLA 184 // ¸ +//#define PS2_SUPERSCRIPT_ONE 185 // ¹ +//#define PS2_MASCULINE_ORDINAL 186 // º +//#define PS2_RIGHT_DOUBLE_ANGLE_QUOTE 187 // » +//#define PS2_FRACTION_ONE_QUARTER 188 // ¼ +//#define PS2_FRACTION_ONE_HALF 189 // ½ +//#define PS2_FRACTION_THREE_QUARTERS 190 // ¾ +//#define PS2_INVERTED_QUESTION MARK 191 // ¿ +//#define PS2_A_GRAVE 192 // À +//#define PS2_A_ACUTE 193 // à +//#define PS2_A_CIRCUMFLEX 194 //  +//#define PS2_A_TILDE 195 // à +#define PS2_A_DIAERESIS 196 // Ä +//#define PS2_A_RING_ABOVE 197 // Ã… +//#define PS2_AE 198 // Æ +//#define PS2_C_CEDILLA 199 // Ç +//#define PS2_E_GRAVE 200 // È +//#define PS2_E_ACUTE 201 // É +//#define PS2_E_CIRCUMFLEX 202 // Ê +//#define PS2_E_DIAERESIS 203 // Ë +//#define PS2_I_GRAVE 204 // ÃŒ +//#define PS2_I_ACUTE 205 // à +//#define PS2_I_CIRCUMFLEX 206 // ÃŽ +//#define PS2_I_DIAERESIS 207 // à +//#define PS2_ETH 208 // à +//#define PS2_N_TILDE 209 // Ñ +//#define PS2_O_GRAVE 210 // Ã’ +//#define PS2_O_ACUTE 211 // Ó +//#define PS2_O_CIRCUMFLEX 212 // Ô +//#define PS2_O_TILDE 213 // Õ +#define PS2_O_DIAERESIS 214 // Ö +//#define PS2_MULTIPLICATION 215 // × +//#define PS2_O_STROKE 216 // Ø +//#define PS2_U_GRAVE 217 // Ù +//#define PS2_U_ACUTE 218 // Ú +//#define PS2_U_CIRCUMFLEX 219 // Û +#define PS2_U_DIAERESIS 220 // Ãœ +//#define PS2_Y_ACUTE 221 // à +//#define PS2_THORN 222 // Þ +#define PS2_SHARP_S 223 // ß +//#define PS2_a_GRAVE 224 // à +//#define PS2_a_ACUTE 225 // á +//#define PS2_a_CIRCUMFLEX 226 // â +//#define PS2_a_TILDE 227 // ã +#define PS2_a_DIAERESIS 228 // ä +//#define PS2_a_RING_ABOVE 229 // Ã¥ +//#define PS2_ae 230 // æ +//#define PS2_c_CEDILLA 231 // ç +//#define PS2_e_GRAVE 232 // è +//#define PS2_e_ACUTE 233 // é +//#define PS2_e_CIRCUMFLEX 234 // ê +//#define PS2_e_DIAERESIS 235 // ë +//#define PS2_i_GRAVE 236 // ì +//#define PS2_i_ACUTE 237 // í +//#define PS2_i_CIRCUMFLEX 238 // î +//#define PS2_i_DIAERESIS 239 // ï +//#define PS2_eth 240 // ð +//#define PS2_n_TILDE 241 // ñ +//#define PS2_o_GRAVE 242 // ò +//#define PS2_o_ACUTE 243 // ó +//#define PS2_o_CIRCUMFLEX 244 // ô +//#define PS2_o_TILDE 245 // õ +#define PS2_o_DIAERESIS 246 // ö +//#define PS2_DIVISION 247 // ÷ +//#define PS2_o_STROKE 248 // ø +//#define PS2_u_GRAVE 249 // ù +//#define PS2_u_ACUTE 250 // ú +//#define PS2_u_CIRCUMFLEX 251 // û +#define PS2_u_DIAERESIS 252 // ü +//#define PS2_y_ACUTE 253 // ý +//#define PS2_thorn 254 // þ +//#define PS2_y_DIAERESIS 255 // ÿ +#endif //defined(OPTION_PS2_KEYBOARD_GERMAN) + +#if defined(OPTION_PS2_KEYBOARD_FRENCH) +#define PS2_A_CTRL 162 +#define PS2_B_CTRL 200 //163 +#define PS2_C_CTRL 164 +#define PS2_D_CTRL 165 +#define PS2_E_CTRL 166 +#define PS2_F_CTRL 201 //167 +#define PS2_G_CTRL 202 //168 +#define PS2_H_CTRL 169 +#define PS2_I_CTRL 170 +#define PS2_J_CTRL 171 +#define PS2_K_CTRL 172 +#define PS2_L_CTRL 173 +#define PS2_M_CTRL 174 +#define PS2_N_CTRL 175 +#define PS2_O_CTRL 203 //176 +#define PS2_P_CTRL 177 +#define PS2_Q_CTRL 204 //178 +#define PS2_R_CTRL 179 +#define PS2_S_CTRL 180 +#define PS2_T_CTRL 205 //181 +#define PS2_U_CTRL 182 +#define PS2_V_CTRL 183 +#define PS2_W_CTRL 184 +#define PS2_X_CTRL 185 +#define PS2_Y_CTRL 186 +#define PS2_Z_CTRL 187 +#define PS2_F1_CTRL 188 +#define PS2_F2_CTRL 189 +#define PS2_F3_CTRL 190 +#define PS2_F4_CTRL 191 +#define PS2_F5_CTRL 192 +#define PS2_F6_CTRL 193 +#define PS2_F7_CTRL 194 +#define PS2_F8_CTRL 195 +#define PS2_F9_CTRL 196 +#define PS2_F10_CTRL 197 +#define PS2_F11_CTRL 198 +#define PS2_F12_CTRL 199 +#define PS2_F1_ALT 233 +#define PS2_F2_ALT 234 +#define PS2_F3_ALT 235 +#define PS2_F4_ALT 236 +#define PS2_F5_ALT 237 +#define PS2_F6_ALT 238 +#define PS2_F7_ALT 239 +#define PS2_F8_ALT 240 +#define PS2_F9_ALT 241 +#define PS2_F10_ALT 242 +#define PS2_F11_ALT 243 +#define PS2_F12_ALT 244 + +//#define PS2_INVERTED_EXCLAMATION 161 // ¡ +//#define PS2_CENT_SIGN 162 // ¢ +#define PS2_POUND_SIGN 163 // £ +//#define PS2_CURRENCY_SIGN 164 // ¤ +//#define PS2_YEN_SIGN 165 // Â¥ +//#define PS2_BROKEN_BAR 166 // ¦ +#define PS2_SECTION_SIGN 167 // § +#define PS2_DIAERESIS 168 // ¨ +//#define PS2_COPYRIGHT_SIGN 169 // © +//#define PS2_FEMININE_ORDINAL 170 // ª +//#define PS2_LEFT_DOUBLE_ANGLE_QUOTE 171 // « +//#define PS2_NOT_SIGN 172 // ¬ +//#define PS2_HYPHEN 173 +//#define PS2_REGISTERED_SIGN 174 // ® +//#define PS2_MACRON 175 // ¯ +#define PS2_DEGREE_SIGN 176 // ° +//#define PS2_PLUS_MINUS_SIGN 177 // ± +#define PS2_SUPERSCRIPT_TWO 178 // ² +//#define PS2_SUPERSCRIPT_THREE 179 // ³ +//#define PS2_ACUTE_ACCENT 180 // ´ +#define PS2_MICRO_SIGN 181 // µ +//#define PS2_PILCROW_SIGN 182 // ¶ +//#define PS2_MIDDLE_DOT 183 // · +//#define PS2_CEDILLA 184 // ¸ +//#define PS2_SUPERSCRIPT_ONE 185 // ¹ +//#define PS2_MASCULINE_ORDINAL 186 // º +//#define PS2_RIGHT_DOUBLE_ANGLE_QUOTE 187 // » +//#define PS2_FRACTION_ONE_QUARTER 188 // ¼ +//#define PS2_FRACTION_ONE_HALF 189 // ½ +//#define PS2_FRACTION_THREE_QUARTERS 190 // ¾ +//#define PS2_INVERTED_QUESTION MARK 191 // ¿ +//#define PS2_A_GRAVE 192 // À +//#define PS2_A_ACUTE 193 // à +//#define PS2_A_CIRCUMFLEX 194 //  +//#define PS2_A_TILDE 195 // à +//#define PS2_A_DIAERESIS 196 // Ä +//#define PS2_A_RING_ABOVE 197 // Ã… +//#define PS2_AE 198 // Æ +//#define PS2_C_CEDILLA 199 // Ç +//#define PS2_E_GRAVE 200 // È +//#define PS2_E_ACUTE 201 // É +//#define PS2_E_CIRCUMFLEX 202 // Ê +//#define PS2_E_DIAERESIS 203 // Ë +//#define PS2_I_GRAVE 204 // ÃŒ +//#define PS2_I_ACUTE 205 // à +//#define PS2_I_CIRCUMFLEX 206 // ÃŽ +//#define PS2_I_DIAERESIS 207 // à +//#define PS2_ETH 208 // à +//#define PS2_N_TILDE 209 // Ñ +//#define PS2_O_GRAVE 210 // Ã’ +//#define PS2_O_ACUTE 211 // Ó +//#define PS2_O_CIRCUMFLEX 212 // Ô +//#define PS2_O_TILDE 213 // Õ +//#define PS2_O_DIAERESIS 214 // Ö +//#define PS2_MULTIPLICATION 215 // × +//#define PS2_O_STROKE 216 // Ø +//#define PS2_U_GRAVE 217 // Ù +//#define PS2_U_ACUTE 218 // Ú +//#define PS2_U_CIRCUMFLEX 219 // Û +//#define PS2_U_DIAERESIS 220 // Ãœ +//#define PS2_Y_ACUTE 221 // à +//#define PS2_THORN 222 // Þ +//#define PS2_SHARP_S 223 // ß +#define PS2_a_GRAVE 224 // à +//#define PS2_a_ACUTE 225 // á +//#define PS2_a_CIRCUMFLEX 226 // â +//#define PS2_a_TILDE 227 // ã +//#define PS2_a_DIAERESIS 228 // ä +//#define PS2_a_RING_ABOVE 229 // Ã¥ +//#define PS2_ae 230 // æ +#define PS2_c_CEDILLA 231 // ç +#define PS2_e_GRAVE 232 // è +#define PS2_e_ACUTE 233 // é +//#define PS2_e_CIRCUMFLEX 234 // ê +//#define PS2_e_DIAERESIS 235 // ë +//#define PS2_i_GRAVE 236 // ì +//#define PS2_i_ACUTE 237 // í +//#define PS2_i_CIRCUMFLEX 238 // î +//#define PS2_i_DIAERESIS 239 // ï +//#define PS2_eth 240 // ð +//#define PS2_n_TILDE 241 // ñ +//#define PS2_o_GRAVE 242 // ò +//#define PS2_o_ACUTE 243 // ó +//#define PS2_o_CIRCUMFLEX 244 // ô +//#define PS2_o_TILDE 245 // õ +//#define PS2_o_DIAERESIS 246 // ö +//#define PS2_DIVISION 247 // ÷ +//#define PS2_o_STROKE 248 // ø +#define PS2_u_GRAVE 249 // ù +//#define PS2_u_ACUTE 250 // ú +//#define PS2_u_CIRCUMFLEX 251 // û +//#define PS2_u_DIAERESIS 252 // ü +//#define PS2_y_ACUTE 253 // ý +//#define PS2_thorn 254 // þ +//#define PS2_y_DIAERESIS 255 // ÿ +#endif //defined(OPTION_PS2_KEYBOARD_FRENCH) + + +// ------------- + + + +/* Previous versions of this library returned a mix of ascii characters + * and raw scan codes (if no ASCII character could be found). There was + * no way to determine if a byte was ascii or a raw scan code. With + * version 2.0, only ASCII is returned, plus the special bytes above. + * These raw scan codes are never returned by read() anymore. + * + * PS2 keyboard "make" codes to check for certain keys. + */ +//#define PS2_KC_BREAK 0xf0 +//#define PS2_KC_ENTER 0x0d +//#define PS2_KC_ESC 0x76 +//#define PS2_KC_KPLUS 0x79 +//#define PS2_KC_KMINUS 0x7b +//#define PS2_KC_KMULTI 0x7c +//#define PS2_KC_NUM 0x77 +//#define PS2_KC_BKSP 0x66 + + +/** + * Purpose: Provides an easy access to PS2 keyboards + * Author: Christian Weichel + */ +class K3NG_PS2Keyboard { + public: + /** + * This constructor does basically nothing. Please call the begin(int,int) + * method before using any other method of this class. + */ + K3NG_PS2Keyboard(); + + /** + * Starts the keyboard "service" by registering the external interrupt. + * setting the pin modes correctly and driving those needed to high. + * The propably best place to call this method is in the setup routine. + */ + static void begin(uint8_t dataPin, uint8_t irq_pin); + + /** + * Returns true if there is a char to be read, false if not. + */ + static bool available(); + + /** + * Returns the char last read from the keyboard. + * If there is no char availble, -1 is returned. + */ + static int read(); +}; + +// interrupt pins for known boards +#ifndef CORE_INT0_PIN // if not already defined by core (eg, Teensy) +#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) // Arduino Mega +#define CORE_INT0_PIN 2 +#define CORE_INT1_PIN 3 +#define CORE_INT2_PIN 21 +#define CORE_INT3_PIN 20 +#define CORE_INT4_PIN 19 +#define CORE_INT5_PIN 18 +#elif defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644__) // Sanguino +#define CORE_INT0_PIN 10 +#define CORE_INT1_PIN 11 +#define CORE_INT2_PIN 2 +#else // Arduino Duemilanove, Diecimila, LilyPad, Mini, Fio, etc... +#define CORE_INT0_PIN 2 +#define CORE_INT1_PIN 3 +#endif +#endif + +#endif diff --git a/libraries/K3NG_PS2Keyboard/library.properties b/libraries/K3NG_PS2Keyboard/library.properties new file mode 100755 index 0000000..2a091c2 --- /dev/null +++ b/libraries/K3NG_PS2Keyboard/library.properties @@ -0,0 +1,10 @@ +name=K3NG_PS2Keyboard +version=2016111701 +author=Anthony Good, K3NG +maintainer=Anthony Good, K3NG +sentence=For use with the K3NG CW Keyer for PS2 keyboard support +paragraph=For use with the K3NG CW Keyer for PS2 keyboard support +category=Uncategorized +url=https://github.com/k3ng/k3ng_cw_keyer +architectures=avr +includes=K3NG_PS2Keyboard.h \ No newline at end of file diff --git a/libraries/Keypad/Keypad.cpp b/libraries/Keypad/Keypad.cpp new file mode 100755 index 0000000..1bd0fdd --- /dev/null +++ b/libraries/Keypad/Keypad.cpp @@ -0,0 +1,256 @@ +/* +|| +|| @file Keypad.h +|| @version 2.0 +|| @author Mark Stanley, Alexander Brevig +|| @contact mstanley@technologist.com, alexanderbrevig@gmail.com +|| +|| @description +|| | This library provides a simple interface for using matrix +|| | keypads. It supports the use of multiple keypads with the +|| | same or different sets of keys. It also supports user +|| | selectable pins and definable keymaps. +|| # +|| +|| @license +|| | This library is free software; you can redistribute it and/or +|| | modify it under the terms of the GNU Lesser General Public +|| | License as published by the Free Software Foundation; version +|| | 2.1 of the License. +|| | +|| | This library is distributed in the hope that it will be useful, +|| | but WITHOUT ANY WARRANTY; without even the implied warranty of +|| | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +|| | Lesser General Public License for more details. +|| | +|| | You should have received a copy of the GNU Lesser General Public +|| | License along with this library; if not, write to the Free Software +|| | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +|| # +|| +*/ + +#include + +// <> Allows custom keymap, pin configuration, and keypad sizes. +Keypad::Keypad(char *userKeymap, byte *row, byte *col, byte numRows, byte numCols) { + rowPins = row; + columnPins = col; + size.rows = numRows; + size.columns = numCols; + + begin(userKeymap); + + setDebounceTime(2); + setHoldTime(500); + keypadEventListener = 0; + + transitionTo(IDLE); + stateChanged = false; + + initializePins(); +} + +// New in 2.0 this function lets the end user test for any changes in state +// before deciding if any variables, etc. need to be updated in their code. +boolean Keypad::keyStateChanged() { + return stateChanged; +} + +// Let the user define a keymap - assume the same row/column count as defined in constructor +void Keypad::begin( char *userKeymap) { + keymap = userKeymap; +} + +char Keypad::getKey() { + // Return the new key value if a keypress was detected. By testing for + // keyStateChanged() we don't return a keypress more than once. + if( getKeyState()==PRESSED && keyStateChanged() ) + { + return currentKey; + } + + return NO_KEY; // Otherwise just return the default key value: +} + +char Keypad::waitForKey() { + char waitKey = NO_KEY; + while( (waitKey = getKey()) == NO_KEY ); // Do nothing. Waiting for keypress. + return waitKey; +} + +// Private +// Scan the keypad and report whether or not a key (or any key) has been pressed. +// 2011-12-23 - Removed from getKeyState() for readability and ease of maintenance. +boolean Keypad::scanKeys() { + static unsigned int allKeys=0; + byte curKey=0; + boolean anyKey; + + // Assume that some other device is sharing the data pins used by the + // keypad. If that is the case then the pins will need to be re-intialized + // each time before they are used. + initializePins(); + + // I rewrote this method to provide a status change (anyKey OPEN/CLOSED) to the + // getKeyState() function which handles debouncing. Now we can scan the keypad + // without having to worry about huge debounce time delays. + for( int c=0; c10 ) + startTime = millis(); + else + return state; + + // Find out whether or not a key was pressed and if so which one it was. + buttons = scanKeys(); + + switch (state) { + case IDLE: + // The only thing to do while idling is to look for a debounced keypress. + if( buttons==CLOSED && (millis()-Timer)>debounceTime ) { + transitionTo(PRESSED); + Timer = millis(); + } + break; + case PRESSED: + // Waiting for a key hold... + if ( (millis()-Timer)>holdTime ) { + transitionTo(HOLD); // Move to next state. + Timer = millis(); // Reset debounce timer. + } + // Or for the key to be release. + else if ( buttons==OPEN && (millis()-Timer)>debounceTime ) { + transitionTo(RELEASED); + Timer = millis(); + } + break; + case HOLD: + // Waiting for the key to be released. + if ( (buttons==OPEN) && (millis()-Timer)>debounceTime ) { + transitionTo(RELEASED); + Timer = millis(); + } + break; + case RELEASED: + transitionTo(IDLE); + break; + } + return state; // Let the world know which state we're in. +} + +KeyState Keypad::getState() { + return state; +} + +void Keypad::setDebounceTime(unsigned int debounce) { + debounceTime = debounce; +} + +void Keypad::setHoldTime(unsigned int hold) { + holdTime = hold; +} + +void Keypad::addEventListener(void (*listener)(char)){ + keypadEventListener = listener; +} + +void Keypad::transitionTo(KeyState nextState) { + state = nextState; + stateChanged = true; + if (keypadEventListener!=NULL){ + keypadEventListener(currentKey); + } +} + +void Keypad::initializePins() { + //configure column pin modes and states + for (byte C=0; C= 100 +#include "Arduino.h" // for digitalRead, digitalWrite, etc +#else +#include "WProgram.h" +#endif + +#define OFF LOW +#define ON HIGH + +#define CLOSED LOW +#define OPEN HIGH + +#define makeKeymap(x) ((char*)x) + +typedef char KeypadEvent; + +typedef enum {IDLE, PRESSED, HOLD, RELEASED} KeyState; // KeyState was KeypadState + +// Made changes according to this post http://arduino.cc/forum/index.php?topic=58337.0 +// by Nick Gammon. Thanks for the input Nick. :) It actually saved 78 bytes for me. +typedef struct { + byte rows; + byte columns; +} KeypadSize; + +const char NO_KEY = '\0'; +#define KEY_RELEASED NO_KEY + +class Keypad { +public: + Keypad(char *userKeymap, byte *row, byte *col, byte numRows, byte numCols); + + void begin(char *userKeymap); + char getKey(); + KeyState getState(); + void setDebounceTime(unsigned int); + void setHoldTime(unsigned int); + void addEventListener(void (*listener)(char)); + // New methods + char waitForKey(); + boolean keyStateChanged(); + + //added by Daniel Kern + char* getBuffer(); + char* growBuffer(char key); + void clearBuffer(); + +private: + void transitionTo(KeyState); + void initializePins(); + + char *keymap; + byte *rowPins; + byte *columnPins; + KeypadSize size; + KeyState state; + char currentKey; + unsigned int debounceTime; + unsigned int holdTime; + void (*keypadEventListener)(char); + + // New methods - 2011-12-23 + boolean scanKeys(); + KeyState getKeyState(); + + // New members - 2011-12-23 + boolean buttons; + boolean stateChanged; + + //added by Daniel Kern + char keyBuffer[5]; +}; + +#endif + +/* +|| @changelog +|| | 2.0 2011-12-29 - Mark Stanley : Added waitForKey(). +|| | 2.0 2011-12-23 - Mark Stanley : Added the public function keyStateChanged(). +|| | 2.0 2011-12-23 - Mark Stanley : Added the private function scanKeys(). +|| | 2.0 2011-12-23 - Mark Stanley : Moved the Finite State Machine into the function getKeyState(). +|| | 2.0 2011-12-23 - Mark Stanley : Removed the member variable lastUdate. Not needed after rewrite. +|| | 1.8 2011-11-21 - Mark Stanley : Added test to determine which header file to compile, +|| | WProgram.h or Arduino.h. +|| | 1.8 2009-07-08 - Alexander Brevig : No longer uses arrays +|| | 1.7 2009-06-18 - Alexander Brevig : This library is a Finite State Machine every time a state changes +|| | the keypadEventListener will trigger, if set +|| | 1.7 2009-06-18 - Alexander Brevig : Added setDebounceTime setHoldTime specifies the amount of +|| | microseconds before a HOLD state triggers +|| | 1.7 2009-06-18 - Alexander Brevig : Added transitionTo +|| | 1.6 2009-06-15 - Alexander Brevig : Added getState() and state variable +|| | 1.5 2009-05-19 - Alexander Brevig : Added setHoldTime() +|| | 1.4 2009-05-15 - Alexander Brevig : Added addEventListener +|| | 1.3 2009-05-12 - Alexander Brevig : Added lastUdate, in order to do simple debouncing +|| | 1.2 2009-05-09 - Alexander Brevig : Changed getKey() +|| | 1.1 2009-04-28 - Alexander Brevig : Modified API, and made variables private +|| | 1.0 2007-XX-XX - Mark Stanley : Initial Release +|| # +*/ diff --git a/libraries/NewTone/NewTone.cpp b/libraries/NewTone/NewTone.cpp new file mode 100755 index 0000000..31c3c8f --- /dev/null +++ b/libraries/NewTone/NewTone.cpp @@ -0,0 +1,49 @@ +// --------------------------------------------------------------------------- +// Created by Tim Eckel - teckel@leethost.com +// Copyright 2013 License: GNU GPL v3 http://www.gnu.org/licenses/gpl-3.0.html +// +// See "NewTone.h" for purpose, syntax, version history, links, and more. +// --------------------------------------------------------------------------- + +#include "NewTone.h" + +unsigned long _nt_time; // Time note should end. +uint8_t _pinMask = 0; // Pin bitmask. +volatile uint8_t *_pinOutput; // Output port register + +void NewTone(uint8_t pin, unsigned long frequency, unsigned long length) { + uint8_t prescaler = _BV(CS10); // Try using prescaler 1 first. + unsigned long top = F_CPU / frequency / 4 - 1; // Calculate the top. + if (top > 65535) { // If not in the range for prescaler 1, use prescaler 256 (61 Hz and lower @ 16 MHz). + prescaler = _BV(CS12); // Set the 256 prescaler bit. + top = top / 256 - 1; // Calculate the top using prescaler 256. + } + + if (length > 0) _nt_time = millis() + length; else _nt_time = 0xFFFFFFFF; // Set when the note should end, or play "forever". + + if (_pinMask == 0) { + _pinMask = digitalPinToBitMask(pin); // Get the port register bitmask for the pin. + _pinOutput = portOutputRegister(digitalPinToPort(pin)); // Get the output port register for the pin. + uint8_t *_pinMode = (uint8_t *) portModeRegister(digitalPinToPort(pin)); // Get the port mode register for the pin. + *_pinMode |= _pinMask; // Set the pin to Output mode. + } + + ICR1 = top; // Set the top. + if (TCNT1 > top) TCNT1 = top; // Counter over the top, put within range. + TCCR1B = _BV(WGM13) | prescaler; // Set PWM, phase and frequency corrected (ICR1) and prescaler. + TCCR1A = _BV(COM1B0); + TIMSK1 |= _BV(OCIE1A); // Activate the timer interrupt. +} + +void noNewTone(uint8_t pin) { + TIMSK1 &= ~_BV(OCIE1A); // Remove the timer interrupt. + TCCR1B = _BV(CS11); // Default clock prescaler of 8. + TCCR1A = _BV(WGM10); // Set to defaults so PWM can work like normal (PWM, phase corrected, 8bit). + *_pinOutput &= ~_pinMask; // Set pin to LOW. + _pinMask = 0; // Flag so we know note is no longer playing. +} + +ISR(TIMER1_COMPA_vect) { // Timer interrupt vector. + if (millis() >= _nt_time) noNewTone(); // Check to see if it's time for the note to end. + *_pinOutput ^= _pinMask; // Toggle the pin state. +} \ No newline at end of file diff --git a/libraries/NewTone/NewTone.h b/libraries/NewTone/NewTone.h new file mode 100755 index 0000000..2bc3a61 --- /dev/null +++ b/libraries/NewTone/NewTone.h @@ -0,0 +1,50 @@ +// --------------------------------------------------------------------------- +// NewTone Library - v1.0 - 01/20/2013 +// +// AUTHOR/LICENSE: +// Created by Tim Eckel - teckel@leethost.com +// Copyright 2013 License: GNU GPL v3 http://www.gnu.org/licenses/gpl-3.0.html +// +// LINKS: +// Project home: http://code.google.com/p/arduino-new-tone/ +// Blog: http://arduino.cc/forum/index.php/XXX +// +// DISCLAIMER: +// This software is furnished "as is", without technical support, and with no +// warranty, express or implied, as to its usefulness for any purpose. +// +// PURPOSE: +// Almost 1,300 bytes smaller code size than the Tone library. Faster execution +// time. Exclusive use of port registers for fastest and smallest code. Higher +// quality sound output than tone library. Plug-in replacement for Tone. Uses +// timer 1 which may free up conflicts with the tone library. +// +// SYNTAX: +// NewTone( pin, frequency [, length ] ) - Play a note on pin at frequency in Hz. +// Parameters: +// * pin - Pin speaker is wired to (other wire to ground, be sure to add an inline 100 ohm resistor). +// * frequency - Play the specified frequency indefinitely, turn off with noNewTone(). +// * length - [optional] Set the length to play in milliseconds. (default: 0 [forever], range: 0 to 2^32-1) +// noNewTone(pin) - Stop playing note (pin is optional, will always stop playing on pin that was last used). +// +// HISTORY: +// 01/20/2013 v1.0 - Initial release. +// +// --------------------------------------------------------------------------- + +#ifndef NewTone_h + #define NewTone_h + + #if defined(ARDUINO) && ARDUINO >= 100 + #include + #else + #include + #endif + + #if defined(__AVR_ATmega8__) || defined(__AVR_ATmega128__) + #define TIMSK1 TIMSK + #endif + + void NewTone(uint8_t pin, unsigned long frequency, unsigned long length = 0); + void noNewTone(uint8_t pin = 0); +#endif \ No newline at end of file diff --git a/libraries/NewTone/keywords.txt b/libraries/NewTone/keywords.txt new file mode 100755 index 0000000..7e44a58 --- /dev/null +++ b/libraries/NewTone/keywords.txt @@ -0,0 +1,18 @@ +################################### +# Syntax Coloring Map For NewPing +################################### + +################################### +# Datatypes (KEYWORD1) +################################### + +################################### +# Methods and Functions (KEYWORD2) +################################### + +NewTone KEYWORD2 +noNewTone KEYWORD2 + +################################### +# Constants (LITERAL1) +################################### diff --git a/libraries/README.md b/libraries/README.md new file mode 100755 index 0000000..31ee67c --- /dev/null +++ b/libraries/README.md @@ -0,0 +1 @@ +AS OF VERSION 2.2.2016012004 LIBRARY FILES MUST BE PUT IN LIBRARIES DIRECTORY AND NOT THE INO SKETCH DIRECTORY !!!! diff --git a/libraries/USB_Host_Shield/.gitattributes b/libraries/USB_Host_Shield/.gitattributes new file mode 100755 index 0000000..6238b03 --- /dev/null +++ b/libraries/USB_Host_Shield/.gitattributes @@ -0,0 +1,23 @@ +# Auto detect text files and perform LF normalization +* text=auto +* text eol=lf + +# Custom for Visual Studio +*.cs diff=csharp +*.sln merge=union +*.csproj merge=union +*.vbproj merge=union +*.fsproj merge=union +*.dbproj merge=union + +# Standard to msysgit +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain diff --git a/libraries/USB_Host_Shield/.gitignore b/libraries/USB_Host_Shield/.gitignore new file mode 100755 index 0000000..7e69f45 --- /dev/null +++ b/libraries/USB_Host_Shield/.gitignore @@ -0,0 +1,4 @@ +*.bak +*.zip +*.rar +build/ \ No newline at end of file diff --git a/libraries/USB_Host_Shield/.gitmodules b/libraries/USB_Host_Shield/.gitmodules new file mode 100755 index 0000000..32a0783 --- /dev/null +++ b/libraries/USB_Host_Shield/.gitmodules @@ -0,0 +1,12 @@ +[submodule "examples/testusbhostFAT/generic_storage"] + path = examples/testusbhostFAT/generic_storage + url = https://github.com/xxxajk/generic_storage +[submodule "examples/testusbhostFAT/xmem2"] + path = examples/testusbhostFAT/xmem2 + url = https://github.com/xxxajk/xmem2 +[submodule "examples/testusbhostFAT/Arduino_Makefile_master"] + path = examples/testusbhostFAT/Arduino_Makefile_master + url = https://github.com/xxxajk/Arduino_Makefile_master +[submodule "examples/testusbhostFAT/RTClib"] + path = examples/testusbhostFAT/RTClib + url = https://github.com/xxxajk/RTClib diff --git a/libraries/USB_Host_Shield/BTD.cpp b/libraries/USB_Host_Shield/BTD.cpp new file mode 100755 index 0000000..42cf3d6 --- /dev/null +++ b/libraries/USB_Host_Shield/BTD.cpp @@ -0,0 +1,1363 @@ +/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved. + + This software may be distributed and modified under the terms of the GNU + General Public License version 2 (GPL2) as published by the Free Software + Foundation and appearing in the file GPL2.TXT included in the packaging of + this file. Please note that GPL2 Section 2[b] requires that all works based + on this software must also be made publicly available under the terms of + the GPL2 ("Copyleft"). + + Contact information + ------------------- + + Kristian Lauszus, TKJ Electronics + Web : http://www.tkjelectronics.com + e-mail : kristianl@tkjelectronics.com + */ + +#include "BTD.h" +// To enable serial debugging see "settings.h" +//#define EXTRADEBUG // Uncomment to get even more debugging data + +const uint8_t BTD::BTD_CONTROL_PIPE = 0; +const uint8_t BTD::BTD_EVENT_PIPE = 1; +const uint8_t BTD::BTD_DATAIN_PIPE = 2; +const uint8_t BTD::BTD_DATAOUT_PIPE = 3; + +BTD::BTD(USB *p) : +connectToWii(false), +pairWithWii(false), +connectToHIDDevice(false), +pairWithHIDDevice(false), +pUsb(p), // Pointer to USB class instance - mandatory +bAddress(0), // Device address - mandatory +bNumEP(1), // If config descriptor needs to be parsed +qNextPollTime(0), // Reset NextPollTime +pollInterval(0), +bPollEnable(false) // Don't start polling before dongle is connected +{ + for(uint8_t i = 0; i < BTD_NUM_SERVICES; i++) + btService[i] = NULL; + + Initialize(); // Set all variables, endpoint structs etc. to default values + + if(pUsb) // Register in USB subsystem + pUsb->RegisterDeviceClass(this); // Set devConfig[] entry +} + +uint8_t BTD::ConfigureDevice(uint8_t parent, uint8_t port, bool lowspeed) { + const uint8_t constBufSize = sizeof (USB_DEVICE_DESCRIPTOR); + uint8_t buf[constBufSize]; + USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast(buf); + uint8_t rcode; + UsbDevice *p = NULL; + EpInfo *oldep_ptr = NULL; + + Initialize(); // Set all variables, endpoint structs etc. to default values + + AddressPool &addrPool = pUsb->GetAddressPool(); // Get memory address of USB device address pool +#ifdef EXTRADEBUG + Notify(PSTR("\r\nBTD ConfigureDevice"), 0x80); +#endif + + if(bAddress) { // Check if address has already been assigned to an instance +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nAddress in use"), 0x80); +#endif + return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE; + } + + p = addrPool.GetUsbDevicePtr(0); // Get pointer to pseudo device with address 0 assigned + if(!p) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nAddress not found"), 0x80); +#endif + return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; + } + + if(!p->epinfo) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nepinfo is null"), 0x80); +#endif + return USB_ERROR_EPINFO_IS_NULL; + } + + oldep_ptr = p->epinfo; // Save old pointer to EP_RECORD of address 0 + p->epinfo = epInfo; // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence + p->lowspeed = lowspeed; + rcode = pUsb->getDevDescr(0, 0, constBufSize, (uint8_t*)buf); // Get device descriptor - addr, ep, nbytes, data + + p->epinfo = oldep_ptr; // Restore p->epinfo + + if(rcode) + goto FailGetDevDescr; + + bAddress = addrPool.AllocAddress(parent, false, port); // Allocate new address according to device class + + if(!bAddress) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nOut of address space"), 0x80); +#endif + return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL; + } + + epInfo[0].maxPktSize = udd->bMaxPacketSize0; // Extract Max Packet Size from device descriptor + epInfo[1].epAddr = udd->bNumConfigurations; // Steal and abuse from epInfo structure to save memory + + VID = udd->idVendor; + PID = udd->idProduct; + + return USB_ERROR_CONFIG_REQUIRES_ADDITIONAL_RESET; + +FailGetDevDescr: +#ifdef DEBUG_USB_HOST + NotifyFailGetDevDescr(rcode); +#endif + if(rcode != hrJERR) + rcode = USB_ERROR_FailGetDevDescr; + Release(); + return rcode; +}; + +uint8_t BTD::Init(uint8_t parent, uint8_t port, bool lowspeed) { + uint8_t rcode; + uint8_t num_of_conf = epInfo[1].epAddr; // Number of configurations + epInfo[1].epAddr = 0; + + AddressPool &addrPool = pUsb->GetAddressPool(); +#ifdef EXTRADEBUG + Notify(PSTR("\r\nBTD Init"), 0x80); +#endif + UsbDevice *p = addrPool.GetUsbDevicePtr(bAddress); // Get pointer to assigned address record + + if(!p) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nAddress not found"), 0x80); +#endif + return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; + } + + delay(300); // Assign new address to the device + + rcode = pUsb->setAddr(0, 0, bAddress); // Assign new address to the device + if(rcode) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nsetAddr: "), 0x80); + D_PrintHex (rcode, 0x80); +#endif + p->lowspeed = false; + goto Fail; + } +#ifdef EXTRADEBUG + Notify(PSTR("\r\nAddr: "), 0x80); + D_PrintHex (bAddress, 0x80); +#endif + + p->lowspeed = false; + + p = addrPool.GetUsbDevicePtr(bAddress); // Get pointer to assigned address record + if(!p) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nAddress not found"), 0x80); +#endif + return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; + } + + p->lowspeed = lowspeed; + + rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo); // Assign epInfo to epinfo pointer - only EP0 is known + if(rcode) + goto FailSetDevTblEntry; + + if(VID == PS3_VID && (PID == PS3_PID || PID == PS3NAVIGATION_PID || PID == PS3MOVE_PID)) { + delay(100); + rcode = pUsb->setConf(bAddress, epInfo[ BTD_CONTROL_PIPE ].epAddr, 1); // We only need the Control endpoint, so we don't have to initialize the other endpoints of device + if(rcode) + goto FailSetConfDescr; + +#ifdef DEBUG_USB_HOST + if(PID == PS3_PID || PID == PS3NAVIGATION_PID) { + if(PID == PS3_PID) + Notify(PSTR("\r\nDualshock 3 Controller Connected"), 0x80); + else // It must be a navigation controller + Notify(PSTR("\r\nNavigation Controller Connected"), 0x80); + } else // It must be a Motion controller + Notify(PSTR("\r\nMotion Controller Connected"), 0x80); +#endif + + if(my_bdaddr[0] == 0x00 && my_bdaddr[1] == 0x00 && my_bdaddr[2] == 0x00 && my_bdaddr[3] == 0x00 && my_bdaddr[4] == 0x00 && my_bdaddr[5] == 0x00) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nPlease plug in the dongle before trying to pair with the PS3 Controller\r\nor set the Bluetooth address in the constructor of the PS3BT class"), 0x80); +#endif + } else { + if(PID == PS3_PID || PID == PS3NAVIGATION_PID) + setBdaddr(my_bdaddr); // Set internal Bluetooth address + else + setMoveBdaddr(my_bdaddr); // Set internal Bluetooth address +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nBluetooth Address was set to: "), 0x80); + for(int8_t i = 5; i > 0; i--) { + D_PrintHex (my_bdaddr[i], 0x80); + Notify(PSTR(":"), 0x80); + } + D_PrintHex (my_bdaddr[0], 0x80); +#endif + } + + pUsb->setConf(bAddress, epInfo[ BTD_CONTROL_PIPE ].epAddr, 0); // Reset configuration value + pUsb->setAddr(bAddress, 0, 0); // Reset address + Release(); // Release device + return USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED; // Return + } else { + // Check if attached device is a Bluetooth dongle and fill endpoint data structure + // First interface in the configuration must have Bluetooth assigned Class/Subclass/Protocol + // And 3 endpoints - interrupt-IN, bulk-IN, bulk-OUT, not necessarily in this order + for(uint8_t i = 0; i < num_of_conf; i++) { + if(VID == IOGEAR_GBU521_VID && PID == IOGEAR_GBU521_PID) { + ConfigDescParser confDescrParser(this); // Needed for the IOGEAR GBU521 + rcode = pUsb->getConfDescr(bAddress, 0, i, &confDescrParser); + } else { + ConfigDescParser confDescrParser(this); + rcode = pUsb->getConfDescr(bAddress, 0, i, &confDescrParser); + } + if(rcode) // Check error code + goto FailGetConfDescr; + if(bNumEP >= BTD_MAX_ENDPOINTS) // All endpoints extracted + break; + } + + if(bNumEP < BTD_MAX_ENDPOINTS) + goto FailUnknownDevice; + + // Assign epInfo to epinfo pointer - this time all 3 endpoins + rcode = pUsb->setEpInfoEntry(bAddress, bNumEP, epInfo); + if(rcode) + goto FailSetDevTblEntry; + + // Set Configuration Value + rcode = pUsb->setConf(bAddress, epInfo[ BTD_CONTROL_PIPE ].epAddr, bConfNum); + if(rcode) + goto FailSetConfDescr; + + hci_num_reset_loops = 100; // only loop 100 times before trying to send the hci reset command + hci_counter = 0; + hci_state = HCI_INIT_STATE; + watingForConnection = false; + bPollEnable = true; + +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nBluetooth Dongle Initialized"), 0x80); +#endif + } + return 0; // Successful configuration + + /* Diagnostic messages */ +FailSetDevTblEntry: +#ifdef DEBUG_USB_HOST + NotifyFailSetDevTblEntry(); + goto Fail; +#endif + +FailGetConfDescr: +#ifdef DEBUG_USB_HOST + NotifyFailGetConfDescr(); + goto Fail; +#endif + +FailSetConfDescr: +#ifdef DEBUG_USB_HOST + NotifyFailSetConfDescr(); +#endif + goto Fail; + +FailUnknownDevice: +#ifdef DEBUG_USB_HOST + NotifyFailUnknownDevice(VID, PID); +#endif + pUsb->setAddr(bAddress, 0, 0); // Reset address + rcode = USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED; +Fail: +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nBTD Init Failed, error code: "), 0x80); + NotifyFail(rcode); +#endif + Release(); + return rcode; +} + +void BTD::Initialize() { + uint8_t i; + for(i = 0; i < BTD_MAX_ENDPOINTS; i++) { + epInfo[i].epAddr = 0; + epInfo[i].maxPktSize = (i) ? 0 : 8; + epInfo[i].epAttribs = 0; + epInfo[i].bmNakPower = (i) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER; + } + for(i = 0; i < BTD_NUM_SERVICES; i++) { + if(btService[i]) + btService[i]->Reset(); // Reset all Bluetooth services + } + + connectToWii = false; + incomingWii = false; + connectToHIDDevice = false; + incomingHIDDevice = false; + incomingPS4 = false; + bAddress = 0; // Clear device address + bNumEP = 1; // Must have to be reset to 1 + qNextPollTime = 0; // Reset next poll time + pollInterval = 0; + bPollEnable = false; // Don't start polling before dongle is connected +} + +/* Extracts interrupt-IN, bulk-IN, bulk-OUT endpoint information from config descriptor */ +void BTD::EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *pep) { + //ErrorMessage(PSTR("Conf.Val"),conf); + //ErrorMessage(PSTR("Iface Num"),iface); + //ErrorMessage(PSTR("Alt.Set"),alt); + + if(alt) // Wrong interface - by BT spec, no alt setting + return; + + bConfNum = conf; + uint8_t index; + + if((pep->bmAttributes & 0x03) == 3 && (pep->bEndpointAddress & 0x80) == 0x80) { // Interrupt In endpoint found + index = BTD_EVENT_PIPE; + epInfo[index].bmNakPower = USB_NAK_NOWAIT; + } else { + if((pep->bmAttributes & 0x02) == 2) // Bulk endpoint found + index = ((pep->bEndpointAddress & 0x80) == 0x80) ? BTD_DATAIN_PIPE : BTD_DATAOUT_PIPE; + else + return; + } + + // Fill the rest of endpoint data structure + epInfo[index].epAddr = (pep->bEndpointAddress & 0x0F); + epInfo[index].maxPktSize = (uint8_t)pep->wMaxPacketSize; +#ifdef EXTRADEBUG + PrintEndpointDescriptor(pep); +#endif + if(pollInterval < pep->bInterval) // Set the polling interval as the largest polling interval obtained from endpoints + pollInterval = pep->bInterval; + bNumEP++; +} + +void BTD::PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr) { +#ifdef EXTRADEBUG + Notify(PSTR("\r\nEndpoint descriptor:"), 0x80); + Notify(PSTR("\r\nLength:\t\t"), 0x80); + D_PrintHex (ep_ptr->bLength, 0x80); + Notify(PSTR("\r\nType:\t\t"), 0x80); + D_PrintHex (ep_ptr->bDescriptorType, 0x80); + Notify(PSTR("\r\nAddress:\t"), 0x80); + D_PrintHex (ep_ptr->bEndpointAddress, 0x80); + Notify(PSTR("\r\nAttributes:\t"), 0x80); + D_PrintHex (ep_ptr->bmAttributes, 0x80); + Notify(PSTR("\r\nMaxPktSize:\t"), 0x80); + D_PrintHex (ep_ptr->wMaxPacketSize, 0x80); + Notify(PSTR("\r\nPoll Intrv:\t"), 0x80); + D_PrintHex (ep_ptr->bInterval, 0x80); +#endif +} + +/* Performs a cleanup after failed Init() attempt */ +uint8_t BTD::Release() { + Initialize(); // Set all variables, endpoint structs etc. to default values + pUsb->GetAddressPool().FreeAddress(bAddress); + return 0; +} + +uint8_t BTD::Poll() { + if(!bPollEnable) + return 0; + if((long)(millis() - qNextPollTime) >= 0L) { // Don't poll if shorter than polling interval + qNextPollTime = millis() + pollInterval; // Set new poll time + HCI_event_task(); // Poll the HCI event pipe + HCI_task(); // HCI state machine + ACL_event_task(); // Poll the ACL input pipe too + } + return 0; +} + +void BTD::disconnect() { + for(uint8_t i = 0; i < BTD_NUM_SERVICES; i++) + if(btService[i]) + btService[i]->disconnect(); +}; + +void BTD::HCI_event_task() { + uint16_t length = BULK_MAXPKTSIZE; // Request more than 16 bytes anyway, the inTransfer routine will take care of this + uint8_t rcode = pUsb->inTransfer(bAddress, epInfo[ BTD_EVENT_PIPE ].epAddr, &length, hcibuf); // Input on endpoint 1 + + if(!rcode || rcode == hrNAK) { // Check for errors + switch(hcibuf[0]) { // Switch on event type + case EV_COMMAND_COMPLETE: + if(!hcibuf[5]) { // Check if command succeeded + hci_set_flag(HCI_FLAG_CMD_COMPLETE); // Set command complete flag + if((hcibuf[3] == 0x01) && (hcibuf[4] == 0x10)) { // Parameters from read local version information + hci_version = hcibuf[6]; // Used to check if it supports 2.0+EDR - see http://www.bluetooth.org/Technical/AssignedNumbers/hci.htm + hci_set_flag(HCI_FLAG_READ_VERSION); + } else if((hcibuf[3] == 0x09) && (hcibuf[4] == 0x10)) { // Parameters from read local bluetooth address + for(uint8_t i = 0; i < 6; i++) + my_bdaddr[i] = hcibuf[6 + i]; + hci_set_flag(HCI_FLAG_READ_BDADDR); + } + } + break; + + case EV_COMMAND_STATUS: + if(hcibuf[2]) { // Show status on serial if not OK +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nHCI Command Failed: "), 0x80); + D_PrintHex (hcibuf[2], 0x80); +#endif + } + break; + + case EV_INQUIRY_COMPLETE: + if(inquiry_counter >= 5 && (pairWithWii || pairWithHIDDevice)) { + inquiry_counter = 0; +#ifdef DEBUG_USB_HOST + if(pairWithWii) + Notify(PSTR("\r\nCouldn't find Wiimote"), 0x80); + else + Notify(PSTR("\r\nCouldn't find HID device"), 0x80); +#endif + connectToWii = false; + pairWithWii = false; + connectToHIDDevice = false; + pairWithHIDDevice = false; + hci_state = HCI_SCANNING_STATE; + } + inquiry_counter++; + break; + + case EV_INQUIRY_RESULT: + if(hcibuf[2]) { // Check that there is more than zero responses +#ifdef EXTRADEBUG + Notify(PSTR("\r\nNumber of responses: "), 0x80); + Notify(hcibuf[2], 0x80); +#endif + for(uint8_t i = 0; i < hcibuf[2]; i++) { + uint8_t offset = 8 * hcibuf[2] + 3 * i; + + for(uint8_t j = 0; j < 3; j++) + classOfDevice[j] = hcibuf[j + 4 + offset]; + + if(pairWithWii && classOfDevice[2] == 0x00 && (classOfDevice[1] & 0x05) && (classOfDevice[0] & 0x0C)) { // See http://wiibrew.org/wiki/Wiimote#SDP_information + if(classOfDevice[0] & 0x08) // Check if it's the new Wiimote with motion plus inside that was detected + motionPlusInside = true; + else + motionPlusInside = false; + + for(uint8_t j = 0; j < 6; j++) + disc_bdaddr[j] = hcibuf[j + 3 + 6 * i]; + + hci_set_flag(HCI_FLAG_DEVICE_FOUND); + break; + } else if(pairWithHIDDevice && (classOfDevice[1] & 0x05) && (classOfDevice[0] & 0xC8)) { // Check if it is a mouse, keyboard or a gamepad - see: http://bluetooth-pentest.narod.ru/software/bluetooth_class_of_device-service_generator.html +#ifdef DEBUG_USB_HOST + if(classOfDevice[0] & 0x80) + Notify(PSTR("\r\nMouse found"), 0x80); + if(classOfDevice[0] & 0x40) + Notify(PSTR("\r\nKeyboard found"), 0x80); + if(classOfDevice[0] & 0x08) + Notify(PSTR("\r\nGamepad found"), 0x80); +#endif + + for(uint8_t j = 0; j < 6; j++) + disc_bdaddr[j] = hcibuf[j + 3 + 6 * i]; + + hci_set_flag(HCI_FLAG_DEVICE_FOUND); + break; + } +#ifdef EXTRADEBUG + else { + Notify(PSTR("\r\nClass of device: "), 0x80); + D_PrintHex (classOfDevice[2], 0x80); + Notify(PSTR(" "), 0x80); + D_PrintHex (classOfDevice[1], 0x80); + Notify(PSTR(" "), 0x80); + D_PrintHex (classOfDevice[0], 0x80); + } +#endif + } + } + break; + + case EV_CONNECT_COMPLETE: + hci_set_flag(HCI_FLAG_CONNECT_EVENT); + if(!hcibuf[2]) { // Check if connected OK +#ifdef EXTRADEBUG + Notify(PSTR("\r\nConnection established"), 0x80); +#endif + hci_handle = hcibuf[3] | ((hcibuf[4] & 0x0F) << 8); // Store the handle for the ACL connection + hci_set_flag(HCI_FLAG_CONNECT_COMPLETE); // Set connection complete flag + } else { + hci_state = HCI_CHECK_DEVICE_SERVICE; +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nConnection Failed: "), 0x80); + D_PrintHex (hcibuf[2], 0x80); +#endif + } + break; + + case EV_DISCONNECT_COMPLETE: + if(!hcibuf[2]) { // Check if disconnected OK + hci_set_flag(HCI_FLAG_DISCONNECT_COMPLETE); // Set disconnect command complete flag + hci_clear_flag(HCI_FLAG_CONNECT_COMPLETE); // Clear connection complete flag + } + break; + + case EV_REMOTE_NAME_COMPLETE: + if(!hcibuf[2]) { // Check if reading is OK + for(uint8_t i = 0; i < min(sizeof (remote_name), sizeof (hcibuf) - 9); i++) { + remote_name[i] = hcibuf[9 + i]; + if(remote_name[i] == '\0') // End of string + break; + } + // TODO: Altid sæt '\0' i remote name! + hci_set_flag(HCI_FLAG_REMOTE_NAME_COMPLETE); + } + break; + + case EV_INCOMING_CONNECT: + for(uint8_t i = 0; i < 6; i++) + disc_bdaddr[i] = hcibuf[i + 2]; + + for(uint8_t i = 0; i < 3; i++) + classOfDevice[i] = hcibuf[i + 8]; + + if((classOfDevice[1] & 0x05) && (classOfDevice[0] & 0xC8)) { // Check if it is a mouse, keyboard or a gamepad +#ifdef DEBUG_USB_HOST + if(classOfDevice[0] & 0x80) + Notify(PSTR("\r\nMouse is connecting"), 0x80); + if(classOfDevice[0] & 0x40) + Notify(PSTR("\r\nKeyboard is connecting"), 0x80); + if(classOfDevice[0] & 0x08) + Notify(PSTR("\r\nGamepad is connecting"), 0x80); +#endif + incomingHIDDevice = true; + } + +#ifdef EXTRADEBUG + Notify(PSTR("\r\nClass of device: "), 0x80); + D_PrintHex (classOfDevice[2], 0x80); + Notify(PSTR(" "), 0x80); + D_PrintHex (classOfDevice[1], 0x80); + Notify(PSTR(" "), 0x80); + D_PrintHex (classOfDevice[0], 0x80); +#endif + hci_set_flag(HCI_FLAG_INCOMING_REQUEST); + break; + + case EV_PIN_CODE_REQUEST: + if(pairWithWii) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nPairing with wiimote"), 0x80); +#endif + hci_pin_code_request_reply(); + } else if(btdPin != NULL) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nBluetooth pin is set too: "), 0x80); + NotifyStr(btdPin, 0x80); +#endif + hci_pin_code_request_reply(); + } else { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nNo pin was set"), 0x80); +#endif + hci_pin_code_negative_request_reply(); + } + break; + + case EV_LINK_KEY_REQUEST: +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nReceived Key Request"), 0x80); +#endif + hci_link_key_request_negative_reply(); + break; + + case EV_AUTHENTICATION_COMPLETE: + if(pairWithWii && !connectToWii) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nPairing successful with Wiimote"), 0x80); +#endif + connectToWii = true; // Used to indicate to the Wii service, that it should connect to this device + } else if(pairWithHIDDevice && !connectToHIDDevice) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nPairing successful with HID device"), 0x80); +#endif + connectToHIDDevice = true; // Used to indicate to the BTHID service, that it should connect to this device + } + break; + /* We will just ignore the following events */ + case EV_NUM_COMPLETE_PKT: + case EV_ROLE_CHANGED: + case EV_PAGE_SCAN_REP_MODE: + case EV_LOOPBACK_COMMAND: + case EV_DATA_BUFFER_OVERFLOW: + case EV_CHANGE_CONNECTION_LINK: + case EV_MAX_SLOTS_CHANGE: + case EV_QOS_SETUP_COMPLETE: + case EV_LINK_KEY_NOTIFICATION: + case EV_ENCRYPTION_CHANGE: + case EV_READ_REMOTE_VERSION_INFORMATION_COMPLETE: + break; +#ifdef EXTRADEBUG + default: + if(hcibuf[0] != 0x00) { + Notify(PSTR("\r\nUnmanaged HCI Event: "), 0x80); + D_PrintHex (hcibuf[0], 0x80); + } + break; +#endif + } // Switch + } +#ifdef EXTRADEBUG + else { + Notify(PSTR("\r\nHCI event error: "), 0x80); + D_PrintHex (rcode, 0x80); + } +#endif +} + +/* Poll Bluetooth and print result */ +void BTD::HCI_task() { + switch(hci_state) { + case HCI_INIT_STATE: + hci_counter++; + if(hci_counter > hci_num_reset_loops) { // wait until we have looped x times to clear any old events + hci_reset(); + hci_state = HCI_RESET_STATE; + hci_counter = 0; + } + break; + + case HCI_RESET_STATE: + hci_counter++; + if(hci_check_flag(HCI_FLAG_CMD_COMPLETE)) { + hci_counter = 0; +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nHCI Reset complete"), 0x80); +#endif + hci_state = HCI_CLASS_STATE; + hci_write_class_of_device(); + } else if(hci_counter > hci_num_reset_loops) { + hci_num_reset_loops *= 10; + if(hci_num_reset_loops > 2000) + hci_num_reset_loops = 2000; +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nNo response to HCI Reset"), 0x80); +#endif + hci_state = HCI_INIT_STATE; + hci_counter = 0; + } + break; + + case HCI_CLASS_STATE: + if(hci_check_flag(HCI_FLAG_CMD_COMPLETE)) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nWrite class of device"), 0x80); +#endif + hci_state = HCI_BDADDR_STATE; + hci_read_bdaddr(); + } + break; + + case HCI_BDADDR_STATE: + if(hci_check_flag(HCI_FLAG_READ_BDADDR)) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nLocal Bluetooth Address: "), 0x80); + for(int8_t i = 5; i > 0; i--) { + D_PrintHex (my_bdaddr[i], 0x80); + Notify(PSTR(":"), 0x80); + } + D_PrintHex (my_bdaddr[0], 0x80); +#endif + hci_read_local_version_information(); + hci_state = HCI_LOCAL_VERSION_STATE; + } + break; + + case HCI_LOCAL_VERSION_STATE: // The local version is used by the PS3BT class + if(hci_check_flag(HCI_FLAG_READ_VERSION)) { + if(btdName != NULL) { + hci_set_local_name(btdName); + hci_state = HCI_SET_NAME_STATE; + } else + hci_state = HCI_CHECK_DEVICE_SERVICE; + } + break; + + case HCI_SET_NAME_STATE: + if(hci_check_flag(HCI_FLAG_CMD_COMPLETE)) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nThe name is set to: "), 0x80); + NotifyStr(btdName, 0x80); +#endif + hci_state = HCI_CHECK_DEVICE_SERVICE; + } + break; + + case HCI_CHECK_DEVICE_SERVICE: + if(pairWithHIDDevice || pairWithWii) { // Check if it should try to connect to a Wiimote +#ifdef DEBUG_USB_HOST + if(pairWithWii) + Notify(PSTR("\r\nStarting inquiry\r\nPress 1 & 2 on the Wiimote\r\nOr press sync if you are using a Wii U Pro Controller"), 0x80); + else + Notify(PSTR("\r\nPlease enable discovery of your device"), 0x80); +#endif + hci_inquiry(); + hci_state = HCI_INQUIRY_STATE; + } else + hci_state = HCI_SCANNING_STATE; // Don't try to connect to a Wiimote + break; + + case HCI_INQUIRY_STATE: + if(hci_check_flag(HCI_FLAG_DEVICE_FOUND)) { + hci_inquiry_cancel(); // Stop inquiry +#ifdef DEBUG_USB_HOST + if(pairWithWii) + Notify(PSTR("\r\nWiimote found"), 0x80); + else + Notify(PSTR("\r\nHID device found"), 0x80); + + Notify(PSTR("\r\nNow just create the instance like so:"), 0x80); + if(pairWithWii) + Notify(PSTR("\r\nWII Wii(&Btd);"), 0x80); + else + Notify(PSTR("\r\nBTHID bthid(&Btd);"), 0x80); + + Notify(PSTR("\r\nAnd then press any button on the "), 0x80); + if(pairWithWii) + Notify(PSTR("Wiimote"), 0x80); + else + Notify(PSTR("device"), 0x80); +#endif + if(motionPlusInside) { + hci_remote_name(); // We need to know the name to distinguish between a Wiimote and a Wii U Pro Controller + hci_state = HCI_REMOTE_NAME_STATE; + } else + hci_state = HCI_CONNECT_DEVICE_STATE; + } + break; + + case HCI_CONNECT_DEVICE_STATE: + if(hci_check_flag(HCI_FLAG_CMD_COMPLETE)) { +#ifdef DEBUG_USB_HOST + if(pairWithWii) + Notify(PSTR("\r\nConnecting to Wiimote"), 0x80); + else + Notify(PSTR("\r\nConnecting to HID device"), 0x80); +#endif + hci_connect(); + hci_state = HCI_CONNECTED_DEVICE_STATE; + } + break; + + case HCI_CONNECTED_DEVICE_STATE: + if(hci_check_flag(HCI_FLAG_CONNECT_EVENT)) { + if(hci_check_flag(HCI_FLAG_CONNECT_COMPLETE)) { +#ifdef DEBUG_USB_HOST + if(pairWithWii) + Notify(PSTR("\r\nConnected to Wiimote"), 0x80); + else + Notify(PSTR("\r\nConnected to HID device"), 0x80); +#endif + hci_authentication_request(); // This will start the pairing with the Wiimote + hci_state = HCI_SCANNING_STATE; + } else { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nTrying to connect one more time..."), 0x80); +#endif + hci_connect(); // Try to connect one more time + } + } + break; + + case HCI_SCANNING_STATE: + if(!connectToWii && !pairWithWii && !connectToHIDDevice && !pairWithHIDDevice) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nWait For Incoming Connection Request"), 0x80); +#endif + hci_write_scan_enable(); + watingForConnection = true; + hci_state = HCI_CONNECT_IN_STATE; + } + break; + + case HCI_CONNECT_IN_STATE: + if(hci_check_flag(HCI_FLAG_INCOMING_REQUEST)) { + watingForConnection = false; +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nIncoming Connection Request"), 0x80); +#endif + hci_remote_name(); + hci_state = HCI_REMOTE_NAME_STATE; + } else if(hci_check_flag(HCI_FLAG_DISCONNECT_COMPLETE)) + hci_state = HCI_DISCONNECT_STATE; + break; + + case HCI_REMOTE_NAME_STATE: + if(hci_check_flag(HCI_FLAG_REMOTE_NAME_COMPLETE)) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nRemote Name: "), 0x80); + for(uint8_t i = 0; i < strlen(remote_name); i++) + Notifyc(remote_name[i], 0x80); +#endif + if(strncmp((const char*)remote_name, "Nintendo", 8) == 0) { + incomingWii = true; +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nWiimote is connecting"), 0x80); +#endif + if(strncmp((const char*)remote_name, "Nintendo RVL-CNT-01-TR", 22) == 0) { +#ifdef DEBUG_USB_HOST + Notify(PSTR(" with Motion Plus Inside"), 0x80); +#endif + motionPlusInside = true; + } else if(strncmp((const char*)remote_name, "Nintendo RVL-CNT-01-UC", 22) == 0) { +#ifdef DEBUG_USB_HOST + Notify(PSTR(" - Wii U Pro Controller"), 0x80); +#endif + motionPlusInside = true; + wiiUProController = true; + } else { + motionPlusInside = false; + wiiUProController = false; + } + } + if(classOfDevice[2] == 0 && classOfDevice[1] == 0x25 && classOfDevice[0] == 0x08 && strncmp((const char*)remote_name, "Wireless Controller", 19) == 0) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nPS4 controller is connecting"), 0x80); +#endif + incomingPS4 = true; + } + if(pairWithWii && motionPlusInside) + hci_state = HCI_CONNECT_DEVICE_STATE; + else { + hci_accept_connection(); + hci_state = HCI_CONNECTED_STATE; + } + } + break; + + case HCI_CONNECTED_STATE: + if(hci_check_flag(HCI_FLAG_CONNECT_COMPLETE)) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nConnected to Device: "), 0x80); + for(int8_t i = 5; i > 0; i--) { + D_PrintHex (disc_bdaddr[i], 0x80); + Notify(PSTR(":"), 0x80); + } + D_PrintHex (disc_bdaddr[0], 0x80); +#endif + if(incomingPS4) + connectToHIDDevice = true; // We should always connect to the PS4 controller + + // Clear these flags for a new connection + l2capConnectionClaimed = false; + sdpConnectionClaimed = false; + rfcommConnectionClaimed = false; + + hci_event_flag = 0; + hci_state = HCI_DONE_STATE; + } + break; + + case HCI_DONE_STATE: + hci_counter++; + if(hci_counter > 1000) { // Wait until we have looped 1000 times to make sure that the L2CAP connection has been started + hci_counter = 0; + hci_state = HCI_SCANNING_STATE; + } + break; + + case HCI_DISCONNECT_STATE: + if(hci_check_flag(HCI_FLAG_DISCONNECT_COMPLETE)) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nHCI Disconnected from Device"), 0x80); +#endif + hci_event_flag = 0; // Clear all flags + + // Reset all buffers + memset(hcibuf, 0, BULK_MAXPKTSIZE); + memset(l2capinbuf, 0, BULK_MAXPKTSIZE); + + connectToWii = incomingWii = pairWithWii = false; + connectToHIDDevice = incomingHIDDevice = pairWithHIDDevice = false; + incomingPS4 = false; + + hci_state = HCI_SCANNING_STATE; + } + break; + default: + break; + } +} + +void BTD::ACL_event_task() { + uint16_t length = BULK_MAXPKTSIZE; + uint8_t rcode = pUsb->inTransfer(bAddress, epInfo[ BTD_DATAIN_PIPE ].epAddr, &length, l2capinbuf); // Input on endpoint 2 + + if(!rcode) { // Check for errors + if(length > 0) { // Check if any data was read + for(uint8_t i = 0; i < BTD_NUM_SERVICES; i++) { + if(btService[i]) + btService[i]->ACLData(l2capinbuf); + } + } + } +#ifdef EXTRADEBUG + else if(rcode != hrNAK) { + Notify(PSTR("\r\nACL data in error: "), 0x80); + D_PrintHex (rcode, 0x80); + } +#endif + for(uint8_t i = 0; i < BTD_NUM_SERVICES; i++) + if(btService[i]) + btService[i]->Run(); +} + +/************************************************************/ +/* HCI Commands */ + +/************************************************************/ +void BTD::HCI_Command(uint8_t* data, uint16_t nbytes) { + hci_clear_flag(HCI_FLAG_CMD_COMPLETE); + pUsb->ctrlReq(bAddress, epInfo[ BTD_CONTROL_PIPE ].epAddr, bmREQ_HCI_OUT, 0x00, 0x00, 0x00, 0x00, nbytes, nbytes, data, NULL); +} + +void BTD::hci_reset() { + hci_event_flag = 0; // Clear all the flags + hcibuf[0] = 0x03; // HCI OCF = 3 + hcibuf[1] = 0x03 << 2; // HCI OGF = 3 + hcibuf[2] = 0x00; + + HCI_Command(hcibuf, 3); +} + +void BTD::hci_write_scan_enable() { + hci_clear_flag(HCI_FLAG_INCOMING_REQUEST); + hcibuf[0] = 0x1A; // HCI OCF = 1A + hcibuf[1] = 0x03 << 2; // HCI OGF = 3 + hcibuf[2] = 0x01; // parameter length = 1 + if(btdName != NULL) + hcibuf[3] = 0x03; // Inquiry Scan enabled. Page Scan enabled. + else + hcibuf[3] = 0x02; // Inquiry Scan disabled. Page Scan enabled. + + HCI_Command(hcibuf, 4); +} + +void BTD::hci_write_scan_disable() { + hcibuf[0] = 0x1A; // HCI OCF = 1A + hcibuf[1] = 0x03 << 2; // HCI OGF = 3 + hcibuf[2] = 0x01; // parameter length = 1 + hcibuf[3] = 0x00; // Inquiry Scan disabled. Page Scan disabled. + + HCI_Command(hcibuf, 4); +} + +void BTD::hci_read_bdaddr() { + hci_clear_flag(HCI_FLAG_READ_BDADDR); + hcibuf[0] = 0x09; // HCI OCF = 9 + hcibuf[1] = 0x04 << 2; // HCI OGF = 4 + hcibuf[2] = 0x00; + + HCI_Command(hcibuf, 3); +} + +void BTD::hci_read_local_version_information() { + hci_clear_flag(HCI_FLAG_READ_VERSION); + hcibuf[0] = 0x01; // HCI OCF = 1 + hcibuf[1] = 0x04 << 2; // HCI OGF = 4 + hcibuf[2] = 0x00; + + HCI_Command(hcibuf, 3); +} + +void BTD::hci_accept_connection() { + hci_clear_flag(HCI_FLAG_CONNECT_COMPLETE); + hcibuf[0] = 0x09; // HCI OCF = 9 + hcibuf[1] = 0x01 << 2; // HCI OGF = 1 + hcibuf[2] = 0x07; // parameter length 7 + hcibuf[3] = disc_bdaddr[0]; // 6 octet bdaddr + hcibuf[4] = disc_bdaddr[1]; + hcibuf[5] = disc_bdaddr[2]; + hcibuf[6] = disc_bdaddr[3]; + hcibuf[7] = disc_bdaddr[4]; + hcibuf[8] = disc_bdaddr[5]; + hcibuf[9] = 0x00; // Switch role to master + + HCI_Command(hcibuf, 10); +} + +void BTD::hci_remote_name() { + hci_clear_flag(HCI_FLAG_REMOTE_NAME_COMPLETE); + hcibuf[0] = 0x19; // HCI OCF = 19 + hcibuf[1] = 0x01 << 2; // HCI OGF = 1 + hcibuf[2] = 0x0A; // parameter length = 10 + hcibuf[3] = disc_bdaddr[0]; // 6 octet bdaddr + hcibuf[4] = disc_bdaddr[1]; + hcibuf[5] = disc_bdaddr[2]; + hcibuf[6] = disc_bdaddr[3]; + hcibuf[7] = disc_bdaddr[4]; + hcibuf[8] = disc_bdaddr[5]; + hcibuf[9] = 0x01; // Page Scan Repetition Mode + hcibuf[10] = 0x00; // Reserved + hcibuf[11] = 0x00; // Clock offset - low byte + hcibuf[12] = 0x00; // Clock offset - high byte + + HCI_Command(hcibuf, 13); +} + +void BTD::hci_set_local_name(const char* name) { + hcibuf[0] = 0x13; // HCI OCF = 13 + hcibuf[1] = 0x03 << 2; // HCI OGF = 3 + hcibuf[2] = strlen(name) + 1; // parameter length = the length of the string + end byte + uint8_t i; + for(i = 0; i < strlen(name); i++) + hcibuf[i + 3] = name[i]; + hcibuf[i + 3] = 0x00; // End of string + + HCI_Command(hcibuf, 4 + strlen(name)); +} + +void BTD::hci_inquiry() { + hci_clear_flag(HCI_FLAG_DEVICE_FOUND); + hcibuf[0] = 0x01; + hcibuf[1] = 0x01 << 2; // HCI OGF = 1 + hcibuf[2] = 0x05; // Parameter Total Length = 5 + hcibuf[3] = 0x33; // LAP: Genera/Unlimited Inquiry Access Code (GIAC = 0x9E8B33) - see https://www.bluetooth.org/Technical/AssignedNumbers/baseband.htm + hcibuf[4] = 0x8B; + hcibuf[5] = 0x9E; + hcibuf[6] = 0x30; // Inquiry time = 61.44 sec (maximum) + hcibuf[7] = 0x0A; // 10 number of responses + + HCI_Command(hcibuf, 8); +} + +void BTD::hci_inquiry_cancel() { + hcibuf[0] = 0x02; + hcibuf[1] = 0x01 << 2; // HCI OGF = 1 + hcibuf[2] = 0x00; // Parameter Total Length = 0 + + HCI_Command(hcibuf, 3); +} + +void BTD::hci_connect() { + hci_connect(disc_bdaddr); // Use last discovered device +} + +void BTD::hci_connect(uint8_t *bdaddr) { + hci_clear_flag(HCI_FLAG_CONNECT_COMPLETE | HCI_FLAG_CONNECT_EVENT); + hcibuf[0] = 0x05; + hcibuf[1] = 0x01 << 2; // HCI OGF = 1 + hcibuf[2] = 0x0D; // parameter Total Length = 13 + hcibuf[3] = bdaddr[0]; // 6 octet bdaddr (LSB) + hcibuf[4] = bdaddr[1]; + hcibuf[5] = bdaddr[2]; + hcibuf[6] = bdaddr[3]; + hcibuf[7] = bdaddr[4]; + hcibuf[8] = bdaddr[5]; + hcibuf[9] = 0x18; // DM1 or DH1 may be used + hcibuf[10] = 0xCC; // DM3, DH3, DM5, DH5 may be used + hcibuf[11] = 0x01; // Page repetition mode R1 + hcibuf[12] = 0x00; // Reserved + hcibuf[13] = 0x00; // Clock offset + hcibuf[14] = 0x00; // Invalid clock offset + hcibuf[15] = 0x00; // Do not allow role switch + + HCI_Command(hcibuf, 16); +} + +void BTD::hci_pin_code_request_reply() { + hcibuf[0] = 0x0D; // HCI OCF = 0D + hcibuf[1] = 0x01 << 2; // HCI OGF = 1 + hcibuf[2] = 0x17; // parameter length 23 + hcibuf[3] = disc_bdaddr[0]; // 6 octet bdaddr + hcibuf[4] = disc_bdaddr[1]; + hcibuf[5] = disc_bdaddr[2]; + hcibuf[6] = disc_bdaddr[3]; + hcibuf[7] = disc_bdaddr[4]; + hcibuf[8] = disc_bdaddr[5]; + if(pairWithWii) { + hcibuf[9] = 6; // Pin length is the length of the Bluetooth address + if(wiiUProController) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nParing with Wii U Pro Controller"), 0x80); +#endif + for(uint8_t i = 0; i < 6; i++) + hcibuf[10 + i] = my_bdaddr[i]; // The pin is the Bluetooth dongles Bluetooth address backwards + } else { + for(uint8_t i = 0; i < 6; i++) + hcibuf[10 + i] = disc_bdaddr[i]; // The pin is the Wiimote's Bluetooth address backwards + } + for(uint8_t i = 16; i < 26; i++) + hcibuf[i] = 0x00; // The rest should be 0 + } else { + hcibuf[9] = strlen(btdPin); // Length of pin + uint8_t i; + for(i = 0; i < strlen(btdPin); i++) // The maximum size of the pin is 16 + hcibuf[i + 10] = btdPin[i]; + for(; i < 16; i++) + hcibuf[i + 10] = 0x00; // The rest should be 0 + } + + HCI_Command(hcibuf, 26); +} + +void BTD::hci_pin_code_negative_request_reply() { + hcibuf[0] = 0x0E; // HCI OCF = 0E + hcibuf[1] = 0x01 << 2; // HCI OGF = 1 + hcibuf[2] = 0x06; // parameter length 6 + hcibuf[3] = disc_bdaddr[0]; // 6 octet bdaddr + hcibuf[4] = disc_bdaddr[1]; + hcibuf[5] = disc_bdaddr[2]; + hcibuf[6] = disc_bdaddr[3]; + hcibuf[7] = disc_bdaddr[4]; + hcibuf[8] = disc_bdaddr[5]; + + HCI_Command(hcibuf, 9); +} + +void BTD::hci_link_key_request_negative_reply() { + hcibuf[0] = 0x0C; // HCI OCF = 0C + hcibuf[1] = 0x01 << 2; // HCI OGF = 1 + hcibuf[2] = 0x06; // parameter length 6 + hcibuf[3] = disc_bdaddr[0]; // 6 octet bdaddr + hcibuf[4] = disc_bdaddr[1]; + hcibuf[5] = disc_bdaddr[2]; + hcibuf[6] = disc_bdaddr[3]; + hcibuf[7] = disc_bdaddr[4]; + hcibuf[8] = disc_bdaddr[5]; + + HCI_Command(hcibuf, 9); +} + +void BTD::hci_authentication_request() { + hcibuf[0] = 0x11; // HCI OCF = 11 + hcibuf[1] = 0x01 << 2; // HCI OGF = 1 + hcibuf[2] = 0x02; // parameter length = 2 + hcibuf[3] = (uint8_t)(hci_handle & 0xFF); //connection handle - low byte + hcibuf[4] = (uint8_t)((hci_handle >> 8) & 0x0F); //connection handle - high byte + + HCI_Command(hcibuf, 5); +} + +void BTD::hci_disconnect(uint16_t handle) { // This is called by the different services + hci_clear_flag(HCI_FLAG_DISCONNECT_COMPLETE); + hcibuf[0] = 0x06; // HCI OCF = 6 + hcibuf[1] = 0x01 << 2; // HCI OGF = 1 + hcibuf[2] = 0x03; // parameter length = 3 + hcibuf[3] = (uint8_t)(handle & 0xFF); //connection handle - low byte + hcibuf[4] = (uint8_t)((handle >> 8) & 0x0F); //connection handle - high byte + hcibuf[5] = 0x13; // reason + + HCI_Command(hcibuf, 6); +} + +void BTD::hci_write_class_of_device() { // See http://bluetooth-pentest.narod.ru/software/bluetooth_class_of_device-service_generator.html + hcibuf[0] = 0x24; // HCI OCF = 24 + hcibuf[1] = 0x03 << 2; // HCI OGF = 3 + hcibuf[2] = 0x03; // parameter length = 3 + hcibuf[3] = 0x04; // Robot + hcibuf[4] = 0x08; // Toy + hcibuf[5] = 0x00; + + HCI_Command(hcibuf, 6); +} +/******************************************************************* + * * + * HCI ACL Data Packet * + * * + * buf[0] buf[1] buf[2] buf[3] + * 0 4 8 11 12 16 24 31 MSB + * .-+-+-+-+-+-+-+-|-+-+-+-|-+-|-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-. + * | HCI Handle |PB |BC | Data Total Length | HCI ACL Data Packet + * .-+-+-+-+-+-+-+-|-+-+-+-|-+-|-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-. + * + * buf[4] buf[5] buf[6] buf[7] + * 0 8 16 31 MSB + * .-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-. + * | Length | Channel ID | Basic L2CAP header + * .-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-. + * + * buf[8] buf[9] buf[10] buf[11] + * 0 8 16 31 MSB + * .-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-. + * | Code | Identifier | Length | Control frame (C-frame) + * .-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-. (signaling packet format) + */ +/************************************************************/ +/* L2CAP Commands */ + +/************************************************************/ +void BTD::L2CAP_Command(uint16_t handle, uint8_t* data, uint8_t nbytes, uint8_t channelLow, uint8_t channelHigh) { + uint8_t buf[8 + nbytes]; + buf[0] = (uint8_t)(handle & 0xff); // HCI handle with PB,BC flag + buf[1] = (uint8_t)(((handle >> 8) & 0x0f) | 0x20); + buf[2] = (uint8_t)((4 + nbytes) & 0xff); // HCI ACL total data length + buf[3] = (uint8_t)((4 + nbytes) >> 8); + buf[4] = (uint8_t)(nbytes & 0xff); // L2CAP header: Length + buf[5] = (uint8_t)(nbytes >> 8); + buf[6] = channelLow; + buf[7] = channelHigh; + + for(uint16_t i = 0; i < nbytes; i++) // L2CAP C-frame + buf[8 + i] = data[i]; + + uint8_t rcode = pUsb->outTransfer(bAddress, epInfo[ BTD_DATAOUT_PIPE ].epAddr, (8 + nbytes), buf); + if(rcode) { + delay(100); // This small delay prevents it from overflowing if it fails +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nError sending L2CAP message: 0x"), 0x80); + D_PrintHex (rcode, 0x80); + Notify(PSTR(" - Channel ID: "), 0x80); + D_PrintHex (channelHigh, 0x80); + Notify(PSTR(" "), 0x80); + D_PrintHex (channelLow, 0x80); +#endif + } +} + +void BTD::l2cap_connection_request(uint16_t handle, uint8_t rxid, uint8_t* scid, uint16_t psm) { + l2capoutbuf[0] = L2CAP_CMD_CONNECTION_REQUEST; // Code + l2capoutbuf[1] = rxid; // Identifier + l2capoutbuf[2] = 0x04; // Length + l2capoutbuf[3] = 0x00; + l2capoutbuf[4] = (uint8_t)(psm & 0xff); // PSM + l2capoutbuf[5] = (uint8_t)(psm >> 8); + l2capoutbuf[6] = scid[0]; // Source CID + l2capoutbuf[7] = scid[1]; + + L2CAP_Command(handle, l2capoutbuf, 8); +} + +void BTD::l2cap_connection_response(uint16_t handle, uint8_t rxid, uint8_t* dcid, uint8_t* scid, uint8_t result) { + l2capoutbuf[0] = L2CAP_CMD_CONNECTION_RESPONSE; // Code + l2capoutbuf[1] = rxid; // Identifier + l2capoutbuf[2] = 0x08; // Length + l2capoutbuf[3] = 0x00; + l2capoutbuf[4] = dcid[0]; // Destination CID + l2capoutbuf[5] = dcid[1]; + l2capoutbuf[6] = scid[0]; // Source CID + l2capoutbuf[7] = scid[1]; + l2capoutbuf[8] = result; // Result: Pending or Success + l2capoutbuf[9] = 0x00; + l2capoutbuf[10] = 0x00; // No further information + l2capoutbuf[11] = 0x00; + + L2CAP_Command(handle, l2capoutbuf, 12); +} + +void BTD::l2cap_config_request(uint16_t handle, uint8_t rxid, uint8_t* dcid) { + l2capoutbuf[0] = L2CAP_CMD_CONFIG_REQUEST; // Code + l2capoutbuf[1] = rxid; // Identifier + l2capoutbuf[2] = 0x08; // Length + l2capoutbuf[3] = 0x00; + l2capoutbuf[4] = dcid[0]; // Destination CID + l2capoutbuf[5] = dcid[1]; + l2capoutbuf[6] = 0x00; // Flags + l2capoutbuf[7] = 0x00; + l2capoutbuf[8] = 0x01; // Config Opt: type = MTU (Maximum Transmission Unit) - Hint + l2capoutbuf[9] = 0x02; // Config Opt: length + l2capoutbuf[10] = 0xFF; // MTU + l2capoutbuf[11] = 0xFF; + + L2CAP_Command(handle, l2capoutbuf, 12); +} + +void BTD::l2cap_config_response(uint16_t handle, uint8_t rxid, uint8_t* scid) { + l2capoutbuf[0] = L2CAP_CMD_CONFIG_RESPONSE; // Code + l2capoutbuf[1] = rxid; // Identifier + l2capoutbuf[2] = 0x0A; // Length + l2capoutbuf[3] = 0x00; + l2capoutbuf[4] = scid[0]; // Source CID + l2capoutbuf[5] = scid[1]; + l2capoutbuf[6] = 0x00; // Flag + l2capoutbuf[7] = 0x00; + l2capoutbuf[8] = 0x00; // Result + l2capoutbuf[9] = 0x00; + l2capoutbuf[10] = 0x01; // Config + l2capoutbuf[11] = 0x02; + l2capoutbuf[12] = 0xA0; + l2capoutbuf[13] = 0x02; + + L2CAP_Command(handle, l2capoutbuf, 14); +} + +void BTD::l2cap_disconnection_request(uint16_t handle, uint8_t rxid, uint8_t* dcid, uint8_t* scid) { + l2capoutbuf[0] = L2CAP_CMD_DISCONNECT_REQUEST; // Code + l2capoutbuf[1] = rxid; // Identifier + l2capoutbuf[2] = 0x04; // Length + l2capoutbuf[3] = 0x00; + l2capoutbuf[4] = dcid[0]; + l2capoutbuf[5] = dcid[1]; + l2capoutbuf[6] = scid[0]; + l2capoutbuf[7] = scid[1]; + + L2CAP_Command(handle, l2capoutbuf, 8); +} + +void BTD::l2cap_disconnection_response(uint16_t handle, uint8_t rxid, uint8_t* dcid, uint8_t* scid) { + l2capoutbuf[0] = L2CAP_CMD_DISCONNECT_RESPONSE; // Code + l2capoutbuf[1] = rxid; // Identifier + l2capoutbuf[2] = 0x04; // Length + l2capoutbuf[3] = 0x00; + l2capoutbuf[4] = dcid[0]; + l2capoutbuf[5] = dcid[1]; + l2capoutbuf[6] = scid[0]; + l2capoutbuf[7] = scid[1]; + + L2CAP_Command(handle, l2capoutbuf, 8); +} + +void BTD::l2cap_information_response(uint16_t handle, uint8_t rxid, uint8_t infoTypeLow, uint8_t infoTypeHigh) { + l2capoutbuf[0] = L2CAP_CMD_INFORMATION_RESPONSE; // Code + l2capoutbuf[1] = rxid; // Identifier + l2capoutbuf[2] = 0x08; // Length + l2capoutbuf[3] = 0x00; + l2capoutbuf[4] = infoTypeLow; + l2capoutbuf[5] = infoTypeHigh; + l2capoutbuf[6] = 0x00; // Result = success + l2capoutbuf[7] = 0x00; // Result = success + l2capoutbuf[8] = 0x00; + l2capoutbuf[9] = 0x00; + l2capoutbuf[10] = 0x00; + l2capoutbuf[11] = 0x00; + + L2CAP_Command(handle, l2capoutbuf, 12); +} + +/* PS3 Commands - only set Bluetooth address is implemented in this library */ +void BTD::setBdaddr(uint8_t* bdaddr) { + /* Set the internal Bluetooth address */ + uint8_t buf[8]; + buf[0] = 0x01; + buf[1] = 0x00; + + for(uint8_t i = 0; i < 6; i++) + buf[i + 2] = bdaddr[5 - i]; // Copy into buffer, has to be written reversed, so it is MSB first + + // bmRequest = Host to device (0x00) | Class (0x20) | Interface (0x01) = 0x21, bRequest = Set Report (0x09), Report ID (0xF5), Report Type (Feature 0x03), interface (0x00), datalength, datalength, data + pUsb->ctrlReq(bAddress, epInfo[BTD_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0xF5, 0x03, 0x00, 8, 8, buf, NULL); +} + +void BTD::setMoveBdaddr(uint8_t* bdaddr) { + /* Set the internal Bluetooth address */ + uint8_t buf[11]; + buf[0] = 0x05; + buf[7] = 0x10; + buf[8] = 0x01; + buf[9] = 0x02; + buf[10] = 0x12; + + for(uint8_t i = 0; i < 6; i++) + buf[i + 1] = bdaddr[i]; + + // bmRequest = Host to device (0x00) | Class (0x20) | Interface (0x01) = 0x21, bRequest = Set Report (0x09), Report ID (0x05), Report Type (Feature 0x03), interface (0x00), datalength, datalength, data + pUsb->ctrlReq(bAddress, epInfo[BTD_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0x05, 0x03, 0x00, 11, 11, buf, NULL); +} diff --git a/libraries/USB_Host_Shield/BTD.h b/libraries/USB_Host_Shield/BTD.h new file mode 100755 index 0000000..b7683f6 --- /dev/null +++ b/libraries/USB_Host_Shield/BTD.h @@ -0,0 +1,618 @@ +/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved. + + This software may be distributed and modified under the terms of the GNU + General Public License version 2 (GPL2) as published by the Free Software + Foundation and appearing in the file GPL2.TXT included in the packaging of + this file. Please note that GPL2 Section 2[b] requires that all works based + on this software must also be made publicly available under the terms of + the GPL2 ("Copyleft"). + + Contact information + ------------------- + + Kristian Lauszus, TKJ Electronics + Web : http://www.tkjelectronics.com + e-mail : kristianl@tkjelectronics.com + */ + +#ifndef _btd_h_ +#define _btd_h_ + +#include "Usb.h" +#include "hid.h" + +//PID and VID of the Sony PS3 devices +#define PS3_VID 0x054C // Sony Corporation +#define PS3_PID 0x0268 // PS3 Controller DualShock 3 +#define PS3NAVIGATION_PID 0x042F // Navigation controller +#define PS3MOVE_PID 0x03D5 // Motion controller + +#define IOGEAR_GBU521_VID 0x0A5C // The IOGEAR GBU521 dongle does not presents itself correctly, so we have to check for it manually +#define IOGEAR_GBU521_PID 0x21E8 + +/* Bluetooth dongle data taken from descriptors */ +#define BULK_MAXPKTSIZE 64 // Max size for ACL data + +// Used in control endpoint header for HCI Commands +#define bmREQ_HCI_OUT USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_DEVICE + +/* Bluetooth HCI states for hci_task() */ +#define HCI_INIT_STATE 0 +#define HCI_RESET_STATE 1 +#define HCI_CLASS_STATE 2 +#define HCI_BDADDR_STATE 3 +#define HCI_LOCAL_VERSION_STATE 4 +#define HCI_SET_NAME_STATE 5 +#define HCI_CHECK_DEVICE_SERVICE 6 + +#define HCI_INQUIRY_STATE 7 // These three states are only used if it should pair and connect to a device +#define HCI_CONNECT_DEVICE_STATE 8 +#define HCI_CONNECTED_DEVICE_STATE 9 + +#define HCI_SCANNING_STATE 10 +#define HCI_CONNECT_IN_STATE 11 +#define HCI_REMOTE_NAME_STATE 12 +#define HCI_CONNECTED_STATE 13 +#define HCI_DISABLE_SCAN_STATE 14 +#define HCI_DONE_STATE 15 +#define HCI_DISCONNECT_STATE 16 + +/* HCI event flags*/ +#define HCI_FLAG_CMD_COMPLETE (1UL << 0) +#define HCI_FLAG_CONNECT_COMPLETE (1UL << 1) +#define HCI_FLAG_DISCONNECT_COMPLETE (1UL << 2) +#define HCI_FLAG_REMOTE_NAME_COMPLETE (1UL << 3) +#define HCI_FLAG_INCOMING_REQUEST (1UL << 4) +#define HCI_FLAG_READ_BDADDR (1UL << 5) +#define HCI_FLAG_READ_VERSION (1UL << 6) +#define HCI_FLAG_DEVICE_FOUND (1UL << 7) +#define HCI_FLAG_CONNECT_EVENT (1UL << 8) + +/* Macros for HCI event flag tests */ +#define hci_check_flag(flag) (hci_event_flag & (flag)) +#define hci_set_flag(flag) (hci_event_flag |= (flag)) +#define hci_clear_flag(flag) (hci_event_flag &= ~(flag)) + +/* HCI Events managed */ +#define EV_INQUIRY_COMPLETE 0x01 +#define EV_INQUIRY_RESULT 0x02 +#define EV_CONNECT_COMPLETE 0x03 +#define EV_INCOMING_CONNECT 0x04 +#define EV_DISCONNECT_COMPLETE 0x05 +#define EV_AUTHENTICATION_COMPLETE 0x06 +#define EV_REMOTE_NAME_COMPLETE 0x07 +#define EV_ENCRYPTION_CHANGE 0x08 +#define EV_CHANGE_CONNECTION_LINK 0x09 +#define EV_ROLE_CHANGED 0x12 +#define EV_NUM_COMPLETE_PKT 0x13 +#define EV_PIN_CODE_REQUEST 0x16 +#define EV_LINK_KEY_REQUEST 0x17 +#define EV_LINK_KEY_NOTIFICATION 0x18 +#define EV_DATA_BUFFER_OVERFLOW 0x1A +#define EV_MAX_SLOTS_CHANGE 0x1B +#define EV_READ_REMOTE_VERSION_INFORMATION_COMPLETE 0x0C +#define EV_QOS_SETUP_COMPLETE 0x0D +#define EV_COMMAND_COMPLETE 0x0E +#define EV_COMMAND_STATUS 0x0F +#define EV_LOOPBACK_COMMAND 0x19 +#define EV_PAGE_SCAN_REP_MODE 0x20 + +/* Bluetooth states for the different Bluetooth drivers */ +#define L2CAP_WAIT 0 +#define L2CAP_DONE 1 + +/* Used for HID Control channel */ +#define L2CAP_CONTROL_CONNECT_REQUEST 2 +#define L2CAP_CONTROL_CONFIG_REQUEST 3 +#define L2CAP_CONTROL_SUCCESS 4 +#define L2CAP_CONTROL_DISCONNECT 5 + +/* Used for HID Interrupt channel */ +#define L2CAP_INTERRUPT_SETUP 6 +#define L2CAP_INTERRUPT_CONNECT_REQUEST 7 +#define L2CAP_INTERRUPT_CONFIG_REQUEST 8 +#define L2CAP_INTERRUPT_DISCONNECT 9 + +/* Used for SDP channel */ +#define L2CAP_SDP_WAIT 10 +#define L2CAP_SDP_SUCCESS 11 + +/* Used for RFCOMM channel */ +#define L2CAP_RFCOMM_WAIT 12 +#define L2CAP_RFCOMM_SUCCESS 13 + +#define L2CAP_DISCONNECT_RESPONSE 14 // Used for both SDP and RFCOMM channel + +/* Bluetooth states used by some drivers */ +#define TURN_ON_LED 17 +#define PS3_ENABLE_SIXAXIS 18 +#define WII_CHECK_MOTION_PLUS_STATE 19 +#define WII_CHECK_EXTENSION_STATE 20 +#define WII_INIT_MOTION_PLUS_STATE 21 + +/* L2CAP event flags for HID Control channel */ +#define L2CAP_FLAG_CONNECTION_CONTROL_REQUEST (1UL << 0) +#define L2CAP_FLAG_CONFIG_CONTROL_SUCCESS (1UL << 1) +#define L2CAP_FLAG_CONTROL_CONNECTED (1UL << 2) +#define L2CAP_FLAG_DISCONNECT_CONTROL_RESPONSE (1UL << 3) + +/* L2CAP event flags for HID Interrupt channel */ +#define L2CAP_FLAG_CONNECTION_INTERRUPT_REQUEST (1UL << 4) +#define L2CAP_FLAG_CONFIG_INTERRUPT_SUCCESS (1UL << 5) +#define L2CAP_FLAG_INTERRUPT_CONNECTED (1UL << 6) +#define L2CAP_FLAG_DISCONNECT_INTERRUPT_RESPONSE (1UL << 7) + +/* L2CAP event flags for SDP channel */ +#define L2CAP_FLAG_CONNECTION_SDP_REQUEST (1UL << 8) +#define L2CAP_FLAG_CONFIG_SDP_SUCCESS (1UL << 9) +#define L2CAP_FLAG_DISCONNECT_SDP_REQUEST (1UL << 10) + +/* L2CAP event flags for RFCOMM channel */ +#define L2CAP_FLAG_CONNECTION_RFCOMM_REQUEST (1UL << 11) +#define L2CAP_FLAG_CONFIG_RFCOMM_SUCCESS (1UL << 12) +#define L2CAP_FLAG_DISCONNECT_RFCOMM_REQUEST (1UL << 13) + +#define L2CAP_FLAG_DISCONNECT_RESPONSE (1UL << 14) + +/* Macros for L2CAP event flag tests */ +#define l2cap_check_flag(flag) (l2cap_event_flag & (flag)) +#define l2cap_set_flag(flag) (l2cap_event_flag |= (flag)) +#define l2cap_clear_flag(flag) (l2cap_event_flag &= ~(flag)) + +/* L2CAP signaling commands */ +#define L2CAP_CMD_COMMAND_REJECT 0x01 +#define L2CAP_CMD_CONNECTION_REQUEST 0x02 +#define L2CAP_CMD_CONNECTION_RESPONSE 0x03 +#define L2CAP_CMD_CONFIG_REQUEST 0x04 +#define L2CAP_CMD_CONFIG_RESPONSE 0x05 +#define L2CAP_CMD_DISCONNECT_REQUEST 0x06 +#define L2CAP_CMD_DISCONNECT_RESPONSE 0x07 +#define L2CAP_CMD_INFORMATION_REQUEST 0x0A +#define L2CAP_CMD_INFORMATION_RESPONSE 0x0B + +// Used For Connection Response - Remember to Include High Byte +#define PENDING 0x01 +#define SUCCESSFUL 0x00 + +/* Bluetooth L2CAP PSM - see http://www.bluetooth.org/Technical/AssignedNumbers/logical_link.htm */ +#define SDP_PSM 0x01 // Service Discovery Protocol PSM Value +#define RFCOMM_PSM 0x03 // RFCOMM PSM Value +#define HID_CTRL_PSM 0x11 // HID_Control PSM Value +#define HID_INTR_PSM 0x13 // HID_Interrupt PSM Value + +// Used to determine if it is a Bluetooth dongle +#define WI_SUBCLASS_RF 0x01 // RF Controller +#define WI_PROTOCOL_BT 0x01 // Bluetooth Programming Interface + +#define BTD_MAX_ENDPOINTS 4 +#define BTD_NUM_SERVICES 4 // Max number of Bluetooth services - if you need more than 4 simply increase this number + +#define PAIR 1 + +class BluetoothService; + +/** + * The Bluetooth Dongle class will take care of all the USB communication + * and then pass the data to the BluetoothService classes. + */ +class BTD : public USBDeviceConfig, public UsbConfigXtracter { +public: + /** + * Constructor for the BTD class. + * @param p Pointer to USB class instance. + */ + BTD(USB *p); + + /** @name USBDeviceConfig implementation */ + /** + * Address assignment and basic initialization is done here. + * @param parent Hub number. + * @param port Port number on the hub. + * @param lowspeed Speed of the device. + * @return 0 on success. + */ + uint8_t ConfigureDevice(uint8_t parent, uint8_t port, bool lowspeed); + /** + * Initialize the Bluetooth dongle. + * @param parent Hub number. + * @param port Port number on the hub. + * @param lowspeed Speed of the device. + * @return 0 on success. + */ + uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed); + /** + * Release the USB device. + * @return 0 on success. + */ + uint8_t Release(); + /** + * Poll the USB Input endpoints and run the state machines. + * @return 0 on success. + */ + uint8_t Poll(); + + /** + * Get the device address. + * @return The device address. + */ + virtual uint8_t GetAddress() { + return bAddress; + }; + + /** + * Used to check if the dongle has been initialized. + * @return True if it's ready. + */ + virtual bool isReady() { + return bPollEnable; + }; + + /** + * Used by the USB core to check what this driver support. + * @param klass The device's USB class. + * @return Returns true if the device's USB class matches this driver. + */ + virtual bool DEVCLASSOK(uint8_t klass) { + return (klass == USB_CLASS_WIRELESS_CTRL); + }; + + /** + * Used by the USB core to check what this driver support. + * Used to set the Bluetooth address into the PS3 controllers. + * @param vid The device's VID. + * @param pid The device's PID. + * @return Returns true if the device's VID and PID matches this driver. + */ + virtual bool VIDPIDOK(uint16_t vid, uint16_t pid) { + if(vid == IOGEAR_GBU521_VID && pid == IOGEAR_GBU521_PID) + return true; + if(my_bdaddr[0] != 0x00 || my_bdaddr[1] != 0x00 || my_bdaddr[2] != 0x00 || my_bdaddr[3] != 0x00 || my_bdaddr[4] != 0x00 || my_bdaddr[5] != 0x00) { // Check if Bluetooth address is set + if(vid == PS3_VID && (pid == PS3_PID || pid == PS3NAVIGATION_PID || pid == PS3MOVE_PID)) + return true; + } + return false; + }; + /**@}*/ + + /** @name UsbConfigXtracter implementation */ + /** + * UsbConfigXtracter implementation, used to extract endpoint information. + * @param conf Configuration value. + * @param iface Interface number. + * @param alt Alternate setting. + * @param proto Interface Protocol. + * @param ep Endpoint Descriptor. + */ + void EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *ep); + /**@}*/ + + /** Disconnects both the L2CAP Channel and the HCI Connection for all Bluetooth services. */ + void disconnect(); + + /** + * Register Bluetooth dongle members/services. + * @param pService Pointer to BluetoothService class instance. + * @return The service ID on success or -1 on fail. + */ + int8_t registerBluetoothService(BluetoothService *pService) { + for(uint8_t i = 0; i < BTD_NUM_SERVICES; i++) { + if(!btService[i]) { + btService[i] = pService; + return i; // Return ID + } + } + return -1; // Error registering BluetoothService + }; + + /** @name HCI Commands */ + /** + * Used to send a HCI Command. + * @param data Data to send. + * @param nbytes Number of bytes to send. + */ + void HCI_Command(uint8_t* data, uint16_t nbytes); + /** Reset the Bluetooth dongle. */ + void hci_reset(); + /** Read the Bluetooth address of the dongle. */ + void hci_read_bdaddr(); + /** Read the HCI Version of the Bluetooth dongle. */ + void hci_read_local_version_information(); + /** + * Set the local name of the Bluetooth dongle. + * @param name Desired name. + */ + void hci_set_local_name(const char* name); + /** Enable visibility to other Bluetooth devices. */ + void hci_write_scan_enable(); + /** Disable visibility to other Bluetooth devices. */ + void hci_write_scan_disable(); + /** Read the remote devices name. */ + void hci_remote_name(); + /** Accept the connection with the Bluetooth device. */ + void hci_accept_connection(); + /** + * Disconnect the HCI connection. + * @param handle The HCI Handle for the connection. + */ + void hci_disconnect(uint16_t handle); + /** + * Respond with the pin for the connection. + * The pin is automatically set for the Wii library, + * but can be customized for the SPP library. + */ + void hci_pin_code_request_reply(); + /** Respons when no pin was set. */ + void hci_pin_code_negative_request_reply(); + /** + * Command is used to reply to a Link Key Request event from the BR/EDR Controller + * if the Host does not have a stored Link Key for the connection. + */ + void hci_link_key_request_negative_reply(); + /** Used to try to authenticate with the remote device. */ + void hci_authentication_request(); + /** Start a HCI inquiry. */ + void hci_inquiry(); + /** Cancel a HCI inquiry. */ + void hci_inquiry_cancel(); + /** Connect to last device communicated with. */ + void hci_connect(); + /** + * Connect to device. + * @param bdaddr Bluetooth address of the device. + */ + void hci_connect(uint8_t *bdaddr); + /** Used to a set the class of the device. */ + void hci_write_class_of_device(); + /**@}*/ + + /** @name L2CAP Commands */ + /** + * Used to send L2CAP Commands. + * @param handle HCI Handle. + * @param data Data to send. + * @param nbytes Number of bytes to send. + * @param channelLow,channelHigh Low and high byte of channel to send to. + * If argument is omitted then the Standard L2CAP header: Channel ID (0x01) for ACL-U will be used. + */ + void L2CAP_Command(uint16_t handle, uint8_t* data, uint8_t nbytes, uint8_t channelLow = 0x01, uint8_t channelHigh = 0x00); + /** + * L2CAP Connection Request. + * @param handle HCI handle. + * @param rxid Identifier. + * @param scid Source Channel Identifier. + * @param psm Protocol/Service Multiplexer - see: https://www.bluetooth.org/Technical/AssignedNumbers/logical_link.htm. + */ + void l2cap_connection_request(uint16_t handle, uint8_t rxid, uint8_t* scid, uint16_t psm); + /** + * L2CAP Connection Response. + * @param handle HCI handle. + * @param rxid Identifier. + * @param dcid Destination Channel Identifier. + * @param scid Source Channel Identifier. + * @param result Result - First send ::PENDING and then ::SUCCESSFUL. + */ + void l2cap_connection_response(uint16_t handle, uint8_t rxid, uint8_t* dcid, uint8_t* scid, uint8_t result); + /** + * L2CAP Config Request. + * @param handle HCI Handle. + * @param rxid Identifier. + * @param dcid Destination Channel Identifier. + */ + void l2cap_config_request(uint16_t handle, uint8_t rxid, uint8_t* dcid); + /** + * L2CAP Config Response. + * @param handle HCI Handle. + * @param rxid Identifier. + * @param scid Source Channel Identifier. + */ + void l2cap_config_response(uint16_t handle, uint8_t rxid, uint8_t* scid); + /** + * L2CAP Disconnection Request. + * @param handle HCI Handle. + * @param rxid Identifier. + * @param dcid Device Channel Identifier. + * @param scid Source Channel Identifier. + */ + void l2cap_disconnection_request(uint16_t handle, uint8_t rxid, uint8_t* dcid, uint8_t* scid); + /** + * L2CAP Disconnection Response. + * @param handle HCI Handle. + * @param rxid Identifier. + * @param dcid Device Channel Identifier. + * @param scid Source Channel Identifier. + */ + void l2cap_disconnection_response(uint16_t handle, uint8_t rxid, uint8_t* dcid, uint8_t* scid); + /** + * L2CAP Information Response. + * @param handle HCI Handle. + * @param rxid Identifier. + * @param infoTypeLow,infoTypeHigh Infotype. + */ + void l2cap_information_response(uint16_t handle, uint8_t rxid, uint8_t infoTypeLow, uint8_t infoTypeHigh); + /**@}*/ + + /** Use this to see if it is waiting for a incoming connection. */ + bool watingForConnection; + /** This is used by the service to know when to store the device information. */ + bool l2capConnectionClaimed; + /** This is used by the SPP library to claim the current SDP incoming request. */ + bool sdpConnectionClaimed; + /** This is used by the SPP library to claim the current RFCOMM incoming request. */ + bool rfcommConnectionClaimed; + + /** The name you wish to make the dongle show up as. It is set automatically by the SPP library. */ + const char* btdName; + /** The pin you wish to make the dongle use for authentication. It is set automatically by the SPP and BTHID library. */ + const char* btdPin; + + /** The bluetooth dongles Bluetooth address. */ + uint8_t my_bdaddr[6]; + /** HCI handle for the last connection. */ + uint16_t hci_handle; + /** Last incoming devices Bluetooth address. */ + uint8_t disc_bdaddr[6]; + /** First 30 chars of last remote name. */ + char remote_name[30]; + /** + * The supported HCI Version read from the Bluetooth dongle. + * Used by the PS3BT library to check the HCI Version of the Bluetooth dongle, + * it should be at least 3 to work properly with the library. + */ + uint8_t hci_version; + + /** Call this function to pair with a Wiimote */ + void pairWithWiimote() { + pairWithWii = true; + hci_state = HCI_CHECK_DEVICE_SERVICE; + }; + /** Used to only send the ACL data to the Wiimote. */ + bool connectToWii; + /** True if a Wiimote is connecting. */ + bool incomingWii; + /** True when it should pair with a Wiimote. */ + bool pairWithWii; + /** True if it's the new Wiimote with the Motion Plus Inside or a Wii U Pro Controller. */ + bool motionPlusInside; + /** True if it's a Wii U Pro Controller. */ + bool wiiUProController; + + /** Call this function to pair with a Wiimote */ + void pairWithHID() { + pairWithHIDDevice = true; + hci_state = HCI_CHECK_DEVICE_SERVICE; + }; + /** Used to only send the ACL data to the Wiimote. */ + bool connectToHIDDevice; + /** True if a Wiimote is connecting. */ + bool incomingHIDDevice; + /** True when it should pair with a device like a mouse or keyboard. */ + bool pairWithHIDDevice; + + /** + * Read the poll interval taken from the endpoint descriptors. + * @return The poll interval in ms. + */ + uint8_t readPollInterval() { + return pollInterval; + }; + +protected: + /** Pointer to USB class instance. */ + USB *pUsb; + /** Device address. */ + uint8_t bAddress; + /** Endpoint info structure. */ + EpInfo epInfo[BTD_MAX_ENDPOINTS]; + + /** Configuration number. */ + uint8_t bConfNum; + /** Total number of endpoints in the configuration. */ + uint8_t bNumEP; + /** Next poll time based on poll interval taken from the USB descriptor. */ + uint32_t qNextPollTime; + + /** Bluetooth dongle control endpoint. */ + static const uint8_t BTD_CONTROL_PIPE; + /** HCI event endpoint index. */ + static const uint8_t BTD_EVENT_PIPE; + /** ACL In endpoint index. */ + static const uint8_t BTD_DATAIN_PIPE; + /** ACL Out endpoint index. */ + static const uint8_t BTD_DATAOUT_PIPE; + + /** + * Used to print the USB Endpoint Descriptor. + * @param ep_ptr Pointer to USB Endpoint Descriptor. + */ + void PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr); + +private: + void Initialize(); // Set all variables, endpoint structs etc. to default values + BluetoothService *btService[BTD_NUM_SERVICES]; + + uint16_t PID, VID; // PID and VID of device connected + + uint8_t pollInterval; + bool bPollEnable; + + bool incomingPS4; // True if a PS4 controller is connecting + uint8_t classOfDevice[3]; // Class of device of last device + + /* Variables used by high level HCI task */ + uint8_t hci_state; // Current state of Bluetooth HCI connection + uint16_t hci_counter; // Counter used for Bluetooth HCI reset loops + uint16_t hci_num_reset_loops; // This value indicate how many times it should read before trying to reset + uint16_t hci_event_flag; // HCI flags of received Bluetooth events + uint8_t inquiry_counter; + + uint8_t hcibuf[BULK_MAXPKTSIZE]; // General purpose buffer for HCI data + uint8_t l2capinbuf[BULK_MAXPKTSIZE]; // General purpose buffer for L2CAP in data + uint8_t l2capoutbuf[14]; // General purpose buffer for L2CAP out data + + /* State machines */ + void HCI_event_task(); // Poll the HCI event pipe + void HCI_task(); // HCI state machine + void ACL_event_task(); // ACL input pipe + + /* Used to set the Bluetooth Address internally to the PS3 Controllers */ + void setBdaddr(uint8_t* BDADDR); + void setMoveBdaddr(uint8_t* BDADDR); +}; + +/** All Bluetooth services should inherit this class. */ +class BluetoothService { +public: + BluetoothService(BTD *p) : pBtd(p) { + if(pBtd) + pBtd->registerBluetoothService(this); // Register it as a Bluetooth service + }; + /** + * Used to pass acldata to the Bluetooth service. + * @param ACLData Pointer to the incoming acldata. + */ + virtual void ACLData(uint8_t* ACLData) = 0; + /** Used to run the different state machines in the Bluetooth service. */ + virtual void Run() = 0; + /** Used to reset the Bluetooth service. */ + virtual void Reset() = 0; + /** Used to disconnect both the L2CAP Channel and the HCI Connection for the Bluetooth service. */ + virtual void disconnect() = 0; + + /** + * Used to call your own function when the device is successfully initialized. + * @param funcOnInit Function to call. + */ + void attachOnInit(void (*funcOnInit)(void)) { + pFuncOnInit = funcOnInit; // TODO: This really belong in a class of it's own as it is repeated several times + }; + +protected: + /** + * Called when a device is successfully initialized. + * Use attachOnInit(void (*funcOnInit)(void)) to call your own function. + * This is useful for instance if you want to set the LEDs in a specific way. + */ + virtual void onInit() = 0; + + /** Used to check if the incoming L2CAP data matches the HCI Handle */ + bool checkHciHandle(uint8_t *buf, uint16_t handle) { + return (buf[0] == (handle & 0xFF)) && (buf[1] == ((handle >> 8) | 0x20)); + } + + /** Pointer to function called in onInit(). */ + void (*pFuncOnInit)(void); + + /** Pointer to BTD instance. */ + BTD *pBtd; + + /** The HCI Handle for the connection. */ + uint16_t hci_handle; + + /** L2CAP flags of received Bluetooth events. */ + uint32_t l2cap_event_flag; + + /** Identifier for L2CAP commands. */ + uint8_t identifier; +}; + +#endif diff --git a/libraries/USB_Host_Shield/BTHID.cpp b/libraries/USB_Host_Shield/BTHID.cpp new file mode 100755 index 0000000..bfa9202 --- /dev/null +++ b/libraries/USB_Host_Shield/BTHID.cpp @@ -0,0 +1,399 @@ +/* Copyright (C) 2013 Kristian Lauszus, TKJ Electronics. All rights reserved. + + This software may be distributed and modified under the terms of the GNU + General Public License version 2 (GPL2) as published by the Free Software + Foundation and appearing in the file GPL2.TXT included in the packaging of + this file. Please note that GPL2 Section 2[b] requires that all works based + on this software must also be made publicly available under the terms of + the GPL2 ("Copyleft"). + + Contact information + ------------------- + + Kristian Lauszus, TKJ Electronics + Web : http://www.tkjelectronics.com + e-mail : kristianl@tkjelectronics.com + */ + +#include "BTHID.h" +// To enable serial debugging see "settings.h" +//#define EXTRADEBUG // Uncomment to get even more debugging data +//#define PRINTREPORT // Uncomment to print the report send by the HID device + +BTHID::BTHID(BTD *p, bool pair, const char *pin) : +BluetoothService(p), // Pointer to USB class instance - mandatory +protocolMode(HID_BOOT_PROTOCOL) { + for(uint8_t i = 0; i < NUM_PARSERS; i++) + pRptParser[i] = NULL; + + pBtd->pairWithHIDDevice = pair; + pBtd->btdPin = pin; + + /* Set device cid for the control and intterrupt channelse - LSB */ + control_dcid[0] = 0x70; // 0x0070 + control_dcid[1] = 0x00; + interrupt_dcid[0] = 0x71; // 0x0071 + interrupt_dcid[1] = 0x00; + + Reset(); +} + +void BTHID::Reset() { + connected = false; + activeConnection = false; + l2cap_event_flag = 0; // Reset flags + l2cap_state = L2CAP_WAIT; + ResetBTHID(); +} + +void BTHID::disconnect() { // Use this void to disconnect the device + // First the HID interrupt channel has to be disconnected, then the HID control channel and finally the HCI connection + pBtd->l2cap_disconnection_request(hci_handle, ++identifier, interrupt_scid, interrupt_dcid); + Reset(); + l2cap_state = L2CAP_INTERRUPT_DISCONNECT; +} + +void BTHID::ACLData(uint8_t* l2capinbuf) { + if(!pBtd->l2capConnectionClaimed && pBtd->incomingHIDDevice && !connected && !activeConnection) { + if(l2capinbuf[8] == L2CAP_CMD_CONNECTION_REQUEST) { + if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == HID_CTRL_PSM) { + pBtd->incomingHIDDevice = false; + pBtd->l2capConnectionClaimed = true; // Claim that the incoming connection belongs to this service + activeConnection = true; + hci_handle = pBtd->hci_handle; // Store the HCI Handle for the connection + l2cap_state = L2CAP_WAIT; + } + } + } + + if(checkHciHandle(l2capinbuf, hci_handle)) { // acl_handle_ok + if((l2capinbuf[6] | (l2capinbuf[7] << 8)) == 0x0001U) { // l2cap_control - Channel ID for ACL-U + if(l2capinbuf[8] == L2CAP_CMD_COMMAND_REJECT) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nL2CAP Command Rejected - Reason: "), 0x80); + D_PrintHex (l2capinbuf[13], 0x80); + Notify(PSTR(" "), 0x80); + D_PrintHex (l2capinbuf[12], 0x80); + Notify(PSTR(" "), 0x80); + D_PrintHex (l2capinbuf[17], 0x80); + Notify(PSTR(" "), 0x80); + D_PrintHex (l2capinbuf[16], 0x80); + Notify(PSTR(" "), 0x80); + D_PrintHex (l2capinbuf[15], 0x80); + Notify(PSTR(" "), 0x80); + D_PrintHex (l2capinbuf[14], 0x80); +#endif + } else if(l2capinbuf[8] == L2CAP_CMD_CONNECTION_RESPONSE) { + if(((l2capinbuf[16] | (l2capinbuf[17] << 8)) == 0x0000) && ((l2capinbuf[18] | (l2capinbuf[19] << 8)) == SUCCESSFUL)) { // Success + if(l2capinbuf[14] == control_dcid[0] && l2capinbuf[15] == control_dcid[1]) { + //Notify(PSTR("\r\nHID Control Connection Complete"), 0x80); + identifier = l2capinbuf[9]; + control_scid[0] = l2capinbuf[12]; + control_scid[1] = l2capinbuf[13]; + l2cap_set_flag(L2CAP_FLAG_CONTROL_CONNECTED); + } else if(l2capinbuf[14] == interrupt_dcid[0] && l2capinbuf[15] == interrupt_dcid[1]) { + //Notify(PSTR("\r\nHID Interrupt Connection Complete"), 0x80); + identifier = l2capinbuf[9]; + interrupt_scid[0] = l2capinbuf[12]; + interrupt_scid[1] = l2capinbuf[13]; + l2cap_set_flag(L2CAP_FLAG_INTERRUPT_CONNECTED); + } + } + } else if(l2capinbuf[8] == L2CAP_CMD_CONNECTION_REQUEST) { +#ifdef EXTRADEBUG + Notify(PSTR("\r\nL2CAP Connection Request - PSM: "), 0x80); + D_PrintHex (l2capinbuf[13], 0x80); + Notify(PSTR(" "), 0x80); + D_PrintHex (l2capinbuf[12], 0x80); + Notify(PSTR(" SCID: "), 0x80); + D_PrintHex (l2capinbuf[15], 0x80); + Notify(PSTR(" "), 0x80); + D_PrintHex (l2capinbuf[14], 0x80); + Notify(PSTR(" Identifier: "), 0x80); + D_PrintHex (l2capinbuf[9], 0x80); +#endif + if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == HID_CTRL_PSM) { + identifier = l2capinbuf[9]; + control_scid[0] = l2capinbuf[14]; + control_scid[1] = l2capinbuf[15]; + l2cap_set_flag(L2CAP_FLAG_CONNECTION_CONTROL_REQUEST); + } else if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == HID_INTR_PSM) { + identifier = l2capinbuf[9]; + interrupt_scid[0] = l2capinbuf[14]; + interrupt_scid[1] = l2capinbuf[15]; + l2cap_set_flag(L2CAP_FLAG_CONNECTION_INTERRUPT_REQUEST); + } + } else if(l2capinbuf[8] == L2CAP_CMD_CONFIG_RESPONSE) { + if((l2capinbuf[16] | (l2capinbuf[17] << 8)) == 0x0000) { // Success + if(l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) { + //Notify(PSTR("\r\nHID Control Configuration Complete"), 0x80); + identifier = l2capinbuf[9]; + l2cap_set_flag(L2CAP_FLAG_CONFIG_CONTROL_SUCCESS); + } else if(l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) { + //Notify(PSTR("\r\nHID Interrupt Configuration Complete"), 0x80); + identifier = l2capinbuf[9]; + l2cap_set_flag(L2CAP_FLAG_CONFIG_INTERRUPT_SUCCESS); + } + } + } else if(l2capinbuf[8] == L2CAP_CMD_CONFIG_REQUEST) { + if(l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) { + //Notify(PSTR("\r\nHID Control Configuration Request"), 0x80); + pBtd->l2cap_config_response(hci_handle, l2capinbuf[9], control_scid); + } else if(l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) { + //Notify(PSTR("\r\nHID Interrupt Configuration Request"), 0x80); + pBtd->l2cap_config_response(hci_handle, l2capinbuf[9], interrupt_scid); + } + } else if(l2capinbuf[8] == L2CAP_CMD_DISCONNECT_REQUEST) { + if(l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nDisconnect Request: Control Channel"), 0x80); +#endif + identifier = l2capinbuf[9]; + pBtd->l2cap_disconnection_response(hci_handle, identifier, control_dcid, control_scid); + Reset(); + } else if(l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nDisconnect Request: Interrupt Channel"), 0x80); +#endif + identifier = l2capinbuf[9]; + pBtd->l2cap_disconnection_response(hci_handle, identifier, interrupt_dcid, interrupt_scid); + Reset(); + } + } else if(l2capinbuf[8] == L2CAP_CMD_DISCONNECT_RESPONSE) { + if(l2capinbuf[12] == control_scid[0] && l2capinbuf[13] == control_scid[1]) { + //Notify(PSTR("\r\nDisconnect Response: Control Channel"), 0x80); + identifier = l2capinbuf[9]; + l2cap_set_flag(L2CAP_FLAG_DISCONNECT_CONTROL_RESPONSE); + } else if(l2capinbuf[12] == interrupt_scid[0] && l2capinbuf[13] == interrupt_scid[1]) { + //Notify(PSTR("\r\nDisconnect Response: Interrupt Channel"), 0x80); + identifier = l2capinbuf[9]; + l2cap_set_flag(L2CAP_FLAG_DISCONNECT_INTERRUPT_RESPONSE); + } + } +#ifdef EXTRADEBUG + else { + identifier = l2capinbuf[9]; + Notify(PSTR("\r\nL2CAP Unknown Signaling Command: "), 0x80); + D_PrintHex (l2capinbuf[8], 0x80); + } +#endif + } else if(l2capinbuf[6] == interrupt_dcid[0] && l2capinbuf[7] == interrupt_dcid[1]) { // l2cap_interrupt +#ifdef PRINTREPORT + Notify(PSTR("\r\nL2CAP Interrupt: "), 0x80); + for(uint16_t i = 0; i < ((uint16_t)l2capinbuf[5] << 8 | l2capinbuf[4]); i++) { + D_PrintHex (l2capinbuf[i + 8], 0x80); + Notify(PSTR(" "), 0x80); + } +#endif + if(l2capinbuf[8] == 0xA1) { // HID_THDR_DATA_INPUT + uint16_t length = ((uint16_t)l2capinbuf[5] << 8 | l2capinbuf[4]); + ParseBTHIDData((uint8_t)(length - 1), &l2capinbuf[9]); + + switch(l2capinbuf[9]) { + case 0x01: // Keyboard or Joystick events + if(pRptParser[KEYBOARD_PARSER_ID]) + pRptParser[KEYBOARD_PARSER_ID]->Parse(reinterpret_cast(this), 0, (uint8_t)(length - 2), &l2capinbuf[10]); // Use reinterpret_cast again to extract the instance + break; + + case 0x02: // Mouse events + if(pRptParser[MOUSE_PARSER_ID]) + pRptParser[MOUSE_PARSER_ID]->Parse(reinterpret_cast(this), 0, (uint8_t)(length - 2), &l2capinbuf[10]); // Use reinterpret_cast again to extract the instance + break; +#ifdef EXTRADEBUG + default: + Notify(PSTR("\r\nUnknown Report type: "), 0x80); + D_PrintHex (l2capinbuf[9], 0x80); + break; +#endif + } + } + } else if(l2capinbuf[6] == control_dcid[0] && l2capinbuf[7] == control_dcid[1]) { // l2cap_control +#ifdef PRINTREPORT + Notify(PSTR("\r\nL2CAP Control: "), 0x80); + for(uint16_t i = 0; i < ((uint16_t)l2capinbuf[5] << 8 | l2capinbuf[4]); i++) { + D_PrintHex (l2capinbuf[i + 8], 0x80); + Notify(PSTR(" "), 0x80); + } +#endif + } +#ifdef EXTRADEBUG + else { + Notify(PSTR("\r\nUnsupported L2CAP Data - Channel ID: "), 0x80); + D_PrintHex (l2capinbuf[7], 0x80); + Notify(PSTR(" "), 0x80); + D_PrintHex (l2capinbuf[6], 0x80); + + Notify(PSTR("\r\nData: "), 0x80); + Notify(PSTR("\r\n"), 0x80); + for(uint16_t i = 0; i < ((uint16_t)l2capinbuf[5] << 8 | l2capinbuf[4]); i++) { + D_PrintHex (l2capinbuf[i + 8], 0x80); + Notify(PSTR(" "), 0x80); + } + } +#endif + L2CAP_task(); + } +} + +void BTHID::L2CAP_task() { + switch(l2cap_state) { + /* These states are used if the HID device is the host */ + case L2CAP_CONTROL_SUCCESS: + if(l2cap_check_flag(L2CAP_FLAG_CONFIG_CONTROL_SUCCESS)) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nHID Control Successfully Configured"), 0x80); +#endif + setProtocol(); // Set protocol before establishing HID interrupt channel + l2cap_state = L2CAP_INTERRUPT_SETUP; + } + break; + + case L2CAP_INTERRUPT_SETUP: + if(l2cap_check_flag(L2CAP_FLAG_CONNECTION_INTERRUPT_REQUEST)) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nHID Interrupt Incoming Connection Request"), 0x80); +#endif + pBtd->l2cap_connection_response(hci_handle, identifier, interrupt_dcid, interrupt_scid, PENDING); + delay(1); + pBtd->l2cap_connection_response(hci_handle, identifier, interrupt_dcid, interrupt_scid, SUCCESSFUL); + identifier++; + delay(1); + pBtd->l2cap_config_request(hci_handle, identifier, interrupt_scid); + + l2cap_state = L2CAP_INTERRUPT_CONFIG_REQUEST; + } + break; + + /* These states are used if the Arduino is the host */ + case L2CAP_CONTROL_CONNECT_REQUEST: + if(l2cap_check_flag(L2CAP_FLAG_CONTROL_CONNECTED)) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nSend HID Control Config Request"), 0x80); +#endif + identifier++; + pBtd->l2cap_config_request(hci_handle, identifier, control_scid); + l2cap_state = L2CAP_CONTROL_CONFIG_REQUEST; + } + break; + + case L2CAP_CONTROL_CONFIG_REQUEST: + if(l2cap_check_flag(L2CAP_FLAG_CONFIG_CONTROL_SUCCESS)) { + setProtocol(); // Set protocol before establishing HID interrupt channel + delay(1); // Short delay between commands - just to be sure +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nSend HID Interrupt Connection Request"), 0x80); +#endif + identifier++; + pBtd->l2cap_connection_request(hci_handle, identifier, interrupt_dcid, HID_INTR_PSM); + l2cap_state = L2CAP_INTERRUPT_CONNECT_REQUEST; + } + break; + + case L2CAP_INTERRUPT_CONNECT_REQUEST: + if(l2cap_check_flag(L2CAP_FLAG_INTERRUPT_CONNECTED)) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nSend HID Interrupt Config Request"), 0x80); +#endif + identifier++; + pBtd->l2cap_config_request(hci_handle, identifier, interrupt_scid); + l2cap_state = L2CAP_INTERRUPT_CONFIG_REQUEST; + } + break; + + case L2CAP_INTERRUPT_CONFIG_REQUEST: + if(l2cap_check_flag(L2CAP_FLAG_CONFIG_INTERRUPT_SUCCESS)) { // Now the HID channels is established +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nHID Channels Established"), 0x80); +#endif + pBtd->connectToHIDDevice = false; + pBtd->pairWithHIDDevice = false; + connected = true; + onInit(); + l2cap_state = L2CAP_DONE; + } + break; + + case L2CAP_DONE: + break; + + case L2CAP_INTERRUPT_DISCONNECT: + if(l2cap_check_flag(L2CAP_FLAG_DISCONNECT_INTERRUPT_RESPONSE)) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nDisconnected Interrupt Channel"), 0x80); +#endif + identifier++; + pBtd->l2cap_disconnection_request(hci_handle, identifier, control_scid, control_dcid); + l2cap_state = L2CAP_CONTROL_DISCONNECT; + } + break; + + case L2CAP_CONTROL_DISCONNECT: + if(l2cap_check_flag(L2CAP_FLAG_DISCONNECT_CONTROL_RESPONSE)) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nDisconnected Control Channel"), 0x80); +#endif + pBtd->hci_disconnect(hci_handle); + hci_handle = -1; // Reset handle + l2cap_event_flag = 0; // Reset flags + l2cap_state = L2CAP_WAIT; + } + break; + } +} + +void BTHID::Run() { + switch(l2cap_state) { + case L2CAP_WAIT: + if(pBtd->connectToHIDDevice && !pBtd->l2capConnectionClaimed && !connected && !activeConnection) { + pBtd->l2capConnectionClaimed = true; + activeConnection = true; +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nSend HID Control Connection Request"), 0x80); +#endif + hci_handle = pBtd->hci_handle; // Store the HCI Handle for the connection + l2cap_event_flag = 0; // Reset flags + identifier = 0; + pBtd->l2cap_connection_request(hci_handle, identifier, control_dcid, HID_CTRL_PSM); + l2cap_state = L2CAP_CONTROL_CONNECT_REQUEST; + } else if(l2cap_check_flag(L2CAP_FLAG_CONNECTION_CONTROL_REQUEST)) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nHID Control Incoming Connection Request"), 0x80); +#endif + pBtd->l2cap_connection_response(hci_handle, identifier, control_dcid, control_scid, PENDING); + delay(1); + pBtd->l2cap_connection_response(hci_handle, identifier, control_dcid, control_scid, SUCCESSFUL); + identifier++; + delay(1); + pBtd->l2cap_config_request(hci_handle, identifier, control_scid); + l2cap_state = L2CAP_CONTROL_SUCCESS; + } + break; + } +} + +/************************************************************/ +/* HID Commands */ + +/************************************************************/ +void BTHID::setProtocol() { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nSet protocol mode: "), 0x80); + D_PrintHex (protocolMode, 0x80); +#endif + if (protocolMode != HID_BOOT_PROTOCOL && protocolMode != HID_RPT_PROTOCOL) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nNot a valid protocol mode. Using Boot protocol instead."), 0x80); +#endif + protocolMode = HID_BOOT_PROTOCOL; // Use Boot Protocol by default + } + uint8_t command = 0x70 | protocolMode; // Set Protocol, see Bluetooth HID specs page 33 + pBtd->L2CAP_Command(hci_handle, &command, 1, control_scid[0], control_scid[1]); +} + +void BTHID::setLeds(uint8_t data) { + uint8_t buf[3]; + buf[0] = 0xA2; // HID BT DATA_request (0xA0) | Report Type (Output 0x02) + buf[1] = 0x01; // Report ID + buf[2] = data; + pBtd->L2CAP_Command(hci_handle, buf, 3, interrupt_scid[0], interrupt_scid[1]); +} diff --git a/libraries/USB_Host_Shield/BTHID.h b/libraries/USB_Host_Shield/BTHID.h new file mode 100755 index 0000000..1a7d868 --- /dev/null +++ b/libraries/USB_Host_Shield/BTHID.h @@ -0,0 +1,155 @@ +/* Copyright (C) 2013 Kristian Lauszus, TKJ Electronics. All rights reserved. + + This software may be distributed and modified under the terms of the GNU + General Public License version 2 (GPL2) as published by the Free Software + Foundation and appearing in the file GPL2.TXT included in the packaging of + this file. Please note that GPL2 Section 2[b] requires that all works based + on this software must also be made publicly available under the terms of + the GPL2 ("Copyleft"). + + Contact information + ------------------- + + Kristian Lauszus, TKJ Electronics + Web : http://www.tkjelectronics.com + e-mail : kristianl@tkjelectronics.com + */ + +#ifndef _bthid_h_ +#define _bthid_h_ + +#include "BTD.h" +#include "hidboot.h" + +#define KEYBOARD_PARSER_ID 0 +#define MOUSE_PARSER_ID 1 +#define NUM_PARSERS 2 + +/** This BluetoothService class implements support for Bluetooth HID devices. */ +class BTHID : public BluetoothService { +public: + /** + * Constructor for the BTHID class. + * @param p Pointer to the BTD class instance. + * @param pair Set this to true in order to pair with the device. If the argument is omitted then it will not pair with it. One can use ::PAIR to set it to true. + * @param pin Write the pin to BTD#btdPin. If argument is omitted, then "0000" will be used. + */ + BTHID(BTD *p, bool pair = false, const char *pin = "0000"); + + /** @name BluetoothService implementation */ + /** Used this to disconnect the devices. */ + void disconnect(); + /**@}*/ + + /** + * Get HIDReportParser. + * @param id ID of parser. + * @return Returns the corresponding HIDReportParser. Returns NULL if id is not valid. + */ + HIDReportParser *GetReportParser(uint8_t id) { + if (id >= NUM_PARSERS) + return NULL; + return pRptParser[id]; + }; + + /** + * Set HIDReportParser to be used. + * @param id Id of parser. + * @param prs Pointer to HIDReportParser. + * @return Returns true if the HIDReportParser is set. False otherwise. + */ + bool SetReportParser(uint8_t id, HIDReportParser *prs) { + if (id >= NUM_PARSERS) + return false; + pRptParser[id] = prs; + return true; + }; + + /** + * Set HID protocol mode. + * @param mode HID protocol to use. Either HID_BOOT_PROTOCOL or HID_RPT_PROTOCOL. + */ + void setProtocolMode(uint8_t mode) { + protocolMode = mode; + }; + + /** + * Used to set the leds on a keyboard. + * @param data See KBDLEDS in hidboot.h + */ + void setLeds(uint8_t data); + + /** True if a device is connected */ + bool connected; + + /** Call this to start the paring sequence with a device */ + void pair(void) { + if(pBtd) + pBtd->pairWithHID(); + }; + +protected: + /** @name BluetoothService implementation */ + /** + * Used to pass acldata to the services. + * @param ACLData Incoming acldata. + */ + void ACLData(uint8_t* ACLData); + /** Used to run part of the state machine. */ + void Run(); + /** Use this to reset the service. */ + void Reset(); + /** + * Called when a device is successfully initialized. + * Use attachOnInit(void (*funcOnInit)(void)) to call your own function. + * This is useful for instance if you want to set the LEDs in a specific way. + */ + void onInit() { + if(pFuncOnInit) + pFuncOnInit(); // Call the user function + OnInitBTHID(); + }; + /**@}*/ + + /** @name Overridable functions */ + /** + * Used to parse Bluetooth HID data to any class that inherits this class. + * @param len The length of the incoming data. + * @param buf Pointer to the data buffer. + */ + virtual void ParseBTHIDData(uint8_t len, uint8_t *buf) { + return; + }; + /** Called when a device is connected */ + virtual void OnInitBTHID() { + return; + }; + /** Used to reset any buffers in the class that inherits this */ + virtual void ResetBTHID() { + return; + } + /**@}*/ + + /** L2CAP source CID for HID_Control */ + uint8_t control_scid[2]; + + /** L2CAP source CID for HID_Interrupt */ + uint8_t interrupt_scid[2]; + +private: + HIDReportParser *pRptParser[NUM_PARSERS]; // Pointer to HIDReportParsers. + + /** Set report protocol. */ + void setProtocol(); + uint8_t protocolMode; + + void L2CAP_task(); // L2CAP state machine + + bool activeConnection; // Used to indicate if it already has established a connection + + /* Variables used for L2CAP communication */ + uint8_t control_dcid[2]; // L2CAP device CID for HID_Control - Always 0x0070 + uint8_t interrupt_dcid[2]; // L2CAP device CID for HID_Interrupt - Always 0x0071 + uint8_t l2cap_state; +}; +#endif diff --git a/libraries/USB_Host_Shield/PS3BT.cpp b/libraries/USB_Host_Shield/PS3BT.cpp new file mode 100755 index 0000000..235092e --- /dev/null +++ b/libraries/USB_Host_Shield/PS3BT.cpp @@ -0,0 +1,634 @@ +/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved. + + This software may be distributed and modified under the terms of the GNU + General Public License version 2 (GPL2) as published by the Free Software + Foundation and appearing in the file GPL2.TXT included in the packaging of + this file. Please note that GPL2 Section 2[b] requires that all works based + on this software must also be made publicly available under the terms of + the GPL2 ("Copyleft"). + + Contact information + ------------------- + + Kristian Lauszus, TKJ Electronics + Web : http://www.tkjelectronics.com + e-mail : kristianl@tkjelectronics.com + */ + +#include "PS3BT.h" +// To enable serial debugging see "settings.h" +//#define EXTRADEBUG // Uncomment to get even more debugging data +//#define PRINTREPORT // Uncomment to print the report send by the PS3 Controllers + +PS3BT::PS3BT(BTD *p, uint8_t btadr5, uint8_t btadr4, uint8_t btadr3, uint8_t btadr2, uint8_t btadr1, uint8_t btadr0) : +BluetoothService(p) // Pointer to USB class instance - mandatory +{ + pBtd->my_bdaddr[5] = btadr5; // Change to your dongle's Bluetooth address instead + pBtd->my_bdaddr[4] = btadr4; + pBtd->my_bdaddr[3] = btadr3; + pBtd->my_bdaddr[2] = btadr2; + pBtd->my_bdaddr[1] = btadr1; + pBtd->my_bdaddr[0] = btadr0; + + HIDBuffer[0] = 0x52; // HID BT Set_report (0x50) | Report Type (Output 0x02) + HIDBuffer[1] = 0x01; // Report ID + + // Needed for PS3 Move Controller commands to work via bluetooth + HIDMoveBuffer[0] = 0xA2; // HID BT DATA_request (0xA0) | Report Type (Output 0x02) + HIDMoveBuffer[1] = 0x02; // Report ID + + /* Set device cid for the control and intterrupt channelse - LSB */ + control_dcid[0] = 0x40; // 0x0040 + control_dcid[1] = 0x00; + interrupt_dcid[0] = 0x41; // 0x0041 + interrupt_dcid[1] = 0x00; + + Reset(); +} + +bool PS3BT::getButtonPress(ButtonEnum b) { + return (ButtonState & pgm_read_dword(&PS3_BUTTONS[(uint8_t)b])); +} + +bool PS3BT::getButtonClick(ButtonEnum b) { + uint32_t button = pgm_read_dword(&PS3_BUTTONS[(uint8_t)b]); + bool click = (ButtonClickState & button); + ButtonClickState &= ~button; // Clear "click" event + return click; +} + +uint8_t PS3BT::getAnalogButton(ButtonEnum a) { + return (uint8_t)(l2capinbuf[pgm_read_byte(&PS3_ANALOG_BUTTONS[(uint8_t)a])]); +} + +uint8_t PS3BT::getAnalogHat(AnalogHatEnum a) { + return (uint8_t)(l2capinbuf[(uint8_t)a + 15]); +} + +int16_t PS3BT::getSensor(SensorEnum a) { + if(PS3Connected) { + if(a == aX || a == aY || a == aZ || a == gZ) + return ((l2capinbuf[(uint16_t)a] << 8) | l2capinbuf[(uint16_t)a + 1]); + else + return 0; + } else if(PS3MoveConnected) { + if(a == mXmove || a == mYmove) // These are all 12-bits long + return (((l2capinbuf[(uint16_t)a] & 0x0F) << 8) | (l2capinbuf[(uint16_t)a + 1])); + else if(a == mZmove || a == tempMove) // The tempearature is also 12 bits long + return ((l2capinbuf[(uint16_t)a] << 4) | ((l2capinbuf[(uint16_t)a + 1] & 0xF0) >> 4)); + else // aXmove, aYmove, aZmove, gXmove, gYmove and gZmove + return (l2capinbuf[(uint16_t)a] | (l2capinbuf[(uint16_t)a + 1] << 8)); + } else + return 0; +} + +double PS3BT::getAngle(AngleEnum a) { + double accXval, accYval, accZval; + + if(PS3Connected) { + // Data for the Kionix KXPC4 used in the DualShock 3 + const double zeroG = 511.5; // 1.65/3.3*1023 (1.65V) + accXval = -((double)getSensor(aX) - zeroG); + accYval = -((double)getSensor(aY) - zeroG); + accZval = -((double)getSensor(aZ) - zeroG); + } else if(PS3MoveConnected) { + // It's a Kionix KXSC4 inside the Motion controller + const uint16_t zeroG = 0x8000; + accXval = -(int16_t)(getSensor(aXmove) - zeroG); + accYval = (int16_t)(getSensor(aYmove) - zeroG); + accZval = (int16_t)(getSensor(aZmove) - zeroG); + } else + return 0; + + // Convert to 360 degrees resolution + // atan2 outputs the value of -Ï€ to Ï€ (radians) + // We are then converting it to 0 to 2Ï€ and then to degrees + if(a == Pitch) + return (atan2(accYval, accZval) + PI) * RAD_TO_DEG; + else + return (atan2(accXval, accZval) + PI) * RAD_TO_DEG; +} + +double PS3BT::get9DOFValues(SensorEnum a) { // Thanks to Manfred Piendl + if(!PS3MoveConnected) + return 0; + int16_t value = getSensor(a); + if(a == mXmove || a == mYmove || a == mZmove) { + if(value > 2047) + value -= 0x1000; + return (double)value / 3.2; // unit: muT = 10^(-6) Tesla + } else if(a == aXmove || a == aYmove || a == aZmove) { + if(value < 0) + value += 0x8000; + else + value -= 0x8000; + return (double)value / 442.0; // unit: m/(s^2) + } else if(a == gXmove || a == gYmove || a == gZmove) { + if(value < 0) + value += 0x8000; + else + value -= 0x8000; + if(a == gXmove) + return (double)value / 11.6; // unit: deg/s + else if(a == gYmove) + return (double)value / 11.2; // unit: deg/s + else // gZmove + return (double)value / 9.6; // unit: deg/s + } else + return 0; +} + +String PS3BT::getTemperature() { + if(PS3MoveConnected) { + int16_t input = getSensor(tempMove); + + String output = String(input / 100); + output += "."; + if(input % 100 < 10) + output += "0"; + output += String(input % 100); + + return output; + } else + return "Error"; +} + +bool PS3BT::getStatus(StatusEnum c) { + return (l2capinbuf[(uint16_t)c >> 8] == ((uint8_t)c & 0xff)); +} + +void PS3BT::printStatusString() { + char statusOutput[100]; // Max string length plus null character + if(PS3Connected || PS3NavigationConnected) { + strcpy_P(statusOutput, PSTR("ConnectionStatus: ")); + + if(getStatus(Plugged)) strcat_P(statusOutput, PSTR("Plugged")); + else if(getStatus(Unplugged)) strcat_P(statusOutput, PSTR("Unplugged")); + else strcat_P(statusOutput, PSTR("Error")); + + strcat_P(statusOutput, PSTR(" - PowerRating: ")); + + if(getStatus(Charging)) strcat_P(statusOutput, PSTR("Charging")); + else if(getStatus(NotCharging)) strcat_P(statusOutput, PSTR("Not Charging")); + else if(getStatus(Shutdown)) strcat_P(statusOutput, PSTR("Shutdown")); + else if(getStatus(Dying)) strcat_P(statusOutput, PSTR("Dying")); + else if(getStatus(Low)) strcat_P(statusOutput, PSTR("Low")); + else if(getStatus(High)) strcat_P(statusOutput, PSTR("High")); + else if(getStatus(Full)) strcat_P(statusOutput, PSTR("Full")); + else strcat_P(statusOutput, PSTR("Error")); + + strcat_P(statusOutput, PSTR(" - WirelessStatus: ")); + + if(getStatus(CableRumble)) strcat_P(statusOutput, PSTR("Cable - Rumble is on")); + else if(getStatus(Cable)) strcat_P(statusOutput, PSTR("Cable - Rumble is off")); + else if(getStatus(BluetoothRumble)) strcat_P(statusOutput, PSTR("Bluetooth - Rumble is on")); + else if(getStatus(Bluetooth)) strcat_P(statusOutput, PSTR("Bluetooth - Rumble is off")); + else strcat_P(statusOutput, PSTR("Error")); + } else if(PS3MoveConnected) { + strcpy_P(statusOutput, PSTR("PowerRating: ")); + + if(getStatus(MoveCharging)) strcat_P(statusOutput, PSTR("Charging")); + else if(getStatus(MoveNotCharging)) strcat_P(statusOutput, PSTR("Not Charging")); + else if(getStatus(MoveShutdown)) strcat_P(statusOutput, PSTR("Shutdown")); + else if(getStatus(MoveDying)) strcat_P(statusOutput, PSTR("Dying")); + else if(getStatus(MoveLow)) strcat_P(statusOutput, PSTR("Low")); + else if(getStatus(MoveHigh)) strcat_P(statusOutput, PSTR("High")); + else if(getStatus(MoveFull)) strcat_P(statusOutput, PSTR("Full")); + else strcat_P(statusOutput, PSTR("Error")); + } else + strcpy_P(statusOutput, PSTR("Error")); + + USB_HOST_SERIAL.write(statusOutput); +} + +void PS3BT::Reset() { + PS3Connected = false; + PS3MoveConnected = false; + PS3NavigationConnected = false; + activeConnection = false; + l2cap_event_flag = 0; // Reset flags + l2cap_state = L2CAP_WAIT; + + // Needed for PS3 Dualshock Controller commands to work via Bluetooth + for(uint8_t i = 0; i < PS3_REPORT_BUFFER_SIZE; i++) + HIDBuffer[i + 2] = pgm_read_byte(&PS3_REPORT_BUFFER[i]); // First two bytes reserved for report type and ID +} + +void PS3BT::disconnect() { // Use this void to disconnect any of the controllers + // First the HID interrupt channel has to be disconnected, then the HID control channel and finally the HCI connection + pBtd->l2cap_disconnection_request(hci_handle, ++identifier, interrupt_scid, interrupt_dcid); + Reset(); + l2cap_state = L2CAP_INTERRUPT_DISCONNECT; +} + +void PS3BT::ACLData(uint8_t* ACLData) { + if(!pBtd->l2capConnectionClaimed && !PS3Connected && !PS3MoveConnected && !PS3NavigationConnected && !activeConnection && !pBtd->connectToWii && !pBtd->incomingWii && !pBtd->pairWithWii) { + if(ACLData[8] == L2CAP_CMD_CONNECTION_REQUEST) { + if((ACLData[12] | (ACLData[13] << 8)) == HID_CTRL_PSM) { + pBtd->l2capConnectionClaimed = true; // Claim that the incoming connection belongs to this service + activeConnection = true; + hci_handle = pBtd->hci_handle; // Store the HCI Handle for the connection + l2cap_state = L2CAP_WAIT; + remote_name_first = pBtd->remote_name[0]; // Store the first letter in remote name for the connection +#ifdef DEBUG_USB_HOST + if(pBtd->hci_version < 3) { // Check the HCI Version of the Bluetooth dongle + Notify(PSTR("\r\nYour dongle may not support reading the analog buttons, sensors and status\r\nYour HCI Version is: "), 0x80); + Notify(pBtd->hci_version, 0x80); + Notify(PSTR("\r\nBut should be at least 3\r\nThis means that it doesn't support Bluetooth Version 2.0+EDR"), 0x80); + } +#endif + } + } + } + + if(checkHciHandle(ACLData, hci_handle)) { // acl_handle_ok + memcpy(l2capinbuf, ACLData, BULK_MAXPKTSIZE); + if((l2capinbuf[6] | (l2capinbuf[7] << 8)) == 0x0001U) { // l2cap_control - Channel ID for ACL-U + if(l2capinbuf[8] == L2CAP_CMD_COMMAND_REJECT) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nL2CAP Command Rejected - Reason: "), 0x80); + D_PrintHex (l2capinbuf[13], 0x80); + Notify(PSTR(" "), 0x80); + D_PrintHex (l2capinbuf[12], 0x80); + Notify(PSTR(" Data: "), 0x80); + D_PrintHex (l2capinbuf[17], 0x80); + Notify(PSTR(" "), 0x80); + D_PrintHex (l2capinbuf[16], 0x80); + Notify(PSTR(" "), 0x80); + D_PrintHex (l2capinbuf[15], 0x80); + Notify(PSTR(" "), 0x80); + D_PrintHex (l2capinbuf[14], 0x80); +#endif + } else if(l2capinbuf[8] == L2CAP_CMD_CONNECTION_REQUEST) { +#ifdef EXTRADEBUG + Notify(PSTR("\r\nL2CAP Connection Request - PSM: "), 0x80); + D_PrintHex (l2capinbuf[13], 0x80); + Notify(PSTR(" "), 0x80); + D_PrintHex (l2capinbuf[12], 0x80); + Notify(PSTR(" SCID: "), 0x80); + D_PrintHex (l2capinbuf[15], 0x80); + Notify(PSTR(" "), 0x80); + D_PrintHex (l2capinbuf[14], 0x80); + Notify(PSTR(" Identifier: "), 0x80); + D_PrintHex (l2capinbuf[9], 0x80); +#endif + if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == HID_CTRL_PSM) { + identifier = l2capinbuf[9]; + control_scid[0] = l2capinbuf[14]; + control_scid[1] = l2capinbuf[15]; + l2cap_set_flag(L2CAP_FLAG_CONNECTION_CONTROL_REQUEST); + } else if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == HID_INTR_PSM) { + identifier = l2capinbuf[9]; + interrupt_scid[0] = l2capinbuf[14]; + interrupt_scid[1] = l2capinbuf[15]; + l2cap_set_flag(L2CAP_FLAG_CONNECTION_INTERRUPT_REQUEST); + } + } else if(l2capinbuf[8] == L2CAP_CMD_CONFIG_RESPONSE) { + if((l2capinbuf[16] | (l2capinbuf[17] << 8)) == 0x0000) { // Success + if(l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) { + //Notify(PSTR("\r\nHID Control Configuration Complete"), 0x80); + l2cap_set_flag(L2CAP_FLAG_CONFIG_CONTROL_SUCCESS); + } else if(l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) { + //Notify(PSTR("\r\nHID Interrupt Configuration Complete"), 0x80); + l2cap_set_flag(L2CAP_FLAG_CONFIG_INTERRUPT_SUCCESS); + } + } + } else if(l2capinbuf[8] == L2CAP_CMD_CONFIG_REQUEST) { + if(l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) { + //Notify(PSTR("\r\nHID Control Configuration Request"), 0x80); + pBtd->l2cap_config_response(hci_handle, l2capinbuf[9], control_scid); + } else if(l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) { + //Notify(PSTR("\r\nHID Interrupt Configuration Request"), 0x80); + pBtd->l2cap_config_response(hci_handle, l2capinbuf[9], interrupt_scid); + } + } else if(l2capinbuf[8] == L2CAP_CMD_DISCONNECT_REQUEST) { + if(l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nDisconnect Request: Control Channel"), 0x80); +#endif + identifier = l2capinbuf[9]; + pBtd->l2cap_disconnection_response(hci_handle, identifier, control_dcid, control_scid); + Reset(); + } else if(l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nDisconnect Request: Interrupt Channel"), 0x80); +#endif + identifier = l2capinbuf[9]; + pBtd->l2cap_disconnection_response(hci_handle, identifier, interrupt_dcid, interrupt_scid); + Reset(); + } + } else if(l2capinbuf[8] == L2CAP_CMD_DISCONNECT_RESPONSE) { + if(l2capinbuf[12] == control_scid[0] && l2capinbuf[13] == control_scid[1]) { + //Notify(PSTR("\r\nDisconnect Response: Control Channel"), 0x80); + identifier = l2capinbuf[9]; + l2cap_set_flag(L2CAP_FLAG_DISCONNECT_CONTROL_RESPONSE); + } else if(l2capinbuf[12] == interrupt_scid[0] && l2capinbuf[13] == interrupt_scid[1]) { + //Notify(PSTR("\r\nDisconnect Response: Interrupt Channel"), 0x80); + identifier = l2capinbuf[9]; + l2cap_set_flag(L2CAP_FLAG_DISCONNECT_INTERRUPT_RESPONSE); + } + } +#ifdef EXTRADEBUG + else { + Notify(PSTR("\r\nL2CAP Unknown Signaling Command: "), 0x80); + D_PrintHex (l2capinbuf[8], 0x80); + } +#endif + } else if(l2capinbuf[6] == interrupt_dcid[0] && l2capinbuf[7] == interrupt_dcid[1]) { // l2cap_interrupt + //Notify(PSTR("\r\nL2CAP Interrupt"), 0x80); + if(PS3Connected || PS3MoveConnected || PS3NavigationConnected) { + /* Read Report */ + if(l2capinbuf[8] == 0xA1) { // HID_THDR_DATA_INPUT + lastMessageTime = millis(); // Store the last message time + + if(PS3Connected || PS3NavigationConnected) + ButtonState = (uint32_t)(l2capinbuf[11] | ((uint16_t)l2capinbuf[12] << 8) | ((uint32_t)l2capinbuf[13] << 16)); + else if(PS3MoveConnected) + ButtonState = (uint32_t)(l2capinbuf[10] | ((uint16_t)l2capinbuf[11] << 8) | ((uint32_t)l2capinbuf[12] << 16)); + + //Notify(PSTR("\r\nButtonState", 0x80); + //PrintHex(ButtonState, 0x80); + + if(ButtonState != OldButtonState) { + ButtonClickState = ButtonState & ~OldButtonState; // Update click state variable + OldButtonState = ButtonState; + } + +#ifdef PRINTREPORT // Uncomment "#define PRINTREPORT" to print the report send by the PS3 Controllers + for(uint8_t i = 10; i < 58; i++) { + D_PrintHex (l2capinbuf[i], 0x80); + Notify(PSTR(" "), 0x80); + } + Notify(PSTR("\r\n"), 0x80); +#endif + } + } + } + L2CAP_task(); + } +} + +void PS3BT::L2CAP_task() { + switch(l2cap_state) { + case L2CAP_WAIT: + if(l2cap_check_flag(L2CAP_FLAG_CONNECTION_CONTROL_REQUEST)) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nHID Control Incoming Connection Request"), 0x80); +#endif + pBtd->l2cap_connection_response(hci_handle, identifier, control_dcid, control_scid, PENDING); + delay(1); + pBtd->l2cap_connection_response(hci_handle, identifier, control_dcid, control_scid, SUCCESSFUL); + identifier++; + delay(1); + pBtd->l2cap_config_request(hci_handle, identifier, control_scid); + l2cap_state = L2CAP_CONTROL_SUCCESS; + } + break; + + case L2CAP_CONTROL_SUCCESS: + if(l2cap_check_flag(L2CAP_FLAG_CONFIG_CONTROL_SUCCESS)) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nHID Control Successfully Configured"), 0x80); +#endif + l2cap_state = L2CAP_INTERRUPT_SETUP; + } + break; + + case L2CAP_INTERRUPT_SETUP: + if(l2cap_check_flag(L2CAP_FLAG_CONNECTION_INTERRUPT_REQUEST)) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nHID Interrupt Incoming Connection Request"), 0x80); +#endif + pBtd->l2cap_connection_response(hci_handle, identifier, interrupt_dcid, interrupt_scid, PENDING); + delay(1); + pBtd->l2cap_connection_response(hci_handle, identifier, interrupt_dcid, interrupt_scid, SUCCESSFUL); + identifier++; + delay(1); + pBtd->l2cap_config_request(hci_handle, identifier, interrupt_scid); + + l2cap_state = L2CAP_INTERRUPT_CONFIG_REQUEST; + } + break; + + case L2CAP_INTERRUPT_CONFIG_REQUEST: + if(l2cap_check_flag(L2CAP_FLAG_CONFIG_INTERRUPT_SUCCESS)) { // Now the HID channels is established +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nHID Interrupt Successfully Configured"), 0x80); +#endif + if(remote_name_first == 'M') { // First letter in Motion Controller ('M') + memset(l2capinbuf, 0, BULK_MAXPKTSIZE); // Reset l2cap in buffer as it sometimes read it as a button has been pressed + l2cap_state = TURN_ON_LED; + } else + l2cap_state = PS3_ENABLE_SIXAXIS; + timer = millis(); + } + break; + + /* These states are handled in Run() */ + + case L2CAP_INTERRUPT_DISCONNECT: + if(l2cap_check_flag(L2CAP_FLAG_DISCONNECT_INTERRUPT_RESPONSE)) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nDisconnected Interrupt Channel"), 0x80); +#endif + identifier++; + pBtd->l2cap_disconnection_request(hci_handle, identifier, control_scid, control_dcid); + l2cap_state = L2CAP_CONTROL_DISCONNECT; + } + break; + + case L2CAP_CONTROL_DISCONNECT: + if(l2cap_check_flag(L2CAP_FLAG_DISCONNECT_CONTROL_RESPONSE)) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nDisconnected Control Channel"), 0x80); +#endif + pBtd->hci_disconnect(hci_handle); + hci_handle = -1; // Reset handle + l2cap_event_flag = 0; // Reset flags + l2cap_state = L2CAP_WAIT; + } + break; + } +} + +void PS3BT::Run() { + switch(l2cap_state) { + case PS3_ENABLE_SIXAXIS: + if(millis() - timer > 1000) { // loop 1 second before sending the command + memset(l2capinbuf, 0, BULK_MAXPKTSIZE); // Reset l2cap in buffer as it sometimes read it as a button has been pressed + for(uint8_t i = 15; i < 19; i++) + l2capinbuf[i] = 0x7F; // Set the analog joystick values to center position + enable_sixaxis(); + l2cap_state = TURN_ON_LED; + timer = millis(); + } + break; + + case TURN_ON_LED: + if(millis() - timer > 1000) { // loop 1 second before sending the command + if(remote_name_first == 'P') { // First letter in PLAYSTATION(R)3 Controller ('P') +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nDualshock 3 Controller Enabled\r\n"), 0x80); +#endif + PS3Connected = true; + } else if(remote_name_first == 'N') { // First letter in Navigation Controller ('N') +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nNavigation Controller Enabled\r\n"), 0x80); +#endif + PS3NavigationConnected = true; + } else if(remote_name_first == 'M') { // First letter in Motion Controller ('M') + timer = millis(); +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nMotion Controller Enabled\r\n"), 0x80); +#endif + PS3MoveConnected = true; + } + ButtonState = 0; // Clear all values + OldButtonState = 0; + ButtonClickState = 0; + + onInit(); // Turn on the LED on the controller + l2cap_state = L2CAP_DONE; + } + break; + + case L2CAP_DONE: + if(PS3MoveConnected) { // The Bulb and rumble values, has to be send at approximately every 5th second for it to stay on + if(millis() - timer > 4000) { // Send at least every 4th second + HIDMove_Command(HIDMoveBuffer, HID_BUFFERSIZE); // The Bulb and rumble values, has to be written again and again, for it to stay turned on + timer = millis(); + } + } + break; + } +} + +/************************************************************/ +/* HID Commands */ +/************************************************************/ + +// Playstation Sixaxis Dualshock and Navigation Controller commands + +void PS3BT::HID_Command(uint8_t* data, uint8_t nbytes) { + if(millis() - timerHID <= 150) // Check if is has been more than 150ms since last command + delay((uint32_t)(150 - (millis() - timerHID))); // There have to be a delay between commands + pBtd->L2CAP_Command(hci_handle, data, nbytes, control_scid[0], control_scid[1]); // Both the Navigation and Dualshock controller sends data via the control channel + timerHID = millis(); +} + +void PS3BT::setAllOff() { + HIDBuffer[3] = 0x00; // Rumble bytes + HIDBuffer[4] = 0x00; + HIDBuffer[5] = 0x00; + HIDBuffer[6] = 0x00; + + HIDBuffer[11] = 0x00; // LED byte + + HID_Command(HIDBuffer, HID_BUFFERSIZE); +} + +void PS3BT::setRumbleOff() { + HIDBuffer[3] = 0x00; + HIDBuffer[4] = 0x00; + HIDBuffer[5] = 0x00; + HIDBuffer[6] = 0x00; + + HID_Command(HIDBuffer, HID_BUFFERSIZE); +} + +void PS3BT::setRumbleOn(RumbleEnum mode) { + uint8_t power[2] = {0xff, 0x00}; // Defaults to RumbleLow + if(mode == RumbleHigh) { + power[0] = 0x00; + power[1] = 0xff; + } + setRumbleOn(0xfe, power[0], 0xfe, power[1]); +} + +void PS3BT::setRumbleOn(uint8_t rightDuration, uint8_t rightPower, uint8_t leftDuration, uint8_t leftPower) { + HIDBuffer[3] = rightDuration; + HIDBuffer[4] = rightPower; + HIDBuffer[5] = leftDuration; + HIDBuffer[6] = leftPower; + HID_Command(HIDBuffer, HID_BUFFERSIZE); +} + +void PS3BT::setLedRaw(uint8_t value) { + HIDBuffer[11] = value << 1; + HID_Command(HIDBuffer, HID_BUFFERSIZE); +} + +void PS3BT::setLedOff(LEDEnum a) { + HIDBuffer[11] &= ~((uint8_t)((pgm_read_byte(&PS3_LEDS[(uint8_t)a]) & 0x0f) << 1)); + HID_Command(HIDBuffer, HID_BUFFERSIZE); +} + +void PS3BT::setLedOn(LEDEnum a) { + if(a == OFF) + setLedRaw(0); + else { + HIDBuffer[11] |= (uint8_t)((pgm_read_byte(&PS3_LEDS[(uint8_t)a]) & 0x0f) << 1); + HID_Command(HIDBuffer, HID_BUFFERSIZE); + } +} + +void PS3BT::setLedToggle(LEDEnum a) { + HIDBuffer[11] ^= (uint8_t)((pgm_read_byte(&PS3_LEDS[(uint8_t)a]) & 0x0f) << 1); + HID_Command(HIDBuffer, HID_BUFFERSIZE); +} + +void PS3BT::enable_sixaxis() { // Command used to enable the Dualshock 3 and Navigation controller to send data via Bluetooth + uint8_t cmd_buf[6]; + cmd_buf[0] = 0x53; // HID BT Set_report (0x50) | Report Type (Feature 0x03) + cmd_buf[1] = 0xF4; // Report ID + cmd_buf[2] = 0x42; // Special PS3 Controller enable commands + cmd_buf[3] = 0x03; + cmd_buf[4] = 0x00; + cmd_buf[5] = 0x00; + + HID_Command(cmd_buf, 6); +} + +// Playstation Move Controller commands + +void PS3BT::HIDMove_Command(uint8_t* data, uint8_t nbytes) { + if(millis() - timerHID <= 150)// Check if is has been less than 150ms since last command + delay((uint32_t)(150 - (millis() - timerHID))); // There have to be a delay between commands + pBtd->L2CAP_Command(hci_handle, data, nbytes, interrupt_scid[0], interrupt_scid[1]); // The Move controller sends it's data via the intterrupt channel + timerHID = millis(); +} + +void PS3BT::moveSetBulb(uint8_t r, uint8_t g, uint8_t b) { // Use this to set the Color using RGB values + // Set the Bulb's values into the write buffer + HIDMoveBuffer[3] = r; + HIDMoveBuffer[4] = g; + HIDMoveBuffer[5] = b; + + HIDMove_Command(HIDMoveBuffer, HID_BUFFERSIZE); +} + +void PS3BT::moveSetBulb(ColorsEnum color) { // Use this to set the Color using the predefined colors in enum + moveSetBulb((uint8_t)(color >> 16), (uint8_t)(color >> 8), (uint8_t)(color)); +} + +void PS3BT::moveSetRumble(uint8_t rumble) { +#ifdef DEBUG_USB_HOST + if(rumble < 64 && rumble != 0) // The rumble value has to at least 64, or approximately 25% (64/255*100) + Notify(PSTR("\r\nThe rumble value has to at least 64, or approximately 25%"), 0x80); +#endif + // Set the rumble value into the write buffer + HIDMoveBuffer[7] = rumble; + + HIDMove_Command(HIDMoveBuffer, HID_BUFFERSIZE); +} + +void PS3BT::onInit() { + if(pFuncOnInit) + pFuncOnInit(); // Call the user function + else { + if(PS3MoveConnected) + moveSetBulb(Red); + else // Dualshock 3 or Navigation controller + setLedOn(LED1); + } +} diff --git a/libraries/USB_Host_Shield/PS3BT.h b/libraries/USB_Host_Shield/PS3BT.h new file mode 100755 index 0000000..c25ac5e --- /dev/null +++ b/libraries/USB_Host_Shield/PS3BT.h @@ -0,0 +1,240 @@ +/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved. + + This software may be distributed and modified under the terms of the GNU + General Public License version 2 (GPL2) as published by the Free Software + Foundation and appearing in the file GPL2.TXT included in the packaging of + this file. Please note that GPL2 Section 2[b] requires that all works based + on this software must also be made publicly available under the terms of + the GPL2 ("Copyleft"). + + Contact information + ------------------- + + Kristian Lauszus, TKJ Electronics + Web : http://www.tkjelectronics.com + e-mail : kristianl@tkjelectronics.com + */ + +#ifndef _ps3bt_h_ +#define _ps3bt_h_ + +#include "BTD.h" +#include "PS3Enums.h" + +#define HID_BUFFERSIZE 50 // Size of the buffer for the Playstation Motion Controller + +/** + * This BluetoothService class implements support for all the official PS3 Controllers: + * Dualshock 3, Navigation or a Motion controller via Bluetooth. + * + * Information about the protocol can be found at the wiki: https://github.com/felis/USB_Host_Shield_2.0/wiki/PS3-Information. + */ +class PS3BT : public BluetoothService { +public: + /** + * Constructor for the PS3BT class. + * @param pBtd Pointer to BTD class instance. + * @param btadr5,btadr4,btadr3,btadr2,btadr1,btadr0 + * Pass your dongles Bluetooth address into the constructor, + * This will set BTD#my_bdaddr, so you don't have to plug in the dongle before pairing with your controller. + */ + PS3BT(BTD *pBtd, uint8_t btadr5 = 0, uint8_t btadr4 = 0, uint8_t btadr3 = 0, uint8_t btadr2 = 0, uint8_t btadr1 = 0, uint8_t btadr0 = 0); + + /** @name BluetoothService implementation */ + /** Used this to disconnect any of the controllers. */ + void disconnect(); + /**@}*/ + + /** @name PS3 Controller functions */ + /** + * getButtonPress(ButtonEnum b) will return true as long as the button is held down. + * + * While getButtonClick(ButtonEnum b) will only return it once. + * + * So you instance if you need to increase a variable once you would use getButtonClick(ButtonEnum b), + * but if you need to drive a robot forward you would use getButtonPress(ButtonEnum b). + * @param b ::ButtonEnum to read. + * @return getButtonPress(ButtonEnum b) will return a true as long as a button is held down, while getButtonClick(ButtonEnum b) will return true once for each button press. + */ + bool getButtonPress(ButtonEnum b); + bool getButtonClick(ButtonEnum b); + /**@}*/ + /** @name PS3 Controller functions */ + /** + * Used to get the analog value from button presses. + * @param a The ::ButtonEnum to read. + * The supported buttons are: + * ::UP, ::RIGHT, ::DOWN, ::LEFT, ::L1, ::L2, ::R1, ::R2, + * ::TRIANGLE, ::CIRCLE, ::CROSS, ::SQUARE, and ::T. + * @return Analog value in the range of 0-255. + */ + uint8_t getAnalogButton(ButtonEnum a); + /** + * Used to read the analog joystick. + * @param a ::LeftHatX, ::LeftHatY, ::RightHatX, and ::RightHatY. + * @return Return the analog value in the range of 0-255. + */ + uint8_t getAnalogHat(AnalogHatEnum a); + /** + * Used to read the sensors inside the Dualshock 3 and Move controller. + * @param a + * The Dualshock 3 has a 3-axis accelerometer and a 1-axis gyro inside. + * The Move controller has a 3-axis accelerometer, a 3-axis gyro, a 3-axis magnetometer + * and a temperature sensor inside. + * @return Return the raw sensor value. + */ + int16_t getSensor(SensorEnum a); + /** + * Use this to get ::Pitch and ::Roll calculated using the accelerometer. + * @param a Either ::Pitch or ::Roll. + * @return Return the angle in the range of 0-360. + */ + double getAngle(AngleEnum a); + /** + * Read the sensors inside the Move controller. + * @param a ::aXmove, ::aYmove, ::aZmove, ::gXmove, ::gYmove, ::gZmove, ::mXmove, ::mYmove, and ::mXmove. + * @return The value in SI units. + */ + double get9DOFValues(SensorEnum a); + /** + * Get the status from the controller. + * @param c The ::StatusEnum you want to read. + * @return True if correct and false if not. + */ + bool getStatus(StatusEnum c); + /** Read all the available statuses from the controller and prints it as a nice formated string. */ + void printStatusString(); + /** + * Read the temperature from the Move controller. + * @return The temperature in degrees Celsius. + */ + String getTemperature(); + + /** Used to set all LEDs and rumble off. */ + void setAllOff(); + /** Turn off rumble. */ + void setRumbleOff(); + /** + * Turn on rumble. + * @param mode Either ::RumbleHigh or ::RumbleLow. + */ + void setRumbleOn(RumbleEnum mode); + /** + * Turn on rumble using custom duration and power. + * @param rightDuration The duration of the right/low rumble effect. + * @param rightPower The intensity of the right/low rumble effect. + * @param leftDuration The duration of the left/high rumble effect. + * @param leftPower The intensity of the left/high rumble effect. + */ + void setRumbleOn(uint8_t rightDuration, uint8_t rightPower, uint8_t leftDuration, uint8_t leftPower); + + /** + * Set LED value without using ::LEDEnum. + * @param value See: ::LEDEnum. + */ + void setLedRaw(uint8_t value); + + /** Turn all LEDs off. */ + void setLedOff() { + setLedRaw(0); + }; + /** + * Turn the specific LED off. + * @param a The ::LEDEnum to turn off. + */ + void setLedOff(LEDEnum a); + /** + * Turn the specific LED on. + * @param a The ::LEDEnum to turn on. + */ + void setLedOn(LEDEnum a); + /** + * Toggle the specific LED. + * @param a The ::LEDEnum to toggle. + */ + void setLedToggle(LEDEnum a); + + /** + * Use this to set the Color using RGB values. + * @param r,g,b RGB value. + */ + void moveSetBulb(uint8_t r, uint8_t g, uint8_t b); + /** + * Use this to set the color using the predefined colors in ::ColorsEnum. + * @param color The desired color. + */ + void moveSetBulb(ColorsEnum color); + /** + * Set the rumble value inside the Move controller. + * @param rumble The desired value in the range from 64-255. + */ + void moveSetRumble(uint8_t rumble); + + /** Used to get the millis() of the last message */ + uint32_t getLastMessageTime() { + return lastMessageTime; + }; + /**@}*/ + + /** Variable used to indicate if the normal Playstation controller is successfully connected. */ + bool PS3Connected; + /** Variable used to indicate if the Move controller is successfully connected. */ + bool PS3MoveConnected; + /** Variable used to indicate if the Navigation controller is successfully connected. */ + bool PS3NavigationConnected; + +protected: + /** @name BluetoothService implementation */ + /** + * Used to pass acldata to the services. + * @param ACLData Incoming acldata. + */ + void ACLData(uint8_t* ACLData); + /** Used to run part of the state machine. */ + void Run(); + /** Use this to reset the service. */ + void Reset(); + /** + * Called when the controller is successfully initialized. + * Use attachOnInit(void (*funcOnInit)(void)) to call your own function. + * This is useful for instance if you want to set the LEDs in a specific way. + */ + void onInit(); + /**@}*/ + +private: + + void L2CAP_task(); // L2CAP state machine + + /* Variables filled from HCI event management */ + char remote_name_first; // First letter in remote name + bool activeConnection; // Used to indicate if it's already has established a connection + + /* Variables used by high level L2CAP task */ + uint8_t l2cap_state; + + uint32_t lastMessageTime; // Variable used to store the millis value of the last message. + + uint32_t ButtonState; + uint32_t OldButtonState; + uint32_t ButtonClickState; + + uint32_t timer; // Timer used to limit time between messages and also used to continuously set PS3 Move controller Bulb and rumble values + uint32_t timerHID; // Timer used see if there has to be a delay before a new HID command + + uint8_t l2capinbuf[BULK_MAXPKTSIZE]; // General purpose buffer for L2CAP in data + uint8_t HIDBuffer[HID_BUFFERSIZE]; // Used to store HID commands + uint8_t HIDMoveBuffer[HID_BUFFERSIZE]; // Used to store HID commands for the Move controller + + /* L2CAP Channels */ + uint8_t control_scid[2]; // L2CAP source CID for HID_Control + uint8_t control_dcid[2]; // 0x0040 + uint8_t interrupt_scid[2]; // L2CAP source CID for HID_Interrupt + uint8_t interrupt_dcid[2]; // 0x0041 + + /* HID Commands */ + void HID_Command(uint8_t* data, uint8_t nbytes); + void HIDMove_Command(uint8_t* data, uint8_t nbytes); + void enable_sixaxis(); // Command used to enable the Dualshock 3 and Navigation controller to send data via Bluetooth +}; +#endif diff --git a/libraries/USB_Host_Shield/PS3Enums.h b/libraries/USB_Host_Shield/PS3Enums.h new file mode 100755 index 0000000..7780194 --- /dev/null +++ b/libraries/USB_Host_Shield/PS3Enums.h @@ -0,0 +1,141 @@ +/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved. + + This software may be distributed and modified under the terms of the GNU + General Public License version 2 (GPL2) as published by the Free Software + Foundation and appearing in the file GPL2.TXT included in the packaging of + this file. Please note that GPL2 Section 2[b] requires that all works based + on this software must also be made publicly available under the terms of + the GPL2 ("Copyleft"). + + Contact information + ------------------- + + Kristian Lauszus, TKJ Electronics + Web : http://www.tkjelectronics.com + e-mail : kristianl@tkjelectronics.com + */ + +#ifndef _ps3enums_h +#define _ps3enums_h + +#include "controllerEnums.h" + +/** Size of the output report buffer for the Dualshock and Navigation controllers */ +#define PS3_REPORT_BUFFER_SIZE 48 + +/** Report buffer for all PS3 commands */ +const uint8_t PS3_REPORT_BUFFER[PS3_REPORT_BUFFER_SIZE] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x27, 0x10, 0x00, 0x32, + 0xff, 0x27, 0x10, 0x00, 0x32, + 0xff, 0x27, 0x10, 0x00, 0x32, + 0xff, 0x27, 0x10, 0x00, 0x32, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/** Size of the output report buffer for the Move Controller */ +#define MOVE_REPORT_BUFFER_SIZE 7 + +/** Used to set the LEDs on the controllers */ +const uint8_t PS3_LEDS[] PROGMEM = { + 0x00, // OFF + 0x01, // LED1 + 0x02, // LED2 + 0x04, // LED3 + 0x08, // LED4 + + 0x09, // LED5 + 0x0A, // LED6 + 0x0C, // LED7 + 0x0D, // LED8 + 0x0E, // LED9 + 0x0F, // LED10 +}; + +/** + * Buttons on the controllers. + * Note: that the location is shifted 9 when it's connected via USB. + */ +const uint32_t PS3_BUTTONS[] PROGMEM = { + 0x10, // UP + 0x20, // RIGHT + 0x40, // DOWN + 0x80, // LEFT + + 0x01, // SELECT + 0x08, // START + 0x02, // L3 + 0x04, // R3 + + 0x0100, // L2 + 0x0200, // R2 + 0x0400, // L1 + 0x0800, // R1 + + 0x1000, // TRIANGLE + 0x2000, // CIRCLE + 0x4000, // CROSS + 0x8000, // SQUARE + + 0x010000, // PS + 0x080000, // MOVE - covers 12 bits - we only need to read the top 8 + 0x100000, // T - covers 12 bits - we only need to read the top 8 +}; + +/** + * Analog buttons on the controllers. + * Note: that the location is shifted 9 when it's connected via USB. + */ +const uint8_t PS3_ANALOG_BUTTONS[] PROGMEM = { + 23, // UP_ANALOG + 24, // RIGHT_ANALOG + 25, // DOWN_ANALOG + 26, // LEFT_ANALOG + 0, 0, 0, 0, // Skip SELECT, L3, R3 and START + + 27, // L2_ANALOG + 28, // R2_ANALOG + 29, // L1_ANALOG + 30, // R1_ANALOG + 31, // TRIANGLE_ANALOG + 32, // CIRCLE_ANALOG + 33, // CROSS_ANALOG + 34, // SQUARE_ANALOG + 0, 0, // Skip PS and MOVE + + // Playstation Move Controller + 15, // T_ANALOG - Both at byte 14 (last reading) and byte 15 (current reading) +}; + +enum StatusEnum { + // Note that the location is shifted 9 when it's connected via USB + // Byte location | bit location + Plugged = (38 << 8) | 0x02, + Unplugged = (38 << 8) | 0x03, + + Charging = (39 << 8) | 0xEE, + NotCharging = (39 << 8) | 0xF1, + Shutdown = (39 << 8) | 0x01, + Dying = (39 << 8) | 0x02, + Low = (39 << 8) | 0x03, + High = (39 << 8) | 0x04, + Full = (39 << 8) | 0x05, + + MoveCharging = (21 << 8) | 0xEE, + MoveNotCharging = (21 << 8) | 0xF1, + MoveShutdown = (21 << 8) | 0x01, + MoveDying = (21 << 8) | 0x02, + MoveLow = (21 << 8) | 0x03, + MoveHigh = (21 << 8) | 0x04, + MoveFull = (21 << 8) | 0x05, + + CableRumble = (40 << 8) | 0x10, // Operating by USB and rumble is turned on + Cable = (40 << 8) | 0x12, // Operating by USB and rumble is turned off + BluetoothRumble = (40 << 8) | 0x14, // Operating by Bluetooth and rumble is turned on + Bluetooth = (40 << 8) | 0x16, // Operating by Bluetooth and rumble is turned off +}; + +#endif diff --git a/libraries/USB_Host_Shield/PS3USB.cpp b/libraries/USB_Host_Shield/PS3USB.cpp new file mode 100755 index 0000000..c321753 --- /dev/null +++ b/libraries/USB_Host_Shield/PS3USB.cpp @@ -0,0 +1,572 @@ +/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved. + + This software may be distributed and modified under the terms of the GNU + General Public License version 2 (GPL2) as published by the Free Software + Foundation and appearing in the file GPL2.TXT included in the packaging of + this file. Please note that GPL2 Section 2[b] requires that all works based + on this software must also be made publicly available under the terms of + the GPL2 ("Copyleft"). + + Contact information + ------------------- + + Kristian Lauszus, TKJ Electronics + Web : http://www.tkjelectronics.com + e-mail : kristianl@tkjelectronics.com + */ + +#include "PS3USB.h" +// To enable serial debugging see "settings.h" +//#define EXTRADEBUG // Uncomment to get even more debugging data +//#define PRINTREPORT // Uncomment to print the report send by the PS3 Controllers + +PS3USB::PS3USB(USB *p, uint8_t btadr5, uint8_t btadr4, uint8_t btadr3, uint8_t btadr2, uint8_t btadr1, uint8_t btadr0) : +pUsb(p), // pointer to USB class instance - mandatory +bAddress(0), // device address - mandatory +bPollEnable(false) // don't start polling before dongle is connected +{ + for(uint8_t i = 0; i < PS3_MAX_ENDPOINTS; i++) { + epInfo[i].epAddr = 0; + epInfo[i].maxPktSize = (i) ? 0 : 8; + epInfo[i].epAttribs = 0; + epInfo[i].bmNakPower = (i) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER; + } + + if(pUsb) // register in USB subsystem + pUsb->RegisterDeviceClass(this); //set devConfig[] entry + + my_bdaddr[5] = btadr5; // Change to your dongle's Bluetooth address instead + my_bdaddr[4] = btadr4; + my_bdaddr[3] = btadr3; + my_bdaddr[2] = btadr2; + my_bdaddr[1] = btadr1; + my_bdaddr[0] = btadr0; +} + +uint8_t PS3USB::Init(uint8_t parent, uint8_t port, bool lowspeed) { + uint8_t buf[sizeof (USB_DEVICE_DESCRIPTOR)]; + USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast(buf); + uint8_t rcode; + UsbDevice *p = NULL; + EpInfo *oldep_ptr = NULL; + uint16_t PID; + uint16_t VID; + + // get memory address of USB device address pool + AddressPool &addrPool = pUsb->GetAddressPool(); +#ifdef EXTRADEBUG + Notify(PSTR("\r\nPS3USB Init"), 0x80); +#endif + // check if address has already been assigned to an instance + if(bAddress) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nAddress in use"), 0x80); +#endif + return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE; + } + + // Get pointer to pseudo device with address 0 assigned + p = addrPool.GetUsbDevicePtr(0); + + if(!p) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nAddress not found"), 0x80); +#endif + return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; + } + + if(!p->epinfo) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nepinfo is null"), 0x80); +#endif + return USB_ERROR_EPINFO_IS_NULL; + } + + // Save old pointer to EP_RECORD of address 0 + oldep_ptr = p->epinfo; + + // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence + p->epinfo = epInfo; + + p->lowspeed = lowspeed; + + // Get device descriptor + rcode = pUsb->getDevDescr(0, 0, sizeof (USB_DEVICE_DESCRIPTOR), (uint8_t*)buf); // Get device descriptor - addr, ep, nbytes, data + // Restore p->epinfo + p->epinfo = oldep_ptr; + + if(rcode) + goto FailGetDevDescr; + + VID = udd->idVendor; + PID = udd->idProduct; + + if(VID != PS3_VID || (PID != PS3_PID && PID != PS3NAVIGATION_PID && PID != PS3MOVE_PID)) + goto FailUnknownDevice; + + // Allocate new address according to device class + bAddress = addrPool.AllocAddress(parent, false, port); + + if(!bAddress) + return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL; + + // Extract Max Packet Size from device descriptor + epInfo[0].maxPktSize = udd->bMaxPacketSize0; + + // Assign new address to the device + rcode = pUsb->setAddr(0, 0, bAddress); + if(rcode) { + p->lowspeed = false; + addrPool.FreeAddress(bAddress); + bAddress = 0; +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nsetAddr: "), 0x80); + D_PrintHex (rcode, 0x80); +#endif + return rcode; + } +#ifdef EXTRADEBUG + Notify(PSTR("\r\nAddr: "), 0x80); + D_PrintHex (bAddress, 0x80); +#endif + //delay(300); // Spec says you should wait at least 200ms + + p->lowspeed = false; + + //get pointer to assigned address record + p = addrPool.GetUsbDevicePtr(bAddress); + if(!p) + return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; + + p->lowspeed = lowspeed; + + // Assign epInfo to epinfo pointer - only EP0 is known + rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo); + if(rcode) + goto FailSetDevTblEntry; + + + /* The application will work in reduced host mode, so we can save program and data + memory space. After verifying the PID and VID we will use known values for the + configuration values for device, interface, endpoints and HID for the PS3 Controllers */ + + /* Initialize data structures for endpoints of device */ + epInfo[ PS3_OUTPUT_PIPE ].epAddr = 0x02; // PS3 output endpoint + epInfo[ PS3_OUTPUT_PIPE ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT; + epInfo[ PS3_OUTPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints + epInfo[ PS3_OUTPUT_PIPE ].maxPktSize = EP_MAXPKTSIZE; + epInfo[ PS3_OUTPUT_PIPE ].bmSndToggle = 0; + epInfo[ PS3_OUTPUT_PIPE ].bmRcvToggle = 0; + epInfo[ PS3_INPUT_PIPE ].epAddr = 0x01; // PS3 report endpoint + epInfo[ PS3_INPUT_PIPE ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT; + epInfo[ PS3_INPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints + epInfo[ PS3_INPUT_PIPE ].maxPktSize = EP_MAXPKTSIZE; + epInfo[ PS3_INPUT_PIPE ].bmSndToggle = 0; + epInfo[ PS3_INPUT_PIPE ].bmRcvToggle = 0; + + rcode = pUsb->setEpInfoEntry(bAddress, 3, epInfo); + if(rcode) + goto FailSetDevTblEntry; + + delay(200); //Give time for address change + + rcode = pUsb->setConf(bAddress, epInfo[ PS3_CONTROL_PIPE ].epAddr, 1); + if(rcode) + goto FailSetConfDescr; + + if(PID == PS3_PID || PID == PS3NAVIGATION_PID) { + if(PID == PS3_PID) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nDualshock 3 Controller Connected"), 0x80); +#endif + PS3Connected = true; + } else { // must be a navigation controller +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nNavigation Controller Connected"), 0x80); +#endif + PS3NavigationConnected = true; + } + enable_sixaxis(); // The PS3 controller needs a special command before it starts sending data + + // Needed for PS3 Dualshock and Navigation commands to work + for(uint8_t i = 0; i < PS3_REPORT_BUFFER_SIZE; i++) + writeBuf[i] = pgm_read_byte(&PS3_REPORT_BUFFER[i]); + + for(uint8_t i = 6; i < 10; i++) + readBuf[i] = 0x7F; // Set the analog joystick values to center position + } else { // must be a Motion controller +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nMotion Controller Connected"), 0x80); +#endif + PS3MoveConnected = true; + writeBuf[0] = 0x02; // Set report ID, this is needed for Move commands to work + } + if(my_bdaddr[0] != 0x00 || my_bdaddr[1] != 0x00 || my_bdaddr[2] != 0x00 || my_bdaddr[3] != 0x00 || my_bdaddr[4] != 0x00 || my_bdaddr[5] != 0x00) { + if(PS3MoveConnected) + setMoveBdaddr(my_bdaddr); // Set internal Bluetooth address + else + setBdaddr(my_bdaddr); // Set internal Bluetooth address + +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nBluetooth Address was set to: "), 0x80); + for(int8_t i = 5; i > 0; i--) { + D_PrintHex (my_bdaddr[i], 0x80); + Notify(PSTR(":"), 0x80); + } + D_PrintHex (my_bdaddr[0], 0x80); +#endif + } + onInit(); + + bPollEnable = true; + Notify(PSTR("\r\n"), 0x80); + timer = millis(); + return 0; // Successful configuration + + /* Diagnostic messages */ +FailGetDevDescr: +#ifdef DEBUG_USB_HOST + NotifyFailGetDevDescr(); + goto Fail; +#endif + +FailSetDevTblEntry: +#ifdef DEBUG_USB_HOST + NotifyFailSetDevTblEntry(); + goto Fail; +#endif + +FailSetConfDescr: +#ifdef DEBUG_USB_HOST + NotifyFailSetConfDescr(); +#endif + goto Fail; + +FailUnknownDevice: +#ifdef DEBUG_USB_HOST + NotifyFailUnknownDevice(VID, PID); +#endif + rcode = USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED; + +Fail: +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nPS3 Init Failed, error code: "), 0x80); + NotifyFail(rcode); +#endif + Release(); + return rcode; +} + +/* Performs a cleanup after failed Init() attempt */ +uint8_t PS3USB::Release() { + PS3Connected = false; + PS3MoveConnected = false; + PS3NavigationConnected = false; + pUsb->GetAddressPool().FreeAddress(bAddress); + bAddress = 0; + bPollEnable = false; + return 0; +} + +uint8_t PS3USB::Poll() { + if(!bPollEnable) + return 0; + + if(PS3Connected || PS3NavigationConnected) { + uint16_t BUFFER_SIZE = EP_MAXPKTSIZE; + pUsb->inTransfer(bAddress, epInfo[ PS3_INPUT_PIPE ].epAddr, &BUFFER_SIZE, readBuf); // input on endpoint 1 + if(millis() - timer > 100) { // Loop 100ms before processing data + readReport(); +#ifdef PRINTREPORT + printReport(); // Uncomment "#define PRINTREPORT" to print the report send by the PS3 Controllers +#endif + } + } else if(PS3MoveConnected) { // One can only set the color of the bulb, set the rumble, set and get the bluetooth address and calibrate the magnetometer via USB + if(millis() - timer > 4000) { // Send at least every 4th second + Move_Command(writeBuf, MOVE_REPORT_BUFFER_SIZE); // The Bulb and rumble values, has to be written again and again, for it to stay turned on + timer = millis(); + } + } + return 0; +} + +void PS3USB::readReport() { + ButtonState = (uint32_t)(readBuf[2] | ((uint16_t)readBuf[3] << 8) | ((uint32_t)readBuf[4] << 16)); + + //Notify(PSTR("\r\nButtonState", 0x80); + //PrintHex(ButtonState, 0x80); + + if(ButtonState != OldButtonState) { + ButtonClickState = ButtonState & ~OldButtonState; // Update click state variable + OldButtonState = ButtonState; + } +} + +void PS3USB::printReport() { // Uncomment "#define PRINTREPORT" to print the report send by the PS3 Controllers +#ifdef PRINTREPORT + for(uint8_t i = 0; i < PS3_REPORT_BUFFER_SIZE; i++) { + D_PrintHex (readBuf[i], 0x80); + Notify(PSTR(" "), 0x80); + } + Notify(PSTR("\r\n"), 0x80); +#endif +} + +bool PS3USB::getButtonPress(ButtonEnum b) { + return (ButtonState & pgm_read_dword(&PS3_BUTTONS[(uint8_t)b])); +} + +bool PS3USB::getButtonClick(ButtonEnum b) { + uint32_t button = pgm_read_dword(&PS3_BUTTONS[(uint8_t)b]); + bool click = (ButtonClickState & button); + ButtonClickState &= ~button; // Clear "click" event + return click; +} + +uint8_t PS3USB::getAnalogButton(ButtonEnum a) { + return (uint8_t)(readBuf[(pgm_read_byte(&PS3_ANALOG_BUTTONS[(uint8_t)a])) - 9]); +} + +uint8_t PS3USB::getAnalogHat(AnalogHatEnum a) { + return (uint8_t)(readBuf[((uint8_t)a + 6)]); +} + +uint16_t PS3USB::getSensor(SensorEnum a) { + return ((readBuf[((uint16_t)a) - 9] << 8) | readBuf[((uint16_t)a + 1) - 9]); +} + +double PS3USB::getAngle(AngleEnum a) { + if(PS3Connected) { + double accXval; + double accYval; + double accZval; + + // Data for the Kionix KXPC4 used in the DualShock 3 + const double zeroG = 511.5; // 1.65/3.3*1023 (1,65V) + accXval = -((double)getSensor(aX) - zeroG); + accYval = -((double)getSensor(aY) - zeroG); + accZval = -((double)getSensor(aZ) - zeroG); + + // Convert to 360 degrees resolution + // atan2 outputs the value of -Ï€ to Ï€ (radians) + // We are then converting it to 0 to 2Ï€ and then to degrees + if(a == Pitch) + return (atan2(accYval, accZval) + PI) * RAD_TO_DEG; + else + return (atan2(accXval, accZval) + PI) * RAD_TO_DEG; + } else + return 0; +} + +bool PS3USB::getStatus(StatusEnum c) { + return (readBuf[((uint16_t)c >> 8) - 9] == ((uint8_t)c & 0xff)); +} + +void PS3USB::printStatusString() { + char statusOutput[100]; // Max string length plus null character + if(PS3Connected || PS3NavigationConnected) { + strcpy_P(statusOutput, PSTR("ConnectionStatus: ")); + + if(getStatus(Plugged)) strcat_P(statusOutput, PSTR("Plugged")); + else if(getStatus(Unplugged)) strcat_P(statusOutput, PSTR("Unplugged")); + else strcat_P(statusOutput, PSTR("Error")); + + strcat_P(statusOutput, PSTR(" - PowerRating: ")); + + if(getStatus(Charging)) strcat_P(statusOutput, PSTR("Charging")); + else if(getStatus(NotCharging)) strcat_P(statusOutput, PSTR("Not Charging")); + else if(getStatus(Shutdown)) strcat_P(statusOutput, PSTR("Shutdown")); + else if(getStatus(Dying)) strcat_P(statusOutput, PSTR("Dying")); + else if(getStatus(Low)) strcat_P(statusOutput, PSTR("Low")); + else if(getStatus(High)) strcat_P(statusOutput, PSTR("High")); + else if(getStatus(Full)) strcat_P(statusOutput, PSTR("Full")); + else strcat_P(statusOutput, PSTR("Error")); + + strcat_P(statusOutput, PSTR(" - WirelessStatus: ")); + + if(getStatus(CableRumble)) strcat_P(statusOutput, PSTR("Cable - Rumble is on")); + else if(getStatus(Cable)) strcat_P(statusOutput, PSTR("Cable - Rumble is off")); + else if(getStatus(BluetoothRumble)) strcat_P(statusOutput, PSTR("Bluetooth - Rumble is on")); + else if(getStatus(Bluetooth)) strcat_P(statusOutput, PSTR("Bluetooth - Rumble is off")); + else strcat_P(statusOutput, PSTR("Error")); + } else + strcpy_P(statusOutput, PSTR("Error")); + + USB_HOST_SERIAL.write(statusOutput); +} + +/* Playstation Sixaxis Dualshock and Navigation Controller commands */ +void PS3USB::PS3_Command(uint8_t *data, uint16_t nbytes) { + // bmRequest = Host to device (0x00) | Class (0x20) | Interface (0x01) = 0x21, bRequest = Set Report (0x09), Report ID (0x01), Report Type (Output 0x02), interface (0x00), datalength, datalength, data) + pUsb->ctrlReq(bAddress, epInfo[PS3_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0x01, 0x02, 0x00, nbytes, nbytes, data, NULL); +} + +void PS3USB::setAllOff() { + for(uint8_t i = 0; i < PS3_REPORT_BUFFER_SIZE; i++) + writeBuf[i] = pgm_read_byte(&PS3_REPORT_BUFFER[i]); // Reset buffer + + PS3_Command(writeBuf, PS3_REPORT_BUFFER_SIZE); +} + +void PS3USB::setRumbleOff() { + writeBuf[1] = 0x00; + writeBuf[2] = 0x00; // Low mode off + writeBuf[3] = 0x00; + writeBuf[4] = 0x00; // High mode off + + PS3_Command(writeBuf, PS3_REPORT_BUFFER_SIZE); +} + +void PS3USB::setRumbleOn(RumbleEnum mode) { + if((mode & 0x30) > 0x00) { + uint8_t power[2] = {0xff, 0x00}; // Defaults to RumbleLow + if(mode == RumbleHigh) { + power[0] = 0x00; + power[1] = 0xff; + } + setRumbleOn(0xfe, power[0], 0xfe, power[1]); + } +} + +void PS3USB::setRumbleOn(uint8_t rightDuration, uint8_t rightPower, uint8_t leftDuration, uint8_t leftPower) { + writeBuf[1] = rightDuration; + writeBuf[2] = rightPower; + writeBuf[3] = leftDuration; + writeBuf[4] = leftPower; + PS3_Command(writeBuf, PS3_REPORT_BUFFER_SIZE); +} + +void PS3USB::setLedRaw(uint8_t value) { + writeBuf[9] = value << 1; + PS3_Command(writeBuf, PS3_REPORT_BUFFER_SIZE); +} + +void PS3USB::setLedOff(LEDEnum a) { + writeBuf[9] &= ~((uint8_t)((pgm_read_byte(&PS3_LEDS[(uint8_t)a]) & 0x0f) << 1)); + PS3_Command(writeBuf, PS3_REPORT_BUFFER_SIZE); +} + +void PS3USB::setLedOn(LEDEnum a) { + if(a == OFF) + setLedRaw(0); + else { + writeBuf[9] |= (uint8_t)((pgm_read_byte(&PS3_LEDS[(uint8_t)a]) & 0x0f) << 1); + PS3_Command(writeBuf, PS3_REPORT_BUFFER_SIZE); + } +} + +void PS3USB::setLedToggle(LEDEnum a) { + writeBuf[9] ^= (uint8_t)((pgm_read_byte(&PS3_LEDS[(uint8_t)a]) & 0x0f) << 1); + PS3_Command(writeBuf, PS3_REPORT_BUFFER_SIZE); +} + +void PS3USB::setBdaddr(uint8_t *bdaddr) { + /* Set the internal Bluetooth address */ + uint8_t buf[8]; + buf[0] = 0x01; + buf[1] = 0x00; + + for(uint8_t i = 0; i < 6; i++) + buf[i + 2] = bdaddr[5 - i]; // Copy into buffer, has to be written reversed, so it is MSB first + + // bmRequest = Host to device (0x00) | Class (0x20) | Interface (0x01) = 0x21, bRequest = Set Report (0x09), Report ID (0xF5), Report Type (Feature 0x03), interface (0x00), datalength, datalength, data + pUsb->ctrlReq(bAddress, epInfo[PS3_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0xF5, 0x03, 0x00, 8, 8, buf, NULL); +} + +void PS3USB::getBdaddr(uint8_t *bdaddr) { + uint8_t buf[8]; + + // bmRequest = Device to host (0x80) | Class (0x20) | Interface (0x01) = 0xA1, bRequest = Get Report (0x01), Report ID (0xF5), Report Type (Feature 0x03), interface (0x00), datalength, datalength, data + pUsb->ctrlReq(bAddress, epInfo[PS3_CONTROL_PIPE].epAddr, bmREQ_HID_IN, HID_REQUEST_GET_REPORT, 0xF5, 0x03, 0x00, 8, 8, buf, NULL); + + for(uint8_t i = 0; i < 6; i++) + bdaddr[5 - i] = buf[i + 2]; // Copy into buffer reversed, so it is LSB first +} + +void PS3USB::enable_sixaxis() { // Command used to enable the Dualshock 3 and Navigation controller to send data via USB + uint8_t cmd_buf[4]; + cmd_buf[0] = 0x42; // Special PS3 Controller enable commands + cmd_buf[1] = 0x0c; + cmd_buf[2] = 0x00; + cmd_buf[3] = 0x00; + + // bmRequest = Host to device (0x00) | Class (0x20) | Interface (0x01) = 0x21, bRequest = Set Report (0x09), Report ID (0xF4), Report Type (Feature 0x03), interface (0x00), datalength, datalength, data) + pUsb->ctrlReq(bAddress, epInfo[PS3_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0xF4, 0x03, 0x00, 4, 4, cmd_buf, NULL); +} + +/* Playstation Move Controller commands */ +void PS3USB::Move_Command(uint8_t *data, uint16_t nbytes) { + pUsb->outTransfer(bAddress, epInfo[ PS3_OUTPUT_PIPE ].epAddr, nbytes, data); +} + +void PS3USB::moveSetBulb(uint8_t r, uint8_t g, uint8_t b) { // Use this to set the Color using RGB values + // Set the Bulb's values into the write buffer + writeBuf[2] = r; + writeBuf[3] = g; + writeBuf[4] = b; + + Move_Command(writeBuf, MOVE_REPORT_BUFFER_SIZE); +} + +void PS3USB::moveSetBulb(ColorsEnum color) { // Use this to set the Color using the predefined colors in "enums.h" + moveSetBulb((uint8_t)(color >> 16), (uint8_t)(color >> 8), (uint8_t)(color)); +} + +void PS3USB::moveSetRumble(uint8_t rumble) { +#ifdef DEBUG_USB_HOST + if(rumble < 64 && rumble != 0) // The rumble value has to at least 64, or approximately 25% (64/255*100) + Notify(PSTR("\r\nThe rumble value has to at least 64, or approximately 25%"), 0x80); +#endif + writeBuf[6] = rumble; // Set the rumble value into the write buffer + + Move_Command(writeBuf, MOVE_REPORT_BUFFER_SIZE); +} + +void PS3USB::setMoveBdaddr(uint8_t *bdaddr) { + /* Set the internal Bluetooth address */ + uint8_t buf[11]; + buf[0] = 0x05; + buf[7] = 0x10; + buf[8] = 0x01; + buf[9] = 0x02; + buf[10] = 0x12; + + for(uint8_t i = 0; i < 6; i++) + buf[i + 1] = bdaddr[i]; + + // bmRequest = Host to device (0x00) | Class (0x20) | Interface (0x01) = 0x21, bRequest = Set Report (0x09), Report ID (0x05), Report Type (Feature 0x03), interface (0x00), datalength, datalength, data + pUsb->ctrlReq(bAddress, epInfo[PS3_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0x05, 0x03, 0x00, 11, 11, buf, NULL); +} + +void PS3USB::getMoveBdaddr(uint8_t *bdaddr) { + uint8_t buf[16]; + + // bmRequest = Device to host (0x80) | Class (0x20) | Interface (0x01) = 0xA1, bRequest = Get Report (0x01), Report ID (0x04), Report Type (Feature 0x03), interface (0x00), datalength, datalength, data + pUsb->ctrlReq(bAddress, epInfo[PS3_CONTROL_PIPE].epAddr, bmREQ_HID_IN, HID_REQUEST_GET_REPORT, 0x04, 0x03, 0x00, 16, 16, buf, NULL); + + for(uint8_t i = 0; i < 6; i++) + bdaddr[i] = buf[10 + i]; +} + +void PS3USB::getMoveCalibration(uint8_t *data) { + uint8_t buf[49]; + + for(uint8_t i = 0; i < 3; i++) { + // bmRequest = Device to host (0x80) | Class (0x20) | Interface (0x01) = 0xA1, bRequest = Get Report (0x01), Report ID (0x10), Report Type (Feature 0x03), interface (0x00), datalength, datalength, data + pUsb->ctrlReq(bAddress, epInfo[PS3_CONTROL_PIPE].epAddr, bmREQ_HID_IN, HID_REQUEST_GET_REPORT, 0x10, 0x03, 0x00, 49, 49, buf, NULL); + + for(byte j = 0; j < 49; j++) + data[49 * i + j] = buf[j]; + } +} + +void PS3USB::onInit() { + if(pFuncOnInit) + pFuncOnInit(); // Call the user function + else { + if(PS3MoveConnected) + moveSetBulb(Red); + else // Dualshock 3 or Navigation controller + setLedOn(LED1); + } +} diff --git a/libraries/USB_Host_Shield/PS3USB.h b/libraries/USB_Host_Shield/PS3USB.h new file mode 100755 index 0000000..2eba925 --- /dev/null +++ b/libraries/USB_Host_Shield/PS3USB.h @@ -0,0 +1,303 @@ +/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved. + + This software may be distributed and modified under the terms of the GNU + General Public License version 2 (GPL2) as published by the Free Software + Foundation and appearing in the file GPL2.TXT included in the packaging of + this file. Please note that GPL2 Section 2[b] requires that all works based + on this software must also be made publicly available under the terms of + the GPL2 ("Copyleft"). + + Contact information + ------------------- + + Kristian Lauszus, TKJ Electronics + Web : http://www.tkjelectronics.com + e-mail : kristianl@tkjelectronics.com + */ + +#ifndef _ps3usb_h_ +#define _ps3usb_h_ + +#include "Usb.h" +#include "hid.h" +#include "PS3Enums.h" + +/* PS3 data taken from descriptors */ +#define EP_MAXPKTSIZE 64 // max size for data via USB + +/* Names we give to the 3 ps3 pipes - this is only used for setting the bluetooth address into the ps3 controllers */ +#define PS3_CONTROL_PIPE 0 +#define PS3_OUTPUT_PIPE 1 +#define PS3_INPUT_PIPE 2 + +//PID and VID of the different devices +#define PS3_VID 0x054C // Sony Corporation +#define PS3_PID 0x0268 // PS3 Controller DualShock 3 +#define PS3NAVIGATION_PID 0x042F // Navigation controller +#define PS3MOVE_PID 0x03D5 // Motion controller + +#define PS3_MAX_ENDPOINTS 3 + +/** + * This class implements support for all the official PS3 Controllers: + * Dualshock 3, Navigation or a Motion controller via USB. + * + * One can only set the color of the bulb, set the rumble, set and get the bluetooth address and calibrate the magnetometer via USB on the Move controller. + * + * Information about the protocol can be found at the wiki: https://github.com/felis/USB_Host_Shield_2.0/wiki/PS3-Information. + */ +class PS3USB : public USBDeviceConfig { +public: + /** + * Constructor for the PS3USB class. + * @param pUsb Pointer to USB class instance. + * @param btadr5,btadr4,btadr3,btadr2,btadr1,btadr0 + * Pass your dongles Bluetooth address into the constructor, + * so you are able to pair the controller with a Bluetooth dongle. + */ + PS3USB(USB *pUsb, uint8_t btadr5 = 0, uint8_t btadr4 = 0, uint8_t btadr3 = 0, uint8_t btadr2 = 0, uint8_t btadr1 = 0, uint8_t btadr0 = 0); + + /** @name USBDeviceConfig implementation */ + /** + * Initialize the PS3 Controller. + * @param parent Hub number. + * @param port Port number on the hub. + * @param lowspeed Speed of the device. + * @return 0 on success. + */ + uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed); + /** + * Release the USB device. + * @return 0 on success. + */ + uint8_t Release(); + /** + * Poll the USB Input endpoins and run the state machines. + * @return 0 on success. + */ + uint8_t Poll(); + + /** + * Get the device address. + * @return The device address. + */ + virtual uint8_t GetAddress() { + return bAddress; + }; + + /** + * Used to check if the controller has been initialized. + * @return True if it's ready. + */ + virtual bool isReady() { + return bPollEnable; + }; + + /** + * Used by the USB core to check what this driver support. + * @param vid The device's VID. + * @param pid The device's PID. + * @return Returns true if the device's VID and PID matches this driver. + */ + virtual bool VIDPIDOK(uint16_t vid, uint16_t pid) { + return (vid == PS3_VID && (pid == PS3_PID || pid == PS3NAVIGATION_PID || pid == PS3MOVE_PID)); + }; + /**@}*/ + + /** + * Used to set the Bluetooth address inside the Dualshock 3 and Navigation controller. + * Set using LSB first. + * @param bdaddr Your dongles Bluetooth address. + */ + void setBdaddr(uint8_t *bdaddr); + /** + * Used to get the Bluetooth address inside the Dualshock 3 and Navigation controller. + * Will return LSB first. + * @param bdaddr Your dongles Bluetooth address. + */ + void getBdaddr(uint8_t *bdaddr); + + /** + * Used to set the Bluetooth address inside the Move controller. + * Set using LSB first. + * @param bdaddr Your dongles Bluetooth address. + */ + void setMoveBdaddr(uint8_t *bdaddr); + /** + * Used to get the Bluetooth address inside the Move controller. + * Will return LSB first. + * @param bdaddr Your dongles Bluetooth address. + */ + void getMoveBdaddr(uint8_t *bdaddr); + /** + * Used to get the calibration data inside the Move controller. + * @param data Buffer to store data in. Must be at least 147 bytes + */ + void getMoveCalibration(uint8_t *data); + + /** @name PS3 Controller functions */ + /** + * getButtonPress(ButtonEnum b) will return true as long as the button is held down. + * + * While getButtonClick(ButtonEnum b) will only return it once. + * + * So you instance if you need to increase a variable once you would use getButtonClick(ButtonEnum b), + * but if you need to drive a robot forward you would use getButtonPress(ButtonEnum b). + * @param b ::ButtonEnum to read. + * @return getButtonPress(ButtonEnum b) will return a true as long as a button is held down, while getButtonClick(ButtonEnum b) will return true once for each button press. + */ + bool getButtonPress(ButtonEnum b); + bool getButtonClick(ButtonEnum b); + /**@}*/ + /** @name PS3 Controller functions */ + /** + * Used to get the analog value from button presses. + * @param a The ::ButtonEnum to read. + * The supported buttons are: + * ::UP, ::RIGHT, ::DOWN, ::LEFT, ::L1, ::L2, ::R1, ::R2, + * ::TRIANGLE, ::CIRCLE, ::CROSS, ::SQUARE, and ::T. + * @return Analog value in the range of 0-255. + */ + uint8_t getAnalogButton(ButtonEnum a); + /** + * Used to read the analog joystick. + * @param a ::LeftHatX, ::LeftHatY, ::RightHatX, and ::RightHatY. + * @return Return the analog value in the range of 0-255. + */ + uint8_t getAnalogHat(AnalogHatEnum a); + /** + * Used to read the sensors inside the Dualshock 3 controller. + * @param a + * The Dualshock 3 has a 3-axis accelerometer and a 1-axis gyro inside. + * @return Return the raw sensor value. + */ + uint16_t getSensor(SensorEnum a); + /** + * Use this to get ::Pitch and ::Roll calculated using the accelerometer. + * @param a Either ::Pitch or ::Roll. + * @return Return the angle in the range of 0-360. + */ + double getAngle(AngleEnum a); + /** + * Get the ::StatusEnum from the controller. + * @param c The ::StatusEnum you want to read. + * @return True if correct and false if not. + */ + bool getStatus(StatusEnum c); + /** Read all the available statuses from the controller and prints it as a nice formated string. */ + void printStatusString(); + + /** Used to set all LEDs and rumble off. */ + void setAllOff(); + /** Turn off rumble. */ + void setRumbleOff(); + /** + * Turn on rumble. + * @param mode Either ::RumbleHigh or ::RumbleLow. + */ + void setRumbleOn(RumbleEnum mode); + /** + * Turn on rumble using custom duration and power. + * @param rightDuration The duration of the right/low rumble effect. + * @param rightPower The intensity of the right/low rumble effect. + * @param leftDuration The duration of the left/high rumble effect. + * @param leftPower The intensity of the left/high rumble effect. + */ + void setRumbleOn(uint8_t rightDuration, uint8_t rightPower, uint8_t leftDuration, uint8_t leftPower); + + /** + * Set LED value without using the ::LEDEnum. + * @param value See: ::LEDEnum. + */ + void setLedRaw(uint8_t value); + + /** Turn all LEDs off. */ + void setLedOff() { + setLedRaw(0); + } + /** + * Turn the specific ::LEDEnum off. + * @param a The ::LEDEnum to turn off. + */ + void setLedOff(LEDEnum a); + /** + * Turn the specific ::LEDEnum on. + * @param a The ::LEDEnum to turn on. + */ + void setLedOn(LEDEnum a); + /** + * Toggle the specific ::LEDEnum. + * @param a The ::LEDEnum to toggle. + */ + void setLedToggle(LEDEnum a); + + /** + * Use this to set the Color using RGB values. + * @param r,g,b RGB value. + */ + void moveSetBulb(uint8_t r, uint8_t g, uint8_t b); + /** + * Use this to set the color using the predefined colors in ::ColorsEnum. + * @param color The desired color. + */ + void moveSetBulb(ColorsEnum color); + /** + * Set the rumble value inside the Move controller. + * @param rumble The desired value in the range from 64-255. + */ + void moveSetRumble(uint8_t rumble); + + /** + * Used to call your own function when the controller is successfully initialized. + * @param funcOnInit Function to call. + */ + void attachOnInit(void (*funcOnInit)(void)) { + pFuncOnInit = funcOnInit; + }; + /**@}*/ + + /** Variable used to indicate if the normal playstation controller is successfully connected. */ + bool PS3Connected; + /** Variable used to indicate if the move controller is successfully connected. */ + bool PS3MoveConnected; + /** Variable used to indicate if the navigation controller is successfully connected. */ + bool PS3NavigationConnected; + +protected: + /** Pointer to USB class instance. */ + USB *pUsb; + /** Device address. */ + uint8_t bAddress; + /** Endpoint info structure. */ + EpInfo epInfo[PS3_MAX_ENDPOINTS]; + +private: + /** + * Called when the controller is successfully initialized. + * Use attachOnInit(void (*funcOnInit)(void)) to call your own function. + * This is useful for instance if you want to set the LEDs in a specific way. + */ + void onInit(); + void (*pFuncOnInit)(void); // Pointer to function called in onInit() + + bool bPollEnable; + + uint32_t timer; // used to continuously set PS3 Move controller Bulb and rumble values + + uint32_t ButtonState; + uint32_t OldButtonState; + uint32_t ButtonClickState; + + uint8_t my_bdaddr[6]; // Change to your dongles Bluetooth address in the constructor + uint8_t readBuf[EP_MAXPKTSIZE]; // General purpose buffer for input data + uint8_t writeBuf[EP_MAXPKTSIZE]; // General purpose buffer for output data + + void readReport(); // read incoming data + void printReport(); // print incoming date - Uncomment for debugging + + /* Private commands */ + void PS3_Command(uint8_t *data, uint16_t nbytes); + void enable_sixaxis(); // Command used to enable the Dualshock 3 and Navigation controller to send data via USB + void Move_Command(uint8_t *data, uint16_t nbytes); +}; +#endif diff --git a/libraries/USB_Host_Shield/PS4BT.h b/libraries/USB_Host_Shield/PS4BT.h new file mode 100755 index 0000000..b7eb4b5 --- /dev/null +++ b/libraries/USB_Host_Shield/PS4BT.h @@ -0,0 +1,121 @@ +/* Copyright (C) 2014 Kristian Lauszus, TKJ Electronics. All rights reserved. + + This software may be distributed and modified under the terms of the GNU + General Public License version 2 (GPL2) as published by the Free Software + Foundation and appearing in the file GPL2.TXT included in the packaging of + this file. Please note that GPL2 Section 2[b] requires that all works based + on this software must also be made publicly available under the terms of + the GPL2 ("Copyleft"). + + Contact information + ------------------- + + Kristian Lauszus, TKJ Electronics + Web : http://www.tkjelectronics.com + e-mail : kristianl@tkjelectronics.com + */ + +#ifndef _ps4bt_h_ +#define _ps4bt_h_ + +#include "BTHID.h" +#include "PS4Parser.h" + +/** + * This class implements support for the PS4 controller via Bluetooth. + * It uses the BTHID class for all the Bluetooth communication. + */ +class PS4BT : public BTHID, public PS4Parser { +public: + /** + * Constructor for the PS4BT class. + * @param p Pointer to the BTD class instance. + * @param pair Set this to true in order to pair with the device. If the argument is omitted then it will not pair with it. One can use ::PAIR to set it to true. + * @param pin Write the pin to BTD#btdPin. If argument is omitted, then "0000" will be used. + */ + PS4BT(BTD *p, bool pair = false, const char *pin = "0000") : + BTHID(p, pair, pin) { + PS4Parser::Reset(); + }; + + /** + * Used to check if a PS4 controller is connected. + * @return Returns true if it is connected. + */ + bool connected() { + return BTHID::connected; + }; + +protected: + /** @name BTHID implementation */ + /** + * Used to parse Bluetooth HID data. + * @param len The length of the incoming data. + * @param buf Pointer to the data buffer. + */ + virtual void ParseBTHIDData(uint8_t len, uint8_t *buf) { + PS4Parser::Parse(len, buf); + }; + + /** + * Called when a device is successfully initialized. + * Use attachOnInit(void (*funcOnInit)(void)) to call your own function. + * This is useful for instance if you want to set the LEDs in a specific way. + */ + virtual void OnInitBTHID() { + PS4Parser::Reset(); + enable_sixaxis(); // Make the controller send out the entire output report + if (pFuncOnInit) + pFuncOnInit(); // Call the user function + else + setLed(Blue); + }; + + /** Used to reset the different buffers to there default values */ + virtual void ResetBTHID() { + PS4Parser::Reset(); + }; + /**@}*/ + + /** @name PS4Parser implementation */ + virtual void sendOutputReport(PS4Output *output) { // Source: https://github.com/chrippa/ds4drv + uint8_t buf[79]; + memset(buf, 0, sizeof(buf)); + + buf[0] = 0x52; // HID BT Set_report (0x50) | Report Type (Output 0x02) + buf[1] = 0x11; // Report ID + buf[2] = 0x80; + buf[4]= 0xFF; + + buf[7] = output->smallRumble; // Small Rumble + buf[8] = output->bigRumble; // Big rumble + + buf[9] = output->r; // Red + buf[10] = output->g; // Green + buf[11] = output->b; // Blue + + buf[12] = output->flashOn; // Time to flash bright (255 = 2.5 seconds) + buf[13] = output->flashOff; // Time to flash dark (255 = 2.5 seconds) + + output->reportChanged = false; + + // The PS4 console actually set the four last bytes to a CRC32 checksum, but it seems like it is actually not needed + + HID_Command(buf, sizeof(buf)); + }; + /**@}*/ + +private: + void enable_sixaxis() { // Command used to make the PS4 controller send out the entire output report + uint8_t buf[2]; + buf[0] = 0x43; // HID BT Get_report (0x40) | Report Type (Feature 0x03) + buf[1] = 0x02; // Report ID + + HID_Command(buf, 2); + }; + + void HID_Command(uint8_t *data, uint8_t nbytes) { + pBtd->L2CAP_Command(hci_handle, data, nbytes, control_scid[0], control_scid[1]); + }; +}; +#endif diff --git a/libraries/USB_Host_Shield/PS4Parser.cpp b/libraries/USB_Host_Shield/PS4Parser.cpp new file mode 100755 index 0000000..106d04e --- /dev/null +++ b/libraries/USB_Host_Shield/PS4Parser.cpp @@ -0,0 +1,109 @@ +/* Copyright (C) 2014 Kristian Lauszus, TKJ Electronics. All rights reserved. + + This software may be distributed and modified under the terms of the GNU + General Public License version 2 (GPL2) as published by the Free Software + Foundation and appearing in the file GPL2.TXT included in the packaging of + this file. Please note that GPL2 Section 2[b] requires that all works based + on this software must also be made publicly available under the terms of + the GPL2 ("Copyleft"). + + Contact information + ------------------- + + Kristian Lauszus, TKJ Electronics + Web : http://www.tkjelectronics.com + e-mail : kristianl@tkjelectronics.com + */ + +#include "PS4Parser.h" + +// To enable serial debugging see "settings.h" +//#define PRINTREPORT // Uncomment to print the report send by the PS4 Controller + +bool PS4Parser::checkDpad(ButtonEnum b) { + switch (b) { + case UP: + return ps4Data.btn.dpad == DPAD_LEFT_UP || ps4Data.btn.dpad == DPAD_UP || ps4Data.btn.dpad == DPAD_UP_RIGHT; + case RIGHT: + return ps4Data.btn.dpad == DPAD_UP_RIGHT || ps4Data.btn.dpad == DPAD_RIGHT || ps4Data.btn.dpad == DPAD_RIGHT_DOWN; + case DOWN: + return ps4Data.btn.dpad == DPAD_RIGHT_DOWN || ps4Data.btn.dpad == DPAD_DOWN || ps4Data.btn.dpad == DPAD_DOWN_LEFT; + case LEFT: + return ps4Data.btn.dpad == DPAD_DOWN_LEFT || ps4Data.btn.dpad == DPAD_LEFT || ps4Data.btn.dpad == DPAD_LEFT_UP; + default: + return false; + } +} + +bool PS4Parser::getButtonPress(ButtonEnum b) { + if (b <= LEFT) // Dpad + return checkDpad(b); + else + return ps4Data.btn.val & (1UL << pgm_read_byte(&PS4_BUTTONS[(uint8_t)b])); +} + +bool PS4Parser::getButtonClick(ButtonEnum b) { + uint32_t mask = 1UL << pgm_read_byte(&PS4_BUTTONS[(uint8_t)b]); + bool click = buttonClickState.val & mask; + buttonClickState.val &= ~mask; // Clear "click" event + return click; +} + +uint8_t PS4Parser::getAnalogButton(ButtonEnum b) { + if (b == L2) // These are the only analog buttons on the controller + return ps4Data.trigger[0]; + else if (b == R2) + return ps4Data.trigger[1]; + return 0; +} + +uint8_t PS4Parser::getAnalogHat(AnalogHatEnum a) { + return ps4Data.hatValue[(uint8_t)a]; +} + +void PS4Parser::Parse(uint8_t len, uint8_t *buf) { + if (len > 0 && buf) { +#ifdef PRINTREPORT + Notify(PSTR("\r\n"), 0x80); + for (uint8_t i = 0; i < len; i++) { + D_PrintHex (buf[i], 0x80); + Notify(PSTR(" "), 0x80); + } +#endif + + if (buf[0] == 0x01) // Check report ID + memcpy(&ps4Data, buf + 1, min(len - 1, sizeof(ps4Data))); + else if (buf[0] == 0x11) // This report is send via Bluetooth, it has an offset of 2 compared to the USB data + memcpy(&ps4Data, buf + 3, min(len - 3, sizeof(ps4Data))); + else { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nUnknown report id: "), 0x80); + D_PrintHex (buf[0], 0x80); +#endif + return; + } + + if (ps4Data.btn.val != oldButtonState.val) { // Check if anything has changed + buttonClickState.val = ps4Data.btn.val & ~oldButtonState.val; // Update click state variable + oldButtonState.val = ps4Data.btn.val; + + // The DPAD buttons does not set the different bits, but set a value corresponding to the buttons pressed, we will simply set the bits ourself + uint8_t newDpad = 0; + if (checkDpad(UP)) + newDpad |= 1 << UP; + if (checkDpad(RIGHT)) + newDpad |= 1 << RIGHT; + if (checkDpad(DOWN)) + newDpad |= 1 << DOWN; + if (checkDpad(LEFT)) + newDpad |= 1 << LEFT; + if (newDpad != oldDpad) { + buttonClickState.dpad = newDpad & ~oldDpad; // Override values + oldDpad = newDpad; + } + } + } + + if (ps4Output.reportChanged) + sendOutputReport(&ps4Output); // Send output report +} diff --git a/libraries/USB_Host_Shield/PS4Parser.h b/libraries/USB_Host_Shield/PS4Parser.h new file mode 100755 index 0000000..51f0806 --- /dev/null +++ b/libraries/USB_Host_Shield/PS4Parser.h @@ -0,0 +1,407 @@ +/* Copyright (C) 2014 Kristian Lauszus, TKJ Electronics. All rights reserved. + + This software may be distributed and modified under the terms of the GNU + General Public License version 2 (GPL2) as published by the Free Software + Foundation and appearing in the file GPL2.TXT included in the packaging of + this file. Please note that GPL2 Section 2[b] requires that all works based + on this software must also be made publicly available under the terms of + the GPL2 ("Copyleft"). + + Contact information + ------------------- + + Kristian Lauszus, TKJ Electronics + Web : http://www.tkjelectronics.com + e-mail : kristianl@tkjelectronics.com + */ + +#ifndef _ps4parser_h_ +#define _ps4parser_h_ + +#include "Usb.h" +#include "controllerEnums.h" + +/** Buttons on the controller */ +const uint8_t PS4_BUTTONS[] PROGMEM = { + UP, // UP + RIGHT, // RIGHT + DOWN, // DOWN + LEFT, // LEFT + + 0x0C, // SHARE + 0x0D, // OPTIONS + 0x0E, // L3 + 0x0F, // R3 + + 0x0A, // L2 + 0x0B, // R2 + 0x08, // L1 + 0x09, // R1 + + 0x07, // TRIANGLE + 0x06, // CIRCLE + 0x05, // CROSS + 0x04, // SQUARE + + 0x10, // PS + 0x11, // TOUCHPAD +}; + +union PS4Buttons { + struct { + uint8_t dpad : 4; + uint8_t square : 1; + uint8_t cross : 1; + uint8_t circle : 1; + uint8_t triangle : 1; + + uint8_t l1 : 1; + uint8_t r1 : 1; + uint8_t l2 : 1; + uint8_t r2 : 1; + uint8_t share : 1; + uint8_t options : 1; + uint8_t l3 : 1; + uint8_t r3 : 1; + + uint8_t ps : 1; + uint8_t touchpad : 1; + uint8_t reportCounter : 6; + } __attribute__((packed)); + uint32_t val : 24; +} __attribute__((packed)); + +struct touchpadXY { + uint8_t dummy; // I can not figure out what this data is for, it seems to change randomly, maybe a timestamp? + struct { + uint8_t counter : 7; // Increments every time a finger is touching the touchpad + uint8_t touching : 1; // The top bit is cleared if the finger is touching the touchpad + uint16_t x : 12; + uint16_t y : 12; + } __attribute__((packed)) finger[2]; // 0 = first finger, 1 = second finger +} __attribute__((packed)); + +struct PS4Status { + uint8_t battery : 4; + uint8_t usb : 1; + uint8_t audio : 1; + uint8_t mic : 1; + uint8_t unknown : 1; // Extension port? +} __attribute__((packed)); + +struct PS4Data { + /* Button and joystick values */ + uint8_t hatValue[4]; + PS4Buttons btn; + uint8_t trigger[2]; + + /* Gyro and accelerometer values */ + uint8_t dummy[3]; // First two looks random, while the third one might be some kind of status - it increments once in a while + int16_t gyroY, gyroZ, gyroX; + int16_t accX, accZ, accY; + + uint8_t dummy2[5]; + PS4Status status; + uint8_t dummy3[3]; + + /* The rest is data for the touchpad */ + touchpadXY xy[3]; // It looks like it sends out three coordinates each time, this might be because the microcontroller inside the PS4 controller is much faster than the Bluetooth connection. + // The last data is read from the last position in the array while the oldest measurement is from the first position. + // The first position will also keep it's value after the finger is released, while the other two will set them to zero. + // Note that if you read fast enough from the device, then only the first one will contain any data. + + // The last three bytes are always: 0x00, 0x80, 0x00 +} __attribute__((packed)); + +struct PS4Output { + uint8_t bigRumble, smallRumble; // Rumble + uint8_t r, g, b; // RGB + uint8_t flashOn, flashOff; // Time to flash bright/dark (255 = 2.5 seconds) + bool reportChanged; // The data is send when data is received from the controller +} __attribute__((packed)); + +enum DPADEnum { + DPAD_UP = 0x0, + DPAD_UP_RIGHT = 0x1, + DPAD_RIGHT = 0x2, + DPAD_RIGHT_DOWN = 0x3, + DPAD_DOWN = 0x4, + DPAD_DOWN_LEFT = 0x5, + DPAD_LEFT = 0x6, + DPAD_LEFT_UP = 0x7, + DPAD_OFF = 0x8, +}; + +/** This class parses all the data sent by the PS4 controller */ +class PS4Parser { +public: + /** Constructor for the PS4Parser class. */ + PS4Parser() { + Reset(); + }; + + /** @name PS4 Controller functions */ + /** + * getButtonPress(ButtonEnum b) will return true as long as the button is held down. + * + * While getButtonClick(ButtonEnum b) will only return it once. + * + * So you instance if you need to increase a variable once you would use getButtonClick(ButtonEnum b), + * but if you need to drive a robot forward you would use getButtonPress(ButtonEnum b). + * @param b ::ButtonEnum to read. + * @return getButtonPress(ButtonEnum b) will return a true as long as a button is held down, while getButtonClick(ButtonEnum b) will return true once for each button press. + */ + bool getButtonPress(ButtonEnum b); + bool getButtonClick(ButtonEnum b); + /**@}*/ + /** @name PS4 Controller functions */ + /** + * Used to get the analog value from button presses. + * @param b The ::ButtonEnum to read. + * The supported buttons are: + * ::UP, ::RIGHT, ::DOWN, ::LEFT, ::L1, ::L2, ::R1, ::R2, + * ::TRIANGLE, ::CIRCLE, ::CROSS, ::SQUARE, and ::T. + * @return Analog value in the range of 0-255. + */ + uint8_t getAnalogButton(ButtonEnum b); + + /** + * Used to read the analog joystick. + * @param a ::LeftHatX, ::LeftHatY, ::RightHatX, and ::RightHatY. + * @return Return the analog value in the range of 0-255. + */ + uint8_t getAnalogHat(AnalogHatEnum a); + + /** + * Get the x-coordinate of the touchpad. Position 0 is in the top left. + * @param finger 0 = first finger, 1 = second finger. If omitted, then 0 will be used. + * @param xyId The controller sends out three packets with the same structure. + * The third one will contain the last measure, but if you read from the controller then there is only be data in the first one. + * For that reason it will be set to 0 if the argument is omitted. + * @return Returns the x-coordinate of the finger. + */ + uint16_t getX(uint8_t finger = 0, uint8_t xyId = 0) { + return ps4Data.xy[xyId].finger[finger].x; + }; + + /** + * Get the y-coordinate of the touchpad. Position 0 is in the top left. + * @param finger 0 = first finger, 1 = second finger. If omitted, then 0 will be used. + * @param xyId The controller sends out three packets with the same structure. + * The third one will contain the last measure, but if you read from the controller then there is only be data in the first one. + * For that reason it will be set to 0 if the argument is omitted. + * @return Returns the y-coordinate of the finger. + */ + uint16_t getY(uint8_t finger = 0, uint8_t xyId = 0) { + return ps4Data.xy[xyId].finger[finger].y; + }; + + /** + * Returns whenever the user is toucing the touchpad. + * @param finger 0 = first finger, 1 = second finger. If omitted, then 0 will be used. + * @param xyId The controller sends out three packets with the same structure. + * The third one will contain the last measure, but if you read from the controller then there is only be data in the first one. + * For that reason it will be set to 0 if the argument is omitted. + * @return Returns true if the specific finger is touching the touchpad. + */ + bool isTouching(uint8_t finger = 0, uint8_t xyId = 0) { + return !(ps4Data.xy[xyId].finger[finger].touching); // The bit is cleared when a finger is touching the touchpad + }; + + /** + * This counter increments every time a finger touches the touchpad. + * @param finger 0 = first finger, 1 = second finger. If omitted, then 0 will be used. + * @param xyId The controller sends out three packets with the same structure. + * The third one will contain the last measure, but if you read from the controller then there is only be data in the first one. + * For that reason it will be set to 0 if the argument is omitted. + * @return Return the value of the counter, note that it is only a 7-bit value. + */ + uint8_t getTouchCounter(uint8_t finger = 0, uint8_t xyId = 0) { + return ps4Data.xy[xyId].finger[finger].counter; + }; + + /** + * Get the angle of the controller calculated using the accelerometer. + * @param a Either ::Pitch or ::Roll. + * @return Return the angle in the range of 0-360. + */ + double getAngle(AngleEnum a) { + if (a == Pitch) + return (atan2(ps4Data.accY, ps4Data.accZ) + PI) * RAD_TO_DEG; + else + return (atan2(ps4Data.accX, ps4Data.accZ) + PI) * RAD_TO_DEG; + }; + + /** + * Used to get the raw values from the 3-axis gyroscope and 3-axis accelerometer inside the PS4 controller. + * @param s The sensor to read. + * @return Returns the raw sensor reading. + */ + int16_t getSensor(SensorEnum s) { + switch(s) { + case gX: + return ps4Data.gyroX; + case gY: + return ps4Data.gyroY; + case gZ: + return ps4Data.gyroZ; + case aX: + return ps4Data.accX; + case aY: + return ps4Data.accY; + case aZ: + return ps4Data.accZ; + default: + return 0; + } + }; + + /** + * Return the battery level of the PS4 controller. + * @return The battery level in the range 0-15. + */ + uint8_t getBatteryLevel() { + return ps4Data.status.battery; + }; + + /** + * Use this to check if an USB cable is connected to the PS4 controller. + * @return Returns true if an USB cable is connected. + */ + bool getUsbStatus() { + return ps4Data.status.usb; + }; + + /** + * Use this to check if an audio jack cable is connected to the PS4 controller. + * @return Returns true if an audio jack cable is connected. + */ + bool getAudioStatus() { + return ps4Data.status.audio; + }; + + /** + * Use this to check if a microphone is connected to the PS4 controller. + * @return Returns true if a microphone is connected. + */ + bool getMicStatus() { + return ps4Data.status.mic; + }; + + /** Turn both rumble and the LEDs off. */ + void setAllOff() { + setRumbleOff(); + setLedOff(); + }; + + /** Set rumble off. */ + void setRumbleOff() { + setRumbleOn(0, 0); + }; + + /** + * Turn on rumble. + * @param mode Either ::RumbleHigh or ::RumbleLow. + */ + void setRumbleOn(RumbleEnum mode) { + if (mode == RumbleLow) + setRumbleOn(0x00, 0xFF); + else + setRumbleOn(0xFF, 0x00); + }; + + /** + * Turn on rumble. + * @param bigRumble Value for big motor. + * @param smallRumble Value for small motor. + */ + void setRumbleOn(uint8_t bigRumble, uint8_t smallRumble) { + ps4Output.bigRumble = bigRumble; + ps4Output.smallRumble = smallRumble; + ps4Output.reportChanged = true; + }; + + /** Turn all LEDs off. */ + void setLedOff() { + setLed(0, 0, 0); + }; + + /** + * Use this to set the color using RGB values. + * @param r,g,b RGB value. + */ + void setLed(uint8_t r, uint8_t g, uint8_t b) { + ps4Output.r = r; + ps4Output.g = g; + ps4Output.b = b; + ps4Output.reportChanged = true; + }; + + /** + * Use this to set the color using the predefined colors in ::ColorsEnum. + * @param color The desired color. + */ + void setLed(ColorsEnum color) { + setLed((uint8_t)(color >> 16), (uint8_t)(color >> 8), (uint8_t)(color)); + }; + + /** + * Set the LEDs flash time. + * @param flashOn Time to flash bright (255 = 2.5 seconds). + * @param flashOff Time to flash dark (255 = 2.5 seconds). + */ + void setLedFlash(uint8_t flashOn, uint8_t flashOff) { + ps4Output.flashOn = flashOn; + ps4Output.flashOff = flashOff; + ps4Output.reportChanged = true; + }; + /**@}*/ + +protected: + /** + * Used to parse data sent from the PS4 controller. + * @param len Length of the data. + * @param buf Pointer to the data buffer. + */ + void Parse(uint8_t len, uint8_t *buf); + + /** Used to reset the different buffers to their default values */ + void Reset() { + uint8_t i; + for (i = 0; i < sizeof(ps4Data.hatValue); i++) + ps4Data.hatValue[i] = 127; // Center value + ps4Data.btn.val = 0; + oldButtonState.val = 0; + for (i = 0; i < sizeof(ps4Data.trigger); i++) + ps4Data.trigger[i] = 0; + for (i = 0; i < sizeof(ps4Data.xy)/sizeof(ps4Data.xy[0]); i++) { + for (uint8_t j = 0; j < sizeof(ps4Data.xy[0].finger)/sizeof(ps4Data.xy[0].finger[0]); j++) + ps4Data.xy[i].finger[j].touching = 1; // The bit is cleared if the finger is touching the touchpad + } + + ps4Data.btn.dpad = DPAD_OFF; + oldButtonState.dpad = DPAD_OFF; + buttonClickState.dpad = 0; + oldDpad = 0; + + ps4Output.bigRumble = ps4Output.smallRumble = 0; + ps4Output.r = ps4Output.g = ps4Output.b = 0; + ps4Output.flashOn = ps4Output.flashOff = 0; + ps4Output.reportChanged = false; + }; + + /** + * Send the output to the PS4 controller. This is implemented in PS4BT.h and PS4USB.h. + * @param output Pointer to PS4Output buffer; + */ + virtual void sendOutputReport(PS4Output *output) = 0; + +private: + bool checkDpad(ButtonEnum b); // Used to check PS4 DPAD buttons + + PS4Data ps4Data; + PS4Buttons oldButtonState, buttonClickState; + PS4Output ps4Output; + uint8_t oldDpad; +}; +#endif diff --git a/libraries/USB_Host_Shield/PS4USB.h b/libraries/USB_Host_Shield/PS4USB.h new file mode 100755 index 0000000..b43079a --- /dev/null +++ b/libraries/USB_Host_Shield/PS4USB.h @@ -0,0 +1,130 @@ +/* Copyright (C) 2014 Kristian Lauszus, TKJ Electronics. All rights reserved. + + This software may be distributed and modified under the terms of the GNU + General Public License version 2 (GPL2) as published by the Free Software + Foundation and appearing in the file GPL2.TXT included in the packaging of + this file. Please note that GPL2 Section 2[b] requires that all works based + on this software must also be made publicly available under the terms of + the GPL2 ("Copyleft"). + + Contact information + ------------------- + + Kristian Lauszus, TKJ Electronics + Web : http://www.tkjelectronics.com + e-mail : kristianl@tkjelectronics.com + */ + +#ifndef _ps4usb_h_ +#define _ps4usb_h_ + +#include "hiduniversal.h" +#include "PS4Parser.h" + +#define PS4_VID 0x054C // Sony Corporation +#define PS4_PID 0x05C4 // PS4 Controller + +/** + * This class implements support for the PS4 controller via USB. + * It uses the HIDUniversal class for all the USB communication. + */ +class PS4USB : public HIDUniversal, public PS4Parser { +public: + /** + * Constructor for the PS4USB class. + * @param p Pointer to the USB class instance. + */ + PS4USB(USB *p) : + HIDUniversal(p) { + PS4Parser::Reset(); + }; + + /** + * Used to check if a PS4 controller is connected. + * @return Returns true if it is connected. + */ + bool connected() { + return HIDUniversal::isReady() && HIDUniversal::VID == PS4_VID && HIDUniversal::PID == PS4_PID; + }; + + /** + * Used to call your own function when the device is successfully initialized. + * @param funcOnInit Function to call. + */ + void attachOnInit(void (*funcOnInit)(void)) { + pFuncOnInit = funcOnInit; + }; + +protected: + /** @name HIDUniversal implementation */ + /** + * Used to parse USB HID data. + * @param hid Pointer to the HID class. + * @param is_rpt_id Only used for Hubs. + * @param len The length of the incoming data. + * @param buf Pointer to the data buffer. + */ + virtual void ParseHIDData(HID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf) { + if (HIDUniversal::VID == PS4_VID && HIDUniversal::PID == PS4_PID) + PS4Parser::Parse(len, buf); + }; + + /** + * Called when a device is successfully initialized. + * Use attachOnInit(void (*funcOnInit)(void)) to call your own function. + * This is useful for instance if you want to set the LEDs in a specific way. + */ + virtual uint8_t OnInitSuccessful() { + if (HIDUniversal::VID == PS4_VID && HIDUniversal::PID == PS4_PID) { + PS4Parser::Reset(); + if (pFuncOnInit) + pFuncOnInit(); // Call the user function + else + setLed(Blue); + }; + return 0; + }; + /**@}*/ + + /** @name PS4Parser implementation */ + virtual void sendOutputReport(PS4Output *output) { // Source: https://github.com/chrippa/ds4drv + uint8_t buf[32]; + memset(buf, 0, sizeof(buf)); + + buf[0] = 0x05; // Report ID + buf[1]= 0xFF; + + buf[4] = output->smallRumble; // Small Rumble + buf[5] = output->bigRumble; // Big rumble + + buf[6] = output->r; // Red + buf[7] = output->g; // Green + buf[8] = output->b; // Blue + + buf[9] = output->flashOn; // Time to flash bright (255 = 2.5 seconds) + buf[10] = output->flashOff; // Time to flash dark (255 = 2.5 seconds) + + output->reportChanged = false; + + // The PS4 console actually set the four last bytes to a CRC32 checksum, but it seems like it is actually not needed + + pUsb->outTransfer(bAddress, epInfo[ hidInterfaces[0].epIndex[epInterruptOutIndex] ].epAddr, sizeof(buf), buf); + }; + /**@}*/ + + /** @name USBDeviceConfig implementation */ + /** + * Used by the USB core to check what this driver support. + * @param vid The device's VID. + * @param pid The device's PID. + * @return Returns true if the device's VID and PID matches this driver. + */ + virtual bool VIDPIDOK(uint16_t vid, uint16_t pid) { + return (vid == PS4_VID && pid == PS4_PID); + }; + /**@}*/ + +private: + void (*pFuncOnInit)(void); // Pointer to function called in onInit() +}; +#endif diff --git a/libraries/USB_Host_Shield/PSBuzz.cpp b/libraries/USB_Host_Shield/PSBuzz.cpp new file mode 100755 index 0000000..aa88371 --- /dev/null +++ b/libraries/USB_Host_Shield/PSBuzz.cpp @@ -0,0 +1,82 @@ +/* Copyright (C) 2014 Kristian Lauszus, TKJ Electronics. All rights reserved. + + This software may be distributed and modified under the terms of the GNU + General Public License version 2 (GPL2) as published by the Free Software + Foundation and appearing in the file GPL2.TXT included in the packaging of + this file. Please note that GPL2 Section 2[b] requires that all works based + on this software must also be made publicly available under the terms of + the GPL2 ("Copyleft"). + + Contact information + ------------------- + + Kristian Lauszus, TKJ Electronics + Web : http://www.tkjelectronics.com + e-mail : kristianl@tkjelectronics.com + */ + +#include "PSBuzz.h" + +// To enable serial debugging see "settings.h" +//#define PRINTREPORT // Uncomment to print the report send by the PS Buzz Controllers + +void PSBuzz::ParseHIDData(HID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf) { + if (HIDUniversal::VID == PSBUZZ_VID && HIDUniversal::PID == PSBUZZ_PID && len > 0 && buf) { +#ifdef PRINTREPORT + Notify(PSTR("\r\n"), 0x80); + for (uint8_t i = 0; i < len; i++) { + D_PrintHex (buf[i], 0x80); + Notify(PSTR(" "), 0x80); + } +#endif + memcpy(&psbuzzButtons, buf + 2, min(len - 2, sizeof(psbuzzButtons))); + + if (psbuzzButtons.val != oldButtonState.val) { // Check if anything has changed + buttonClickState.val = psbuzzButtons.val & ~oldButtonState.val; // Update click state variable + oldButtonState.val = psbuzzButtons.val; + } + } +}; + +uint8_t PSBuzz::OnInitSuccessful() { + if (HIDUniversal::VID == PSBUZZ_VID && HIDUniversal::PID == PSBUZZ_PID) { + Reset(); + if (pFuncOnInit) + pFuncOnInit(); // Call the user function + else + setLedOnAll(); // Turn the LED on, on all four controllers + }; + return 0; +}; + +bool PSBuzz::getButtonPress(ButtonEnum b, uint8_t controller) { + return psbuzzButtons.val & (1UL << (b + 5 * controller)); // Each controller uses 5 bits, so the value is shifted 5 for each controller +}; + +bool PSBuzz::getButtonClick(ButtonEnum b, uint8_t controller) { + uint32_t mask = (1UL << (b + 5 * controller)); // Each controller uses 5 bits, so the value is shifted 5 for each controller + bool click = buttonClickState.val & mask; + buttonClickState.val &= ~mask; // Clear "click" event + return click; +}; + +// Source: http://www.developerfusion.com/article/84338/making-usb-c-friendly/ and https://github.com/torvalds/linux/blob/master/drivers/hid/hid-sony.c +void PSBuzz::setLedRaw(bool value, uint8_t controller) { + ledState[controller] = value; // Save value for next time it is called + + uint8_t buf[7]; + buf[0] = 0x00; + buf[1] = ledState[0] ? 0xFF : 0x00; + buf[2] = ledState[1] ? 0xFF : 0x00; + buf[3] = ledState[2] ? 0xFF : 0x00; + buf[4] = ledState[3] ? 0xFF : 0x00; + buf[5] = 0x00; + buf[6] = 0x00; + + PSBuzz_Command(buf, sizeof(buf)); +}; + +void PSBuzz::PSBuzz_Command(uint8_t *data, uint16_t nbytes) { + // bmRequest = Host to device (0x00) | Class (0x20) | Interface (0x01) = 0x21, bRequest = Set Report (0x09), Report ID (0x00), Report Type (Output 0x02), interface (0x00), datalength, datalength, data) + pUsb->ctrlReq(bAddress, epInfo[0].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0x00, 0x02, 0x00, nbytes, nbytes, data, NULL); +}; diff --git a/libraries/USB_Host_Shield/PSBuzz.h b/libraries/USB_Host_Shield/PSBuzz.h new file mode 100755 index 0000000..8880d9e --- /dev/null +++ b/libraries/USB_Host_Shield/PSBuzz.h @@ -0,0 +1,185 @@ +/* Copyright (C) 2014 Kristian Lauszus, TKJ Electronics. All rights reserved. + + This software may be distributed and modified under the terms of the GNU + General Public License version 2 (GPL2) as published by the Free Software + Foundation and appearing in the file GPL2.TXT included in the packaging of + this file. Please note that GPL2 Section 2[b] requires that all works based + on this software must also be made publicly available under the terms of + the GPL2 ("Copyleft"). + + Contact information + ------------------- + + Kristian Lauszus, TKJ Electronics + Web : http://www.tkjelectronics.com + e-mail : kristianl@tkjelectronics.com + */ + +#ifndef _psbuzz_h_ +#define _psbuzz_h_ + +#include "hiduniversal.h" +#include "controllerEnums.h" + +#define PSBUZZ_VID 0x054C // Sony Corporation +#define PSBUZZ_PID 0x1000 // PS Buzz Controller + +/** Struct used to easily read the different buttons on the controllers */ +union PSBUZZButtons { + struct { + uint8_t red : 1; + uint8_t yellow : 1; + uint8_t green : 1; + uint8_t orange : 1; + uint8_t blue : 1; + } __attribute__((packed)) btn[4]; + uint32_t val : 20; +} __attribute__((packed)); + +/** + * This class implements support for the PS Buzz controllers via USB. + * It uses the HIDUniversal class for all the USB communication. + */ +class PSBuzz : public HIDUniversal { +public: + /** + * Constructor for the PSBuzz class. + * @param p Pointer to the USB class instance. + */ + PSBuzz(USB *p) : + HIDUniversal(p) { + Reset(); + }; + + /** + * Used to check if a PS Buzz controller is connected. + * @return Returns true if it is connected. + */ + bool connected() { + return HIDUniversal::isReady() && HIDUniversal::VID == PSBUZZ_VID && HIDUniversal::PID == PSBUZZ_PID; + }; + + /** + * Used to call your own function when the device is successfully initialized. + * @param funcOnInit Function to call. + */ + void attachOnInit(void (*funcOnInit)(void)) { + pFuncOnInit = funcOnInit; + }; + + /** @name PS Buzzer Controller functions */ + /** + * getButtonPress(ButtonEnum b) will return true as long as the button is held down. + * + * While getButtonClick(ButtonEnum b) will only return it once. + * + * So you instance if you need to increase a variable once you would use getButtonClick(ButtonEnum b), + * but if you need to drive a robot forward you would use getButtonPress(ButtonEnum b). + * @param b ::ButtonEnum to read. + * @param controller The controller to read from. Default to 0. + * @return getButtonPress(ButtonEnum b) will return a true as long as a button is held down, while getButtonClick(ButtonEnum b) will return true once for each button press. + */ + bool getButtonPress(ButtonEnum b, uint8_t controller = 0); + bool getButtonClick(ButtonEnum b, uint8_t controller = 0); + /**@}*/ + /** @name PS Buzzer Controller functions */ + /** + * Set LED value without using ::LEDEnum. + * @param value See: ::LEDEnum. + */ + /** + * Set LED values directly. + * @param value Used to set whenever the LED should be on or off + * @param controller The controller to control. Defaults to 0. + */ + void setLedRaw(bool value, uint8_t controller = 0); + + /** Turn all LEDs off. */ + void setLedOffAll() { + for (uint8_t i = 1; i < 4; i++) // Skip first as it will be set in setLedRaw + ledState[i] = false; // Just an easy way to set all four off at the same time + setLedRaw(false); // Turn the LED off, on all four controllers + }; + + /** + * Turn the LED off on a specific controller. + * @param controller The controller to turn off. Defaults to 0. + */ + void setLedOff(uint8_t controller = 0) { + setLedRaw(false, controller); + }; + + + /** Turn all LEDs on. */ + void setLedOnAll() { + for (uint8_t i = 1; i < 4; i++) // Skip first as it will be set in setLedRaw + ledState[i] = true; // Just an easy way to set all four off at the same time + setLedRaw(true); // Turn the LED on, on all four controllers + }; + + /** + * Turn the LED on on a specific controller. + * @param controller The controller to turn off. Defaults to 0. + */ + void setLedOn(uint8_t controller = 0) { + setLedRaw(true, controller); + }; + + /** + * Toggle the LED on a specific controller. + * @param controller The controller to turn off. Defaults to 0. + */ + void setLedToggle(uint8_t controller = 0) { + setLedRaw(!ledState[controller], controller); + }; + /**@}*/ + +protected: + /** @name HIDUniversal implementation */ + /** + * Used to parse USB HID data. + * @param hid Pointer to the HID class. + * @param is_rpt_id Only used for Hubs. + * @param len The length of the incoming data. + * @param buf Pointer to the data buffer. + */ + void ParseHIDData(HID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf); + + /** + * Called when a device is successfully initialized. + * Use attachOnInit(void (*funcOnInit)(void)) to call your own function. + * This is useful for instance if you want to set the LEDs in a specific way. + */ + uint8_t OnInitSuccessful(); + /**@}*/ + + /** Used to reset the different buffers to their default values */ + void Reset() { + psbuzzButtons.val = 0; + oldButtonState.val = 0; + buttonClickState.val = 0; + for (uint8_t i = 0; i < sizeof(ledState); i++) + ledState[i] = 0; + }; + + /** @name USBDeviceConfig implementation */ + /** + * Used by the USB core to check what this driver support. + * @param vid The device's VID. + * @param pid The device's PID. + * @return Returns true if the device's VID and PID matches this driver. + */ + virtual bool VIDPIDOK(uint16_t vid, uint16_t pid) { + return (vid == PSBUZZ_VID && pid == PSBUZZ_PID); + }; + /**@}*/ + +private: + void (*pFuncOnInit)(void); // Pointer to function called in onInit() + + void PSBuzz_Command(uint8_t *data, uint16_t nbytes); + + PSBUZZButtons psbuzzButtons, oldButtonState, buttonClickState; + bool ledState[4]; +}; +#endif diff --git a/libraries/USB_Host_Shield/README.md b/libraries/USB_Host_Shield/README.md new file mode 100755 index 0000000..a4ef7a8 --- /dev/null +++ b/libraries/USB_Host_Shield/README.md @@ -0,0 +1,325 @@ +# USB Host Library Rev.2.0 + +The code is released under the GNU General Public License. +__________ + +# Summary +This is Revision 2.0 of MAX3421E-based USB Host Shield Library for AVR's. + +Project main web site is: . + +Some information can also be found at: . + +The shield can be purchased at the main site: or from [TKJ Electronics](http://tkjelectronics.com/): . + +![USB Host Shield](http://www.circuitsathome.com/wp/wp-content/uploads/2012/02/UHS_20_main-288x216.jpg) + +For more information about the hardware see the [Hardware Manual](http://www.circuitsathome.com/usb-host-shield-hardware-manual). + +# Developed By + +* __Oleg Mazurov, Circuits@Home__ - +* __Alexei Glushchenko, Circuits@Home__ - + * Developers of the USB Core, HID, FTDI, ADK, ACM, and PL2303 libraries +* __Kristian Lauszus, TKJ Electronics__ - + * Developer of the [BTD](#bluetooth-libraries), [BTHID](#bthid-library), [SPP](#spp-library), [PS4](#ps4-library), [PS3](#ps3-library), [Wii](#wii-library), [Xbox](#xbox-library), and [PSBuzz](#ps-buzz-library) libraries +* __Andrew Kroll__ - + * Major contributor to mass storage code + +# Donate + +Help yourself by helping us support you! Many thousands of hours have been spent developing the USB Host Shield library. Since you find it useful, please consider donating via the button below. Donations will allow us to support you by ensuring hardware that you have can be acquired in order to add support for your microcontroller board. + +PayPal - The safer, easier way to pay online! + +# Table of Contents + +* [How to include the library](#how-to-include-the-library) +* [How to use the library](#how-to-use-the-library) + * [Documentation](#documentation) + * [Enable debugging](#enable-debugging) + * [Boards](#boards) + * [Bluetooth libraries](#bluetooth-libraries) + * [BTHID library](#bthid-library) + * [SPP library](#spp-library) + * [PS4 Library](#ps4-library) + * [PS3 Library](#ps3-library) + * [Xbox Libraries](#xbox-libraries) + * [Xbox library](#xbox-library) + * [Xbox 360 Library](#xbox-360-library) + * [Wii library](#wii-library) + * [PS Buzz Library](#ps-buzz-library) +* [Interface modifications](#interface-modifications) +* [FAQ](#faq) + +# How to include the library + +First download the library by clicking on the following link: . + +Then uncompress the zip folder and rename the directory to "USB\_Host\_Shield\_20", as any special characters are not supported by the Arduino IDE. + +Now open up the Arduino IDE and open "File>Preferences". There you will see the location of your sketchbook. Open that directory and create a directory called "libraries" inside that directory. +Now move the "USB\_Host\_Shield\_20" directory to the "libraries" directory. + +The final structure should look like this: + +* Arduino/ + * libraries/ + * USB\_Host\_Shield\_20/ + +Now quit the Arduino IDE and reopen it. + +Now you should be able to go open all the examples codes by navigating to "File>Examples>USB\_Host\_Shield\_20" and then select the example you will like to open. + +For more information visit the following sites: and . + +# How to use the library + +### Documentation + +Documentation for the library can be found at the following link: . + +### Enable debugging + +By default serial debugging is disabled. To turn it on simply change ```ENABLE_UHS_DEBUGGING``` to 1 in [settings.h](settings.h) like so: + +```C++ +#define ENABLE_UHS_DEBUGGING 1 +``` + +### Boards + +Currently the following boards are supported by the library: + +* All official Arduino AVR boards (Uno, Duemilanove, Mega, Mega 2560, Mega ADK, Leonardo etc.) +* Arduino Due + * If you are using the Arduino Due, then you must include the Arduino SPI library like so: ```#include ``` in your .ino file. +* Teensy (Teensy++ 1.0, Teensy 2.0, Teensy++ 2.0, and Teensy 3.x) + * Note if you are using the Teensy 3.x you should download this SPI library as well: . You should then add ```#include ``` to your .ino file. +* Balanduino +* Sanguino +* Black Widdow +* RedBearLab nRF51822 + * If you are using the RedBearLab nRF51822, then you must include the RedBearLab SPI library like so: ```#include ``` in your .ino file. + +The following boards need to be activated manually in [settings.h](settings.h): + +* Arduino Mega ADK + * If you are using Arduino 1.5.5 or newer there is no need to activate the Arduino Mega ADK manually +* Black Widdow + +Simply set the corresponding value to 1 instead of 0. + +### [Bluetooth libraries](BTD.cpp) + +The [BTD library](BTD.cpp) is a general purpose library for an ordinary Bluetooth dongle. +This library make it easy to add support for different Bluetooth services like a PS3 or a Wii controller or SPP which is a virtual serial port via Bluetooth. +Some different examples can be found in the [example directory](examples/Bluetooth). + +The BTD library also makes it possible to use multiple services at once, the following example sketch is an example of this: +[PS3SPP.ino](examples/Bluetooth/PS3SPP/PS3SPP.ino). + +### [BTHID library](BTHID.cpp) + +The [Bluetooth HID library](BTHID.cpp) allows you to connect HID devices via Bluetooth to the USB Host Shield. + +Currently HID mice and keyboards are supported. + +It uses the standard Boot protocol by default, but it is also able to use the Report protocol as well. You would simply have to call ```setProtocolMode()``` and then parse ```HID_RPT_PROTOCOL``` as an argument. You will then have to modify the parser for your device. See the example: [BTHID.ino](examples/Bluetooth/BTHID/BTHID.ino) for more information. + +The [PS4 library](#ps4-library) also uses this class to handle all Bluetooth communication. + +For information see the following blog post: . + +### [SPP library](SPP.cpp) + +SPP stands for "Serial Port Profile" and is a Bluetooth protocol that implements a virtual comport which allows you to send data back and forth from your computer/phone to your Arduino via Bluetooth. +It has been tested successfully on Windows, Mac OS X, Linux, and Android. + +Take a look at the [SPP.ino](examples/Bluetooth/SPP/SPP.ino) example for more information. + +More information can be found at these blog posts: + +* +* + +To implement the SPP protocol I used a Bluetooth sniffing tool called [PacketLogger](http://www.tkjelectronics.com/uploads/PacketLogger.zip) developed by Apple. +It enables me to see the Bluetooth communication between my Mac and any device. + +### PS4 Library + +The PS4BT library is split up into the [PS4BT](PS4BT.h) and the [PS4USB](PS4USB.h) library. These allow you to use the Sony PS4 controller via Bluetooth and USB. + +The [PS4BT.ino](examples/Bluetooth/PS4BT/PS4BT.ino) and [PS4USB.ino](examples/PS4USB/PS4USB.ino) examples shows how to easily read the buttons, joysticks, touchpad and IMU on the controller via Bluetooth and USB respectively. It is also possible to control the rumble and light on the controller and get the battery level. + +Before you can use the PS4 controller via Bluetooth you will need to pair with it. + +Simply create the PS4BT instance like so: ```PS4BT PS4(&Btd, PAIR);``` and then hold down the Share button and then hold down the PS without releasing the Share button. The PS4 controller will then start to blink rapidly indicating that it is in paring mode. + +It should then automatically pair the dongle with your controller. This only have to be done once. + +For information see the following blog post: . + +Also check out this excellent Wiki by Frank Zhao about the PS4 controller: and this Linux driver: . + +### PS3 Library + +These libraries consist of the [PS3BT](PS3BT.cpp) and [PS3USB](PS3USB.cpp). These libraries allows you to use a Dualshock 3, Navigation or a Motion controller with the USB Host Shield both via Bluetooth and USB. + +In order to use your Playstation controller via Bluetooth you have to set the Bluetooth address of the dongle internally to your PS3 Controller. This can be achieved by first plugging in the Bluetooth dongle and wait a few seconds. Now plug in the controller via USB and wait until the LEDs start to flash. The library has now written the Bluetooth address of the dongle to the PS3 controller. + +Finally simply plug in the Bluetooth dongle again and press PS on the PS3 controller. After a few seconds it should be connected to the dongle and ready to use. + +__Note:__ You will have to plug in the Bluetooth dongle before connecting the controller, as the library needs to read the address of the dongle. Alternatively you could set it in code like so: [PS3BT.ino#L20](examples/Bluetooth/PS3BT/PS3BT.ino#L20). + +For more information about the PS3 protocol see the official wiki: . + +Also take a look at the blog posts: + +* +* +* + +A special thanks go to the following people: + +1. _Richard Ibbotson_ who made this excellent guide: +2. _Tomoyuki Tanaka_ for releasing his code for the Arduino USB Host shield connected to the wiimote: + +Also a big thanks all the people behind these sites about the Motion controller: + +* +* +* +* + +### Xbox Libraries + +The library supports both the original Xbox controller via USB and the Xbox 360 controller both via USB and wirelessly. + +#### Xbox library + +The [XBOXOLD](XBOXOLD.cpp) class implements support for the original Xbox controller via USB. + +All the information are from the following sites: + +* +* +* +* + +#### Xbox 360 Library + +The library support one Xbox 360 via USB or up to four Xbox 360 controllers wirelessly by using a [Xbox 360 wireless receiver](http://blog.tkjelectronics.dk/wp-content/uploads/xbox360-wireless-receiver.jpg). + +To use it via USB use the [XBOXUSB](XBOXUSB.cpp) library or to use it wirelessly use the [XBOXRECV](XBOXRECV.cpp) library. + +__Note that a Wireless controller can NOT be used via USB!__ + +Examples code can be found in the [examples directory](examples/Xbox). + +Also see the following blog posts: + +* +* +* + +All the information regarding the Xbox 360 controller protocol are form these sites: + +* +* +* + +### [Wii library](Wii.cpp) + +The [Wii](Wii.cpp) library support the Wiimote, but also the Nunchuch and Motion Plus extensions via Bluetooth. The Wii U Pro Controller is also supported via Bluetooth. + +First you have to pair with the controller, this is done automatically by the library if you create the instance like so: + +```C++ +WII Wii(&Btd, PAIR); +``` + +And then press 1 & 2 at once on the Wiimote or press sync if you are using a Wii U Pro Controller. + +After that you can simply create the instance like so: + +```C++ +WII Wii(&Btd); +``` + +Then just press any button on the Wiimote and it will then connect to the dongle. + +Take a look at the example for more information: [Wii.ino](examples/Bluetooth/Wii/Wii.ino). + +Also take a look at the blog post: + +* + +The Wii IR camera can also be used, but you will have to activate the code for it manually as it is quite large. Simply set ```ENABLE_WII_IR_CAMERA``` to 1 in [settings.h](settings.h). + +The [WiiIRCamera.ino](examples/Bluetooth/WiiIRCamera/WiiIRCamera.ino) example shows how it can be used. + + +All the information about the Wii controllers are from these sites: + +* +* +* +* +* The old library created by _Tomoyuki Tanaka_: also helped a lot. + +### [PS Buzz Library](PSBuzz.cpp) + +This library implements support for the Playstation Buzz controllers via USB. + +It is essentially just a wrapper around the [HIDUniversal](hiduniversal.cpp) which takes care of the initializing and reading of the controllers. The [PSBuzz](PSBuzz.cpp) class simply inherits this and parses the data, so it is easy for users to read the buttons and turn the big red button on the controllers on and off. + +The example [PSBuzz.ino](examples/PSBuzz/PSBuzz.ino) shows how one can do this with just a few lines of code. + +More information about the controller can be found at the following sites: + +* http://www.developerfusion.com/article/84338/making-usb-c-friendly/ +* https://github.com/torvalds/linux/blob/master/drivers/hid/hid-sony.c + +# Interface modifications + +The shield is using SPI for communicating with the MAX3421E USB host controller. It uses the SCK, MISO and MOSI pins via the ICSP on your board. + +Note this means that it uses pin 13, 12, 11 on an Arduino Uno, so these pins can not be used for anything else than SPI communication! + +Furthermore it uses one pin as SS and one INT pin. These are by default located on pin 10 and 9 respectively. They can easily be reconfigured in case you need to use them for something else by cutting the jumper on the shield and then solder a wire from the pad to the new pin. + +After that you need modify the following entry in [UsbCore.h](UsbCore.h): + +```C++ +typedef MAX3421e MAX3421E; +``` + +For instance if you have rerouted SS to pin 7 it should read: + +```C++ +typedef MAX3421e MAX3421E; +``` + +See the "Interface modifications" section in the [hardware manual](https://www.circuitsathome.com/usb-host-shield-hardware-manual) for more information. + +# FAQ + +> When I plug my device into the USB connector nothing happens? + +* Try to connect a external power supply to the Arduino - this solves the problem in most cases. +* You can also use a powered hub between the device and the USB Host Shield. You should then include the USB hub library: ```#include ``` and create the instance like so: ```USBHub Hub1(&Usb);```. + +> When I connecting my PS3 controller I get a output like this: + +``` +Dualshock 3 Controller Enabled + +LeftHatX: 0 LeftHatY: 0 RightHatX: 0 RightHatY: 0 +LeftHatX: 0 LeftHatY: 0 RightHatX: 0 RightHatY: 0 +LeftHatX: 0 LeftHatY: 0 RightHatX: 0 RightHatY: 0 +LeftHatX: 0 LeftHatY: 0 RightHatX: 0 RightHatY: 0 +LeftHatX: 0 LeftHatY: 0 RightHatX: 0 RightHatY: 0 +``` + +* This means that your dongle does not support 2.0+EDR, so you will need another dongle. Please see the following [list](https://github.com/felis/USB_Host_Shield_2.0/wiki/Bluetooth-dongles) for tested working dongles. \ No newline at end of file diff --git a/libraries/USB_Host_Shield/SPP.cpp b/libraries/USB_Host_Shield/SPP.cpp new file mode 100755 index 0000000..0f4ee5e --- /dev/null +++ b/libraries/USB_Host_Shield/SPP.cpp @@ -0,0 +1,829 @@ +/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved. + + This software may be distributed and modified under the terms of the GNU + General Public License version 2 (GPL2) as published by the Free Software + Foundation and appearing in the file GPL2.TXT included in the packaging of + this file. Please note that GPL2 Section 2[b] requires that all works based + on this software must also be made publicly available under the terms of + the GPL2 ("Copyleft"). + + Contact information + ------------------- + + Kristian Lauszus, TKJ Electronics + Web : http://www.tkjelectronics.com + e-mail : kristianl@tkjelectronics.com + */ + +#include "SPP.h" +// To enable serial debugging see "settings.h" +//#define EXTRADEBUG // Uncomment to get even more debugging data +//#define PRINTREPORT // Uncomment to print the report sent to the Arduino + +/* + * CRC (reversed crc) lookup table as calculated by the table generator in ETSI TS 101 369 V6.3.0. + */ +const uint8_t rfcomm_crc_table[256] PROGMEM = {/* reversed, 8-bit, poly=0x07 */ + 0x00, 0x91, 0xE3, 0x72, 0x07, 0x96, 0xE4, 0x75, 0x0E, 0x9F, 0xED, 0x7C, 0x09, 0x98, 0xEA, 0x7B, + 0x1C, 0x8D, 0xFF, 0x6E, 0x1B, 0x8A, 0xF8, 0x69, 0x12, 0x83, 0xF1, 0x60, 0x15, 0x84, 0xF6, 0x67, + 0x38, 0xA9, 0xDB, 0x4A, 0x3F, 0xAE, 0xDC, 0x4D, 0x36, 0xA7, 0xD5, 0x44, 0x31, 0xA0, 0xD2, 0x43, + 0x24, 0xB5, 0xC7, 0x56, 0x23, 0xB2, 0xC0, 0x51, 0x2A, 0xBB, 0xC9, 0x58, 0x2D, 0xBC, 0xCE, 0x5F, + 0x70, 0xE1, 0x93, 0x02, 0x77, 0xE6, 0x94, 0x05, 0x7E, 0xEF, 0x9D, 0x0C, 0x79, 0xE8, 0x9A, 0x0B, + 0x6C, 0xFD, 0x8F, 0x1E, 0x6B, 0xFA, 0x88, 0x19, 0x62, 0xF3, 0x81, 0x10, 0x65, 0xF4, 0x86, 0x17, + 0x48, 0xD9, 0xAB, 0x3A, 0x4F, 0xDE, 0xAC, 0x3D, 0x46, 0xD7, 0xA5, 0x34, 0x41, 0xD0, 0xA2, 0x33, + 0x54, 0xC5, 0xB7, 0x26, 0x53, 0xC2, 0xB0, 0x21, 0x5A, 0xCB, 0xB9, 0x28, 0x5D, 0xCC, 0xBE, 0x2F, + 0xE0, 0x71, 0x03, 0x92, 0xE7, 0x76, 0x04, 0x95, 0xEE, 0x7F, 0x0D, 0x9C, 0xE9, 0x78, 0x0A, 0x9B, + 0xFC, 0x6D, 0x1F, 0x8E, 0xFB, 0x6A, 0x18, 0x89, 0xF2, 0x63, 0x11, 0x80, 0xF5, 0x64, 0x16, 0x87, + 0xD8, 0x49, 0x3B, 0xAA, 0xDF, 0x4E, 0x3C, 0xAD, 0xD6, 0x47, 0x35, 0xA4, 0xD1, 0x40, 0x32, 0xA3, + 0xC4, 0x55, 0x27, 0xB6, 0xC3, 0x52, 0x20, 0xB1, 0xCA, 0x5B, 0x29, 0xB8, 0xCD, 0x5C, 0x2E, 0xBF, + 0x90, 0x01, 0x73, 0xE2, 0x97, 0x06, 0x74, 0xE5, 0x9E, 0x0F, 0x7D, 0xEC, 0x99, 0x08, 0x7A, 0xEB, + 0x8C, 0x1D, 0x6F, 0xFE, 0x8B, 0x1A, 0x68, 0xF9, 0x82, 0x13, 0x61, 0xF0, 0x85, 0x14, 0x66, 0xF7, + 0xA8, 0x39, 0x4B, 0xDA, 0xAF, 0x3E, 0x4C, 0xDD, 0xA6, 0x37, 0x45, 0xD4, 0xA1, 0x30, 0x42, 0xD3, + 0xB4, 0x25, 0x57, 0xC6, 0xB3, 0x22, 0x50, 0xC1, 0xBA, 0x2B, 0x59, 0xC8, 0xBD, 0x2C, 0x5E, 0xCF +}; + +SPP::SPP(BTD *p, const char* name, const char* pin) : +BluetoothService(p) // Pointer to BTD class instance - mandatory +{ + pBtd->btdName = name; + pBtd->btdPin = pin; + + /* Set device cid for the SDP and RFCOMM channelse */ + sdp_dcid[0] = 0x50; // 0x0050 + sdp_dcid[1] = 0x00; + rfcomm_dcid[0] = 0x51; // 0x0051 + rfcomm_dcid[1] = 0x00; + + Reset(); +} + +void SPP::Reset() { + connected = false; + RFCOMMConnected = false; + SDPConnected = false; + waitForLastCommand = false; + l2cap_sdp_state = L2CAP_SDP_WAIT; + l2cap_rfcomm_state = L2CAP_RFCOMM_WAIT; + l2cap_event_flag = 0; + sppIndex = 0; + creditSent = false; +} + +void SPP::disconnect() { + connected = false; + // First the two L2CAP channels has to be disconnected and then the HCI connection + if(RFCOMMConnected) + pBtd->l2cap_disconnection_request(hci_handle, ++identifier, rfcomm_scid, rfcomm_dcid); + if(RFCOMMConnected && SDPConnected) + delay(1); // Add delay between commands + if(SDPConnected) + pBtd->l2cap_disconnection_request(hci_handle, ++identifier, sdp_scid, sdp_dcid); + l2cap_sdp_state = L2CAP_DISCONNECT_RESPONSE; +} + +void SPP::ACLData(uint8_t* l2capinbuf) { + if(!connected) { + if(l2capinbuf[8] == L2CAP_CMD_CONNECTION_REQUEST) { + if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == SDP_PSM && !pBtd->sdpConnectionClaimed) { + pBtd->sdpConnectionClaimed = true; + hci_handle = pBtd->hci_handle; // Store the HCI Handle for the connection + l2cap_sdp_state = L2CAP_SDP_WAIT; // Reset state + } else if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == RFCOMM_PSM && !pBtd->rfcommConnectionClaimed) { + pBtd->rfcommConnectionClaimed = true; + hci_handle = pBtd->hci_handle; // Store the HCI Handle for the connection + l2cap_rfcomm_state = L2CAP_RFCOMM_WAIT; // Reset state + } + } + } + + if(checkHciHandle(l2capinbuf, hci_handle)) { // acl_handle_ok + if((l2capinbuf[6] | (l2capinbuf[7] << 8)) == 0x0001U) { // l2cap_control - Channel ID for ACL-U + if(l2capinbuf[8] == L2CAP_CMD_COMMAND_REJECT) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nL2CAP Command Rejected - Reason: "), 0x80); + D_PrintHex (l2capinbuf[13], 0x80); + Notify(PSTR(" "), 0x80); + D_PrintHex (l2capinbuf[12], 0x80); + Notify(PSTR(" Data: "), 0x80); + D_PrintHex (l2capinbuf[17], 0x80); + Notify(PSTR(" "), 0x80); + D_PrintHex (l2capinbuf[16], 0x80); + Notify(PSTR(" "), 0x80); + D_PrintHex (l2capinbuf[15], 0x80); + Notify(PSTR(" "), 0x80); + D_PrintHex (l2capinbuf[14], 0x80); +#endif + } else if(l2capinbuf[8] == L2CAP_CMD_CONNECTION_REQUEST) { +#ifdef EXTRADEBUG + Notify(PSTR("\r\nL2CAP Connection Request - PSM: "), 0x80); + D_PrintHex (l2capinbuf[13], 0x80); + Notify(PSTR(" "), 0x80); + D_PrintHex (l2capinbuf[12], 0x80); + Notify(PSTR(" SCID: "), 0x80); + D_PrintHex (l2capinbuf[15], 0x80); + Notify(PSTR(" "), 0x80); + D_PrintHex (l2capinbuf[14], 0x80); + Notify(PSTR(" Identifier: "), 0x80); + D_PrintHex (l2capinbuf[9], 0x80); +#endif + if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == SDP_PSM) { // It doesn't matter if it receives another reqeust, since it waits for the channel to disconnect in the L2CAP_SDP_DONE state, and the l2cap_event_flag will be cleared if so + identifier = l2capinbuf[9]; + sdp_scid[0] = l2capinbuf[14]; + sdp_scid[1] = l2capinbuf[15]; + l2cap_set_flag(L2CAP_FLAG_CONNECTION_SDP_REQUEST); + } else if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == RFCOMM_PSM) { // ----- || ----- + identifier = l2capinbuf[9]; + rfcomm_scid[0] = l2capinbuf[14]; + rfcomm_scid[1] = l2capinbuf[15]; + l2cap_set_flag(L2CAP_FLAG_CONNECTION_RFCOMM_REQUEST); + } + } else if(l2capinbuf[8] == L2CAP_CMD_CONFIG_RESPONSE) { + if((l2capinbuf[16] | (l2capinbuf[17] << 8)) == 0x0000) { // Success + if(l2capinbuf[12] == sdp_dcid[0] && l2capinbuf[13] == sdp_dcid[1]) { + //Notify(PSTR("\r\nSDP Configuration Complete"), 0x80); + l2cap_set_flag(L2CAP_FLAG_CONFIG_SDP_SUCCESS); + } else if(l2capinbuf[12] == rfcomm_dcid[0] && l2capinbuf[13] == rfcomm_dcid[1]) { + //Notify(PSTR("\r\nRFCOMM Configuration Complete"), 0x80); + l2cap_set_flag(L2CAP_FLAG_CONFIG_RFCOMM_SUCCESS); + } + } + } else if(l2capinbuf[8] == L2CAP_CMD_CONFIG_REQUEST) { + if(l2capinbuf[12] == sdp_dcid[0] && l2capinbuf[13] == sdp_dcid[1]) { + //Notify(PSTR("\r\nSDP Configuration Request"), 0x80); + pBtd->l2cap_config_response(hci_handle, l2capinbuf[9], sdp_scid); + } else if(l2capinbuf[12] == rfcomm_dcid[0] && l2capinbuf[13] == rfcomm_dcid[1]) { + //Notify(PSTR("\r\nRFCOMM Configuration Request"), 0x80); + pBtd->l2cap_config_response(hci_handle, l2capinbuf[9], rfcomm_scid); + } + } else if(l2capinbuf[8] == L2CAP_CMD_DISCONNECT_REQUEST) { + if(l2capinbuf[12] == sdp_dcid[0] && l2capinbuf[13] == sdp_dcid[1]) { + //Notify(PSTR("\r\nDisconnect Request: SDP Channel"), 0x80); + identifier = l2capinbuf[9]; + l2cap_set_flag(L2CAP_FLAG_DISCONNECT_SDP_REQUEST); + } else if(l2capinbuf[12] == rfcomm_dcid[0] && l2capinbuf[13] == rfcomm_dcid[1]) { + //Notify(PSTR("\r\nDisconnect Request: RFCOMM Channel"), 0x80); + identifier = l2capinbuf[9]; + l2cap_set_flag(L2CAP_FLAG_DISCONNECT_RFCOMM_REQUEST); + } + } else if(l2capinbuf[8] == L2CAP_CMD_DISCONNECT_RESPONSE) { + if(l2capinbuf[12] == sdp_scid[0] && l2capinbuf[13] == sdp_scid[1]) { + //Notify(PSTR("\r\nDisconnect Response: SDP Channel"), 0x80); + identifier = l2capinbuf[9]; + l2cap_set_flag(L2CAP_FLAG_DISCONNECT_RESPONSE); + } else if(l2capinbuf[12] == rfcomm_scid[0] && l2capinbuf[13] == rfcomm_scid[1]) { + //Notify(PSTR("\r\nDisconnect Response: RFCOMM Channel"), 0x80); + identifier = l2capinbuf[9]; + l2cap_set_flag(L2CAP_FLAG_DISCONNECT_RESPONSE); + } + } else if(l2capinbuf[8] == L2CAP_CMD_INFORMATION_REQUEST) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nInformation request"), 0x80); +#endif + identifier = l2capinbuf[9]; + pBtd->l2cap_information_response(hci_handle, identifier, l2capinbuf[12], l2capinbuf[13]); + } +#ifdef EXTRADEBUG + else { + Notify(PSTR("\r\nL2CAP Unknown Signaling Command: "), 0x80); + D_PrintHex (l2capinbuf[8], 0x80); + } +#endif + } else if(l2capinbuf[6] == sdp_dcid[0] && l2capinbuf[7] == sdp_dcid[1]) { // SDP + if(l2capinbuf[8] == SDP_SERVICE_SEARCH_ATTRIBUTE_REQUEST_PDU) { + if(((l2capinbuf[16] << 8 | l2capinbuf[17]) == SERIALPORT_UUID) || ((l2capinbuf[16] << 8 | l2capinbuf[17]) == 0x0000 && (l2capinbuf[18] << 8 | l2capinbuf[19]) == SERIALPORT_UUID)) { // Check if it's sending the full UUID, see: https://www.bluetooth.org/Technical/AssignedNumbers/service_discovery.htm, we will just check the first four bytes + if(firstMessage) { + serialPortResponse1(l2capinbuf[9], l2capinbuf[10]); + firstMessage = false; + } else { + serialPortResponse2(l2capinbuf[9], l2capinbuf[10]); // Serialport continuation state + firstMessage = true; + } + } else if(((l2capinbuf[16] << 8 | l2capinbuf[17]) == L2CAP_UUID) || ((l2capinbuf[16] << 8 | l2capinbuf[17]) == 0x0000 && (l2capinbuf[18] << 8 | l2capinbuf[19]) == L2CAP_UUID)) { + if(firstMessage) { + l2capResponse1(l2capinbuf[9], l2capinbuf[10]); + firstMessage = false; + } else { + l2capResponse2(l2capinbuf[9], l2capinbuf[10]); // L2CAP continuation state + firstMessage = true; + } + } else + serviceNotSupported(l2capinbuf[9], l2capinbuf[10]); // The service is not supported +#ifdef EXTRADEBUG + Notify(PSTR("\r\nUUID: "), 0x80); + uint16_t uuid; + if((l2capinbuf[16] << 8 | l2capinbuf[17]) == 0x0000) // Check if it's sending the UUID as a 128-bit UUID + uuid = (l2capinbuf[18] << 8 | l2capinbuf[19]); + else // Short UUID + uuid = (l2capinbuf[16] << 8 | l2capinbuf[17]); + D_PrintHex (uuid, 0x80); + + Notify(PSTR("\r\nLength: "), 0x80); + uint16_t length = l2capinbuf[11] << 8 | l2capinbuf[12]; + D_PrintHex (length, 0x80); + Notify(PSTR("\r\nData: "), 0x80); + for(uint8_t i = 0; i < length; i++) { + D_PrintHex (l2capinbuf[13 + i], 0x80); + Notify(PSTR(" "), 0x80); + } +#endif + } +#ifdef EXTRADEBUG + else { + Notify(PSTR("\r\nUnknown PDU: "), 0x80); + D_PrintHex (l2capinbuf[8], 0x80); + } +#endif + } else if(l2capinbuf[6] == rfcomm_dcid[0] && l2capinbuf[7] == rfcomm_dcid[1]) { // RFCOMM + rfcommChannel = l2capinbuf[8] & 0xF8; + rfcommDirection = l2capinbuf[8] & 0x04; + rfcommCommandResponse = l2capinbuf[8] & 0x02; + rfcommChannelType = l2capinbuf[9] & 0xEF; + rfcommPfBit = l2capinbuf[9] & 0x10; + + if(rfcommChannel >> 3 != 0x00) + rfcommChannelConnection = rfcommChannel; + +#ifdef EXTRADEBUG + Notify(PSTR("\r\nRFCOMM Channel: "), 0x80); + D_PrintHex (rfcommChannel >> 3, 0x80); + Notify(PSTR(" Direction: "), 0x80); + D_PrintHex (rfcommDirection >> 2, 0x80); + Notify(PSTR(" CommandResponse: "), 0x80); + D_PrintHex (rfcommCommandResponse >> 1, 0x80); + Notify(PSTR(" ChannelType: "), 0x80); + D_PrintHex (rfcommChannelType, 0x80); + Notify(PSTR(" PF_BIT: "), 0x80); + D_PrintHex (rfcommPfBit, 0x80); +#endif + if(rfcommChannelType == RFCOMM_DISC) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nReceived Disconnect RFCOMM Command on channel: "), 0x80); + D_PrintHex (rfcommChannel >> 3, 0x80); +#endif + connected = false; + sendRfcomm(rfcommChannel, rfcommDirection, rfcommCommandResponse, RFCOMM_UA, rfcommPfBit, rfcommbuf, 0x00); // UA Command + } + if(connected) { + /* Read the incoming message */ + if(rfcommChannelType == RFCOMM_UIH && rfcommChannel == rfcommChannelConnection) { + uint8_t length = l2capinbuf[10] >> 1; // Get length + uint8_t offset = l2capinbuf[4] - length - 4; // Check if there is credit + if(checkFcs(&l2capinbuf[8], l2capinbuf[11 + length + offset])) { + uint8_t i = 0; + for(; i < length; i++) { + if(rfcommAvailable + i >= sizeof (rfcommDataBuffer)) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nWarning: Buffer is full!"), 0x80); +#endif + break; + } + rfcommDataBuffer[rfcommAvailable + i] = l2capinbuf[11 + i + offset]; + } + rfcommAvailable += i; +#ifdef EXTRADEBUG + Notify(PSTR("\r\nRFCOMM Data Available: "), 0x80); + Notify(rfcommAvailable, 0x80); + if(offset) { + Notify(PSTR(" - Credit: 0x"), 0x80); + D_PrintHex (l2capinbuf[11], 0x80); + } +#endif + } +#ifdef DEBUG_USB_HOST + else + Notify(PSTR("\r\nError in FCS checksum!"), 0x80); +#endif +#ifdef PRINTREPORT // Uncomment "#define PRINTREPORT" to print the report send to the Arduino via Bluetooth + for(uint8_t i = 0; i < length; i++) + Notifyc(l2capinbuf[i + 11 + offset], 0x80); +#endif + } else if(rfcommChannelType == RFCOMM_UIH && l2capinbuf[11] == BT_RFCOMM_RPN_CMD) { // UIH Remote Port Negotiation Command +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nReceived UIH Remote Port Negotiation Command"), 0x80); +#endif + rfcommbuf[0] = BT_RFCOMM_RPN_RSP; // Command + rfcommbuf[1] = l2capinbuf[12]; // Length and shiftet like so: length << 1 | 1 + rfcommbuf[2] = l2capinbuf[13]; // Channel: channel << 1 | 1 + rfcommbuf[3] = l2capinbuf[14]; // Pre difined for Bluetooth, see 5.5.3 of TS 07.10 Adaption for RFCOMM + rfcommbuf[4] = l2capinbuf[15]; // Priority + rfcommbuf[5] = l2capinbuf[16]; // Timer + rfcommbuf[6] = l2capinbuf[17]; // Max Fram Size LSB + rfcommbuf[7] = l2capinbuf[18]; // Max Fram Size MSB + rfcommbuf[8] = l2capinbuf[19]; // MaxRatransm. + rfcommbuf[9] = l2capinbuf[20]; // Number of Frames + sendRfcomm(rfcommChannel, rfcommDirection, 0, RFCOMM_UIH, rfcommPfBit, rfcommbuf, 0x0A); // UIH Remote Port Negotiation Response + } else if(rfcommChannelType == RFCOMM_UIH && l2capinbuf[11] == BT_RFCOMM_MSC_CMD) { // UIH Modem Status Command +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nSend UIH Modem Status Response"), 0x80); +#endif + rfcommbuf[0] = BT_RFCOMM_MSC_RSP; // UIH Modem Status Response + rfcommbuf[1] = 2 << 1 | 1; // Length and shiftet like so: length << 1 | 1 + rfcommbuf[2] = l2capinbuf[13]; // Channel: (1 << 0) | (1 << 1) | (0 << 2) | (channel << 3) + rfcommbuf[3] = l2capinbuf[14]; + sendRfcomm(rfcommChannel, rfcommDirection, 0, RFCOMM_UIH, rfcommPfBit, rfcommbuf, 0x04); + } + } else { + if(rfcommChannelType == RFCOMM_SABM) { // SABM Command - this is sent twice: once for channel 0 and then for the channel to establish +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nReceived SABM Command"), 0x80); +#endif + sendRfcomm(rfcommChannel, rfcommDirection, rfcommCommandResponse, RFCOMM_UA, rfcommPfBit, rfcommbuf, 0x00); // UA Command + } else if(rfcommChannelType == RFCOMM_UIH && l2capinbuf[11] == BT_RFCOMM_PN_CMD) { // UIH Parameter Negotiation Command +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nReceived UIH Parameter Negotiation Command"), 0x80); +#endif + rfcommbuf[0] = BT_RFCOMM_PN_RSP; // UIH Parameter Negotiation Response + rfcommbuf[1] = l2capinbuf[12]; // Length and shiftet like so: length << 1 | 1 + rfcommbuf[2] = l2capinbuf[13]; // Channel: channel << 1 | 1 + rfcommbuf[3] = 0xE0; // Pre difined for Bluetooth, see 5.5.3 of TS 07.10 Adaption for RFCOMM + rfcommbuf[4] = 0x00; // Priority + rfcommbuf[5] = 0x00; // Timer + rfcommbuf[6] = BULK_MAXPKTSIZE - 14; // Max Fram Size LSB - set to the size of received data (50) + rfcommbuf[7] = 0x00; // Max Fram Size MSB + rfcommbuf[8] = 0x00; // MaxRatransm. + rfcommbuf[9] = 0x00; // Number of Frames + sendRfcomm(rfcommChannel, rfcommDirection, 0, RFCOMM_UIH, rfcommPfBit, rfcommbuf, 0x0A); + } else if(rfcommChannelType == RFCOMM_UIH && l2capinbuf[11] == BT_RFCOMM_MSC_CMD) { // UIH Modem Status Command +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nSend UIH Modem Status Response"), 0x80); +#endif + rfcommbuf[0] = BT_RFCOMM_MSC_RSP; // UIH Modem Status Response + rfcommbuf[1] = 2 << 1 | 1; // Length and shiftet like so: length << 1 | 1 + rfcommbuf[2] = l2capinbuf[13]; // Channel: (1 << 0) | (1 << 1) | (0 << 2) | (channel << 3) + rfcommbuf[3] = l2capinbuf[14]; + sendRfcomm(rfcommChannel, rfcommDirection, 0, RFCOMM_UIH, rfcommPfBit, rfcommbuf, 0x04); + + delay(1); +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nSend UIH Modem Status Command"), 0x80); +#endif + rfcommbuf[0] = BT_RFCOMM_MSC_CMD; // UIH Modem Status Command + rfcommbuf[1] = 2 << 1 | 1; // Length and shiftet like so: length << 1 | 1 + rfcommbuf[2] = l2capinbuf[13]; // Channel: (1 << 0) | (1 << 1) | (0 << 2) | (channel << 3) + rfcommbuf[3] = 0x8D; // Can receive frames (YES), Ready to Communicate (YES), Ready to Receive (YES), Incomig Call (NO), Data is Value (YES) + + sendRfcomm(rfcommChannel, rfcommDirection, 0, RFCOMM_UIH, rfcommPfBit, rfcommbuf, 0x04); + } else if(rfcommChannelType == RFCOMM_UIH && l2capinbuf[11] == BT_RFCOMM_MSC_RSP) { // UIH Modem Status Response + if(!creditSent) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nSend UIH Command with credit"), 0x80); +#endif + sendRfcommCredit(rfcommChannelConnection, rfcommDirection, 0, RFCOMM_UIH, 0x10, sizeof (rfcommDataBuffer)); // Send credit + creditSent = true; + timer = millis(); + waitForLastCommand = true; + } + } else if(rfcommChannelType == RFCOMM_UIH && l2capinbuf[10] == 0x01) { // UIH Command with credit +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nReceived UIH Command with credit"), 0x80); +#endif + } else if(rfcommChannelType == RFCOMM_UIH && l2capinbuf[11] == BT_RFCOMM_RPN_CMD) { // UIH Remote Port Negotiation Command +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nReceived UIH Remote Port Negotiation Command"), 0x80); +#endif + rfcommbuf[0] = BT_RFCOMM_RPN_RSP; // Command + rfcommbuf[1] = l2capinbuf[12]; // Length and shiftet like so: length << 1 | 1 + rfcommbuf[2] = l2capinbuf[13]; // Channel: channel << 1 | 1 + rfcommbuf[3] = l2capinbuf[14]; // Pre difined for Bluetooth, see 5.5.3 of TS 07.10 Adaption for RFCOMM + rfcommbuf[4] = l2capinbuf[15]; // Priority + rfcommbuf[5] = l2capinbuf[16]; // Timer + rfcommbuf[6] = l2capinbuf[17]; // Max Fram Size LSB + rfcommbuf[7] = l2capinbuf[18]; // Max Fram Size MSB + rfcommbuf[8] = l2capinbuf[19]; // MaxRatransm. + rfcommbuf[9] = l2capinbuf[20]; // Number of Frames + sendRfcomm(rfcommChannel, rfcommDirection, 0, RFCOMM_UIH, rfcommPfBit, rfcommbuf, 0x0A); // UIH Remote Port Negotiation Response +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nRFCOMM Connection is now established\r\n"), 0x80); +#endif + onInit(); + } +#ifdef EXTRADEBUG + else if(rfcommChannelType != RFCOMM_DISC) { + Notify(PSTR("\r\nUnsupported RFCOMM Data - ChannelType: "), 0x80); + D_PrintHex (rfcommChannelType, 0x80); + Notify(PSTR(" Command: "), 0x80); + D_PrintHex (l2capinbuf[11], 0x80); + } +#endif + } + } +#ifdef EXTRADEBUG + else { + Notify(PSTR("\r\nUnsupported L2CAP Data - Channel ID: "), 0x80); + D_PrintHex (l2capinbuf[7], 0x80); + Notify(PSTR(" "), 0x80); + D_PrintHex (l2capinbuf[6], 0x80); + } +#endif + SDP_task(); + RFCOMM_task(); + } +} + +void SPP::Run() { + if(waitForLastCommand && (millis() - timer) > 100) { // We will only wait 100ms and see if the UIH Remote Port Negotiation Command is send, as some deviced don't send it +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nRFCOMM Connection is now established - Automatic\r\n"), 0x80); +#endif + onInit(); + } + send(); // Send all bytes currently in the buffer +} + +void SPP::onInit() { + creditSent = false; + waitForLastCommand = false; + connected = true; // The RFCOMM channel is now established + sppIndex = 0; + if(pFuncOnInit) + pFuncOnInit(); // Call the user function +}; + +void SPP::SDP_task() { + switch(l2cap_sdp_state) { + case L2CAP_SDP_WAIT: + if(l2cap_check_flag(L2CAP_FLAG_CONNECTION_SDP_REQUEST)) { + l2cap_clear_flag(L2CAP_FLAG_CONNECTION_SDP_REQUEST); // Clear flag +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nSDP Incoming Connection Request"), 0x80); +#endif + pBtd->l2cap_connection_response(hci_handle, identifier, sdp_dcid, sdp_scid, PENDING); + delay(1); + pBtd->l2cap_connection_response(hci_handle, identifier, sdp_dcid, sdp_scid, SUCCESSFUL); + identifier++; + delay(1); + pBtd->l2cap_config_request(hci_handle, identifier, sdp_scid); + l2cap_sdp_state = L2CAP_SDP_SUCCESS; + } else if(l2cap_check_flag(L2CAP_FLAG_DISCONNECT_SDP_REQUEST)) { + l2cap_clear_flag(L2CAP_FLAG_DISCONNECT_SDP_REQUEST); // Clear flag + SDPConnected = false; +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nDisconnected SDP Channel"), 0x80); +#endif + pBtd->l2cap_disconnection_response(hci_handle, identifier, sdp_dcid, sdp_scid); + } + break; + case L2CAP_SDP_SUCCESS: + if(l2cap_check_flag(L2CAP_FLAG_CONFIG_SDP_SUCCESS)) { + l2cap_clear_flag(L2CAP_FLAG_CONFIG_SDP_SUCCESS); // Clear flag +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nSDP Successfully Configured"), 0x80); +#endif + firstMessage = true; // Reset bool + SDPConnected = true; + l2cap_sdp_state = L2CAP_SDP_WAIT; + } + break; + + case L2CAP_DISCONNECT_RESPONSE: // This is for both disconnection response from the RFCOMM and SDP channel if they were connected + if(l2cap_check_flag(L2CAP_FLAG_DISCONNECT_RESPONSE)) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nDisconnected L2CAP Connection"), 0x80); +#endif + pBtd->hci_disconnect(hci_handle); + hci_handle = -1; // Reset handle + Reset(); + } + break; + } +} + +void SPP::RFCOMM_task() { + switch(l2cap_rfcomm_state) { + case L2CAP_RFCOMM_WAIT: + if(l2cap_check_flag(L2CAP_FLAG_CONNECTION_RFCOMM_REQUEST)) { + l2cap_clear_flag(L2CAP_FLAG_CONNECTION_RFCOMM_REQUEST); // Clear flag +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nRFCOMM Incoming Connection Request"), 0x80); +#endif + pBtd->l2cap_connection_response(hci_handle, identifier, rfcomm_dcid, rfcomm_scid, PENDING); + delay(1); + pBtd->l2cap_connection_response(hci_handle, identifier, rfcomm_dcid, rfcomm_scid, SUCCESSFUL); + identifier++; + delay(1); + pBtd->l2cap_config_request(hci_handle, identifier, rfcomm_scid); + l2cap_rfcomm_state = L2CAP_RFCOMM_SUCCESS; + } else if(l2cap_check_flag(L2CAP_FLAG_DISCONNECT_RFCOMM_REQUEST)) { + l2cap_clear_flag(L2CAP_FLAG_DISCONNECT_RFCOMM_REQUEST); // Clear flag + RFCOMMConnected = false; + connected = false; +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nDisconnected RFCOMM Channel"), 0x80); +#endif + pBtd->l2cap_disconnection_response(hci_handle, identifier, rfcomm_dcid, rfcomm_scid); + } + break; + case L2CAP_RFCOMM_SUCCESS: + if(l2cap_check_flag(L2CAP_FLAG_CONFIG_RFCOMM_SUCCESS)) { + l2cap_clear_flag(L2CAP_FLAG_CONFIG_RFCOMM_SUCCESS); // Clear flag +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nRFCOMM Successfully Configured"), 0x80); +#endif + rfcommAvailable = 0; // Reset number of bytes available + bytesRead = 0; // Reset number of bytes received + RFCOMMConnected = true; + l2cap_rfcomm_state = L2CAP_RFCOMM_WAIT; + } + break; + } +} +/************************************************************/ +/* SDP Commands */ + +/************************************************************/ +void SPP::SDP_Command(uint8_t* data, uint8_t nbytes) { // See page 223 in the Bluetooth specs + pBtd->L2CAP_Command(hci_handle, data, nbytes, sdp_scid[0], sdp_scid[1]); +} + +void SPP::serviceNotSupported(uint8_t transactionIDHigh, uint8_t transactionIDLow) { // See page 235 in the Bluetooth specs + l2capoutbuf[0] = SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE_PDU; + l2capoutbuf[1] = transactionIDHigh; + l2capoutbuf[2] = transactionIDLow; + l2capoutbuf[3] = 0x00; // MSB Parameter Length + l2capoutbuf[4] = 0x05; // LSB Parameter Length = 5 + l2capoutbuf[5] = 0x00; // MSB AttributeListsByteCount + l2capoutbuf[6] = 0x02; // LSB AttributeListsByteCount = 2 + + /* Attribute ID/Value Sequence: */ + l2capoutbuf[7] = 0x35; // Data element sequence - length in next byte + l2capoutbuf[8] = 0x00; // Length = 0 + l2capoutbuf[9] = 0x00; // No continuation state + + SDP_Command(l2capoutbuf, 10); +} + +void SPP::serialPortResponse1(uint8_t transactionIDHigh, uint8_t transactionIDLow) { + l2capoutbuf[0] = SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE_PDU; + l2capoutbuf[1] = transactionIDHigh; + l2capoutbuf[2] = transactionIDLow; + l2capoutbuf[3] = 0x00; // MSB Parameter Length + l2capoutbuf[4] = 0x2B; // LSB Parameter Length = 43 + l2capoutbuf[5] = 0x00; // MSB AttributeListsByteCount + l2capoutbuf[6] = 0x26; // LSB AttributeListsByteCount = 38 + + /* Attribute ID/Value Sequence: */ + l2capoutbuf[7] = 0x36; // Data element sequence - length in next two bytes + l2capoutbuf[8] = 0x00; // MSB Length + l2capoutbuf[9] = 0x3C; // LSB Length = 60 + + l2capoutbuf[10] = 0x36; // Data element sequence - length in next two bytes + l2capoutbuf[11] = 0x00; // MSB Length + l2capoutbuf[12] = 0x39; // LSB Length = 57 + + l2capoutbuf[13] = 0x09; // Unsigned Integer - length 2 bytes + l2capoutbuf[14] = 0x00; // MSB ServiceRecordHandle + l2capoutbuf[15] = 0x00; // LSB ServiceRecordHandle + l2capoutbuf[16] = 0x0A; // Unsigned int - length 4 bytes + l2capoutbuf[17] = 0x00; // ServiceRecordHandle value - TODO: Is this related to HCI_Handle? + l2capoutbuf[18] = 0x01; + l2capoutbuf[19] = 0x00; + l2capoutbuf[20] = 0x06; + + l2capoutbuf[21] = 0x09; // Unsigned Integer - length 2 bytes + l2capoutbuf[22] = 0x00; // MSB ServiceClassIDList + l2capoutbuf[23] = 0x01; // LSB ServiceClassIDList + l2capoutbuf[24] = 0x35; // Data element sequence - length in next byte + l2capoutbuf[25] = 0x03; // Length = 3 + l2capoutbuf[26] = 0x19; // UUID (universally unique identifier) - length = 2 bytes + l2capoutbuf[27] = 0x11; // MSB SerialPort + l2capoutbuf[28] = 0x01; // LSB SerialPort + + l2capoutbuf[29] = 0x09; // Unsigned Integer - length 2 bytes + l2capoutbuf[30] = 0x00; // MSB ProtocolDescriptorList + l2capoutbuf[31] = 0x04; // LSB ProtocolDescriptorList + l2capoutbuf[32] = 0x35; // Data element sequence - length in next byte + l2capoutbuf[33] = 0x0C; // Length = 12 + + l2capoutbuf[34] = 0x35; // Data element sequence - length in next byte + l2capoutbuf[35] = 0x03; // Length = 3 + l2capoutbuf[36] = 0x19; // UUID (universally unique identifier) - length = 2 bytes + l2capoutbuf[37] = 0x01; // MSB L2CAP + l2capoutbuf[38] = 0x00; // LSB L2CAP + + l2capoutbuf[39] = 0x35; // Data element sequence - length in next byte + l2capoutbuf[40] = 0x05; // Length = 5 + l2capoutbuf[41] = 0x19; // UUID (universally unique identifier) - length = 2 bytes + l2capoutbuf[42] = 0x00; // MSB RFCOMM + l2capoutbuf[43] = 0x03; // LSB RFCOMM + l2capoutbuf[44] = 0x08; // Unsigned Integer - length 1 byte + + l2capoutbuf[45] = 0x02; // ContinuationState - Two more bytes + l2capoutbuf[46] = 0x00; // MSB length + l2capoutbuf[47] = 0x19; // LSB length = 25 more bytes to come + + SDP_Command(l2capoutbuf, 48); +} + +void SPP::serialPortResponse2(uint8_t transactionIDHigh, uint8_t transactionIDLow) { + l2capoutbuf[0] = SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE_PDU; + l2capoutbuf[1] = transactionIDHigh; + l2capoutbuf[2] = transactionIDLow; + l2capoutbuf[3] = 0x00; // MSB Parameter Length + l2capoutbuf[4] = 0x1C; // LSB Parameter Length = 28 + l2capoutbuf[5] = 0x00; // MSB AttributeListsByteCount + l2capoutbuf[6] = 0x19; // LSB AttributeListsByteCount = 25 + + /* Attribute ID/Value Sequence: */ + l2capoutbuf[7] = 0x01; // Channel 1 - TODO: Try different values, so multiple servers can be used at once + + l2capoutbuf[8] = 0x09; // Unsigned Integer - length 2 bytes + l2capoutbuf[9] = 0x00; // MSB LanguageBaseAttributeIDList + l2capoutbuf[10] = 0x06; // LSB LanguageBaseAttributeIDList + l2capoutbuf[11] = 0x35; // Data element sequence - length in next byte + l2capoutbuf[12] = 0x09; // Length = 9 + + // Identifier representing the natural language = en = English - see: "ISO 639:1988" + l2capoutbuf[13] = 0x09; // Unsigned Integer - length 2 bytes + l2capoutbuf[14] = 0x65; // 'e' + l2capoutbuf[15] = 0x6E; // 'n' + + // "The second element of each triplet contains an identifier that specifies a character encoding used for the language" + // Encoding is set to 106 (UTF-8) - see: http://www.iana.org/assignments/character-sets/character-sets.xhtml + l2capoutbuf[16] = 0x09; // Unsigned Integer - length 2 bytes + l2capoutbuf[17] = 0x00; // MSB of character encoding + l2capoutbuf[18] = 0x6A; // LSB of character encoding (106) + + // Attribute ID that serves as the base attribute ID for the natural language in the service record + // "To facilitate the retrieval of human-readable universal attributes in a principal language, the base attribute ID value for the primary language supported by a service record shall be 0x0100" + l2capoutbuf[19] = 0x09; // Unsigned Integer - length 2 bytes + l2capoutbuf[20] = 0x01; + l2capoutbuf[21] = 0x00; + + l2capoutbuf[22] = 0x09; // Unsigned Integer - length 2 bytes + l2capoutbuf[23] = 0x01; // MSB ServiceDescription + l2capoutbuf[24] = 0x00; // LSB ServiceDescription + + l2capoutbuf[25] = 0x25; // Text string - length in next byte + l2capoutbuf[26] = 0x05; // Name length + l2capoutbuf[27] = 'T'; + l2capoutbuf[28] = 'K'; + l2capoutbuf[29] = 'J'; + l2capoutbuf[30] = 'S'; + l2capoutbuf[31] = 'P'; + l2capoutbuf[32] = 0x00; // No continuation state + + SDP_Command(l2capoutbuf, 33); +} + +void SPP::l2capResponse1(uint8_t transactionIDHigh, uint8_t transactionIDLow) { + serialPortResponse1(transactionIDHigh, transactionIDLow); // These has to send all the supported functions, since it only supports virtual serialport it just sends the message again +} + +void SPP::l2capResponse2(uint8_t transactionIDHigh, uint8_t transactionIDLow) { + serialPortResponse2(transactionIDHigh, transactionIDLow); // Same data as serialPortResponse2 +} +/************************************************************/ +/* RFCOMM Commands */ + +/************************************************************/ +void SPP::RFCOMM_Command(uint8_t* data, uint8_t nbytes) { + pBtd->L2CAP_Command(hci_handle, data, nbytes, rfcomm_scid[0], rfcomm_scid[1]); +} + +void SPP::sendRfcomm(uint8_t channel, uint8_t direction, uint8_t CR, uint8_t channelType, uint8_t pfBit, uint8_t* data, uint8_t length) { + l2capoutbuf[0] = channel | direction | CR | extendAddress; // RFCOMM Address + l2capoutbuf[1] = channelType | pfBit; // RFCOMM Control + l2capoutbuf[2] = length << 1 | 0x01; // Length and format (always 0x01 bytes format) + uint8_t i = 0; + for(; i < length; i++) + l2capoutbuf[i + 3] = data[i]; + l2capoutbuf[i + 3] = calcFcs(l2capoutbuf); +#ifdef EXTRADEBUG + Notify(PSTR(" - RFCOMM Data: "), 0x80); + for(i = 0; i < length + 4; i++) { + D_PrintHex (l2capoutbuf[i], 0x80); + Notify(PSTR(" "), 0x80); + } +#endif + RFCOMM_Command(l2capoutbuf, length + 4); +} + +void SPP::sendRfcommCredit(uint8_t channel, uint8_t direction, uint8_t CR, uint8_t channelType, uint8_t pfBit, uint8_t credit) { + l2capoutbuf[0] = channel | direction | CR | extendAddress; // RFCOMM Address + l2capoutbuf[1] = channelType | pfBit; // RFCOMM Control + l2capoutbuf[2] = 0x01; // Length = 0 + l2capoutbuf[3] = credit; // Credit + l2capoutbuf[4] = calcFcs(l2capoutbuf); +#ifdef EXTRADEBUG + Notify(PSTR(" - RFCOMM Credit Data: "), 0x80); + for(uint8_t i = 0; i < 5; i++) { + D_PrintHex (l2capoutbuf[i], 0x80); + Notify(PSTR(" "), 0x80); + } +#endif + RFCOMM_Command(l2capoutbuf, 5); +} + +/* CRC on 2 bytes */ +uint8_t SPP::crc(uint8_t *data) { + return (pgm_read_byte(&rfcomm_crc_table[pgm_read_byte(&rfcomm_crc_table[0xFF ^ data[0]]) ^ data[1]])); +} + +/* Calculate FCS */ +uint8_t SPP::calcFcs(uint8_t *data) { + uint8_t temp = crc(data); + if((data[1] & 0xEF) == RFCOMM_UIH) + return (0xFF - temp); // FCS on 2 bytes + else + return (0xFF - pgm_read_byte(&rfcomm_crc_table[temp ^ data[2]])); // FCS on 3 bytes +} + +/* Check FCS */ +bool SPP::checkFcs(uint8_t *data, uint8_t fcs) { + uint8_t temp = crc(data); + if((data[1] & 0xEF) != RFCOMM_UIH) + temp = pgm_read_byte(&rfcomm_crc_table[temp ^ data[2]]); // FCS on 3 bytes + return (pgm_read_byte(&rfcomm_crc_table[temp ^ fcs]) == 0xCF); +} + +/* Serial commands */ +#if defined(ARDUINO) && ARDUINO >=100 + +size_t SPP::write(uint8_t data) { + return write(&data, 1); +} +#else + +void SPP::write(uint8_t data) { + write(&data, 1); +} +#endif + +#if defined(ARDUINO) && ARDUINO >=100 + +size_t SPP::write(const uint8_t *data, size_t size) { +#else + +void SPP::write(const uint8_t *data, size_t size) { +#endif + for(uint8_t i = 0; i < size; i++) { + if(sppIndex >= sizeof (sppOutputBuffer) / sizeof (sppOutputBuffer[0])) + send(); // Send the current data in the buffer + sppOutputBuffer[sppIndex++] = data[i]; // All the bytes are put into a buffer and then send using the send() function + } +#if defined(ARDUINO) && ARDUINO >=100 + return size; +#endif +} + +void SPP::send() { + if(!connected || !sppIndex) + return; + uint8_t length; // This is the length of the string we are sending + uint8_t offset = 0; // This is used to keep track of where we are in the string + + l2capoutbuf[0] = rfcommChannelConnection | 0 | 0 | extendAddress; // RFCOMM Address + l2capoutbuf[1] = RFCOMM_UIH; // RFCOMM Control + + while(sppIndex) { // We will run this while loop until this variable is 0 + if(sppIndex > (sizeof (l2capoutbuf) - 4)) // Check if the string is larger than the outgoing buffer + length = sizeof (l2capoutbuf) - 4; + else + length = sppIndex; + + l2capoutbuf[2] = length << 1 | 1; // Length + uint8_t i = 0; + for(; i < length; i++) + l2capoutbuf[i + 3] = sppOutputBuffer[i + offset]; + l2capoutbuf[i + 3] = calcFcs(l2capoutbuf); // Calculate checksum + + RFCOMM_Command(l2capoutbuf, length + 4); + + sppIndex -= length; + offset += length; // Increment the offset + } +} + +int SPP::available(void) { + return rfcommAvailable; +}; + +void SPP::discard(void) { + rfcommAvailable = 0; +} + +int SPP::peek(void) { + if(rfcommAvailable == 0) // Don't read if there is nothing in the buffer + return -1; + return rfcommDataBuffer[0]; +} + +int SPP::read(void) { + if(rfcommAvailable == 0) // Don't read if there is nothing in the buffer + return -1; + uint8_t output = rfcommDataBuffer[0]; + for(uint8_t i = 1; i < rfcommAvailable; i++) + rfcommDataBuffer[i - 1] = rfcommDataBuffer[i]; // Shift the buffer one left + rfcommAvailable--; + bytesRead++; + if(bytesRead > (sizeof (rfcommDataBuffer) - 5)) { // We will send the command just before it runs out of credit + bytesRead = 0; + sendRfcommCredit(rfcommChannelConnection, rfcommDirection, 0, RFCOMM_UIH, 0x10, sizeof (rfcommDataBuffer)); // Send more credit +#ifdef EXTRADEBUG + Notify(PSTR("\r\nSent "), 0x80); + Notify((uint8_t)sizeof (rfcommDataBuffer), 0x80); + Notify(PSTR(" more credit"), 0x80); +#endif + } + return output; +} diff --git a/libraries/USB_Host_Shield/SPP.h b/libraries/USB_Host_Shield/SPP.h new file mode 100755 index 0000000..233ac61 --- /dev/null +++ b/libraries/USB_Host_Shield/SPP.h @@ -0,0 +1,225 @@ +/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved. + + This software may be distributed and modified under the terms of the GNU + General Public License version 2 (GPL2) as published by the Free Software + Foundation and appearing in the file GPL2.TXT included in the packaging of + this file. Please note that GPL2 Section 2[b] requires that all works based + on this software must also be made publicly available under the terms of + the GPL2 ("Copyleft"). + + Contact information + ------------------- + + Kristian Lauszus, TKJ Electronics + Web : http://www.tkjelectronics.com + e-mail : kristianl@tkjelectronics.com + */ + +#ifndef _spp_h_ +#define _spp_h_ + +#include "BTD.h" + +/* Used for SDP */ +#define SDP_SERVICE_SEARCH_ATTRIBUTE_REQUEST_PDU 0x06 // See the RFCOMM specs +#define SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE_PDU 0x07 // See the RFCOMM specs +#define SERIALPORT_UUID 0x1101 // See http://www.bluetooth.org/Technical/AssignedNumbers/service_discovery.htm +#define L2CAP_UUID 0x0100 + +/* Used for RFCOMM */ +#define RFCOMM_SABM 0x2F +#define RFCOMM_UA 0x63 +#define RFCOMM_UIH 0xEF +//#define RFCOMM_DM 0x0F +#define RFCOMM_DISC 0x43 + +#define extendAddress 0x01 // Always 1 + +// Multiplexer message types +#define BT_RFCOMM_PN_CMD 0x83 +#define BT_RFCOMM_PN_RSP 0x81 +#define BT_RFCOMM_MSC_CMD 0xE3 +#define BT_RFCOMM_MSC_RSP 0xE1 +#define BT_RFCOMM_RPN_CMD 0x93 +#define BT_RFCOMM_RPN_RSP 0x91 +/* +#define BT_RFCOMM_TEST_CMD 0x23 +#define BT_RFCOMM_TEST_RSP 0x21 +#define BT_RFCOMM_FCON_CMD 0xA3 +#define BT_RFCOMM_FCON_RSP 0xA1 +#define BT_RFCOMM_FCOFF_CMD 0x63 +#define BT_RFCOMM_FCOFF_RSP 0x61 +#define BT_RFCOMM_RLS_CMD 0x53 +#define BT_RFCOMM_RLS_RSP 0x51 +#define BT_RFCOMM_NSC_RSP 0x11 + */ + +/** + * This BluetoothService class implements the Serial Port Protocol (SPP). + * It inherits the Arduino Stream class. This allows it to use all the standard Arduino print and stream functions. + */ +class SPP : public BluetoothService, public Stream { +public: + /** + * Constructor for the SPP class. + * @param p Pointer to BTD class instance. + * @param name Set the name to BTD#btdName. If argument is omitted, then "Arduino" will be used. + * @param pin Write the pin to BTD#btdPin. If argument is omitted, then "0000" will be used. + */ + SPP(BTD *p, const char *name = "Arduino", const char *pin = "0000"); + + /** @name BluetoothService implementation */ + /** Used this to disconnect the virtual serial port. */ + void disconnect(); + /**@}*/ + + /** + * Used to provide Boolean tests for the class. + * @return Return true if SPP communication is connected. + */ + operator bool() { + return connected; + } + /** Variable used to indicate if the connection is established. */ + bool connected; + + /** @name Serial port profile (SPP) Print functions */ + /** + * Get number of bytes waiting to be read. + * @return Return the number of bytes ready to be read. + */ + int available(void); + + /** Send out all bytes in the buffer. */ + void flush(void) { + send(); + }; + /** + * Used to read the next value in the buffer without advancing to the next one. + * @return Return the byte. Will return -1 if no bytes are available. + */ + int peek(void); + /** + * Used to read the buffer. + * @return Return the byte. Will return -1 if no bytes are available. + */ + int read(void); + +#if defined(ARDUINO) && ARDUINO >=100 + /** + * Writes the byte to send to a buffer. The message is send when either send() or after Usb.Task() is called. + * @param data The byte to write. + * @return Return the number of bytes written. + */ + size_t write(uint8_t data); + /** + * Writes the bytes to send to a buffer. The message is send when either send() or after Usb.Task() is called. + * @param data The data array to send. + * @param size Size of the data. + * @return Return the number of bytes written. + */ + size_t write(const uint8_t* data, size_t size); + /** Pull in write(const char *str) from Print */ + using Print::write; +#else + /** + * Writes the byte to send to a buffer. The message is send when either send() or after Usb.Task() is called. + * @param data The byte to write. + */ + void write(uint8_t data); + /** + * Writes the bytes to send to a buffer. The message is send when either send() or after Usb.Task() is called. + * @param data The data array to send. + * @param size Size of the data. + */ + void write(const uint8_t* data, size_t size); +#endif + + /** Discard all the bytes in the buffer. */ + void discard(void); + /** + * This will send all the bytes in the buffer. + * This is called whenever Usb.Task() is called, + * but can also be called via this function. + */ + void send(void); + /**@}*/ + +protected: + /** @name BluetoothService implementation */ + /** + * Used to pass acldata to the services. + * @param ACLData Incoming acldata. + */ + void ACLData(uint8_t* ACLData); + /** Used to establish the connection automatically. */ + void Run(); + /** Use this to reset the service. */ + void Reset(); + /** + * Called when a device is successfully initialized. + * Use attachOnInit(void (*funcOnInit)(void)) to call your own function. + * This is useful for instance if you want to set the LEDs in a specific way. + */ + void onInit(); + /**@}*/ + +private: + /* Set true when a channel is created */ + bool SDPConnected; + bool RFCOMMConnected; + + /* Variables used by L2CAP state machines */ + uint8_t l2cap_sdp_state; + uint8_t l2cap_rfcomm_state; + + uint8_t l2capoutbuf[BULK_MAXPKTSIZE]; // General purpose buffer for l2cap out data + uint8_t rfcommbuf[10]; // Buffer for RFCOMM Commands + + /* L2CAP Channels */ + uint8_t sdp_scid[2]; // L2CAP source CID for SDP + uint8_t sdp_dcid[2]; // 0x0050 + uint8_t rfcomm_scid[2]; // L2CAP source CID for RFCOMM + uint8_t rfcomm_dcid[2]; // 0x0051 + + /* RFCOMM Variables */ + uint8_t rfcommChannel; + uint8_t rfcommChannelConnection; // This is the channel the SPP channel will be running at + uint8_t rfcommDirection; + uint8_t rfcommCommandResponse; + uint8_t rfcommChannelType; + uint8_t rfcommPfBit; + + uint32_t timer; + bool waitForLastCommand; + bool creditSent; + + uint8_t rfcommDataBuffer[100]; // Create a 100 sized buffer for incoming data + uint8_t sppOutputBuffer[100]; // Create a 100 sized buffer for outgoing SPP data + uint8_t sppIndex; + uint8_t rfcommAvailable; + + bool firstMessage; // Used to see if it's the first SDP request received + uint8_t bytesRead; // Counter to see when it's time to send more credit + + /* State machines */ + void SDP_task(); // SDP state machine + void RFCOMM_task(); // RFCOMM state machine + + /* SDP Commands */ + void SDP_Command(uint8_t *data, uint8_t nbytes); + void serviceNotSupported(uint8_t transactionIDHigh, uint8_t transactionIDLow); + void serialPortResponse1(uint8_t transactionIDHigh, uint8_t transactionIDLow); + void serialPortResponse2(uint8_t transactionIDHigh, uint8_t transactionIDLow); + void l2capResponse1(uint8_t transactionIDHigh, uint8_t transactionIDLow); + void l2capResponse2(uint8_t transactionIDHigh, uint8_t transactionIDLow); + + /* RFCOMM Commands */ + void RFCOMM_Command(uint8_t *data, uint8_t nbytes); + void sendRfcomm(uint8_t channel, uint8_t direction, uint8_t CR, uint8_t channelType, uint8_t pfBit, uint8_t *data, uint8_t length); + void sendRfcommCredit(uint8_t channel, uint8_t direction, uint8_t CR, uint8_t channelType, uint8_t pfBit, uint8_t credit); + uint8_t calcFcs(uint8_t *data); + bool checkFcs(uint8_t *data, uint8_t fcs); + uint8_t crc(uint8_t *data); +}; +#endif diff --git a/libraries/USB_Host_Shield/Usb.cpp b/libraries/USB_Host_Shield/Usb.cpp new file mode 100755 index 0000000..3e07149 --- /dev/null +++ b/libraries/USB_Host_Shield/Usb.cpp @@ -0,0 +1,812 @@ +/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved. + +This software may be distributed and modified under the terms of the GNU +General Public License version 2 (GPL2) as published by the Free Software +Foundation and appearing in the file GPL2.TXT included in the packaging of +this file. Please note that GPL2 Section 2[b] requires that all works based +on this software must also be made publicly available under the terms of +the GPL2 ("Copyleft"). + +Contact information +------------------- + +Circuits At Home, LTD +Web : http://www.circuitsathome.com +e-mail : support@circuitsathome.com + */ +/* USB functions */ + +#include "Usb.h" + +static uint8_t usb_error = 0; +static uint8_t usb_task_state; + +/* constructor */ +USB::USB() : bmHubPre(0) { + usb_task_state = USB_DETACHED_SUBSTATE_INITIALIZE; //set up state machine + init(); +} + +/* Initialize data structures */ +void USB::init() { + //devConfigIndex = 0; + bmHubPre = 0; +} + +uint8_t USB::getUsbTaskState(void) { + return ( usb_task_state); +} + +void USB::setUsbTaskState(uint8_t state) { + usb_task_state = state; +} + +EpInfo* USB::getEpInfoEntry(uint8_t addr, uint8_t ep) { + UsbDevice *p = addrPool.GetUsbDevicePtr(addr); + + if(!p || !p->epinfo) + return NULL; + + EpInfo *pep = p->epinfo; + + for(uint8_t i = 0; i < p->epcount; i++) { + if((pep)->epAddr == ep) + return pep; + + pep++; + } + return NULL; +} + +/* set device table entry */ + +/* each device is different and has different number of endpoints. This function plugs endpoint record structure, defined in application, to devtable */ +uint8_t USB::setEpInfoEntry(uint8_t addr, uint8_t epcount, EpInfo* eprecord_ptr) { + if(!eprecord_ptr) + return USB_ERROR_INVALID_ARGUMENT; + + UsbDevice *p = addrPool.GetUsbDevicePtr(addr); + + if(!p) + return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; + + p->address.devAddress = addr; + p->epinfo = eprecord_ptr; + p->epcount = epcount; + + return 0; +} + +uint8_t USB::SetAddress(uint8_t addr, uint8_t ep, EpInfo **ppep, uint16_t *nak_limit) { + UsbDevice *p = addrPool.GetUsbDevicePtr(addr); + + if(!p) + return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; + + if(!p->epinfo) + return USB_ERROR_EPINFO_IS_NULL; + + *ppep = getEpInfoEntry(addr, ep); + + if(!*ppep) + return USB_ERROR_EP_NOT_FOUND_IN_TBL; + + *nak_limit = (0x0001UL << (((*ppep)->bmNakPower > USB_NAK_MAX_POWER) ? USB_NAK_MAX_POWER : (*ppep)->bmNakPower)); + *nak_limit--; + /* + USBTRACE2("\r\nAddress: ", addr); + USBTRACE2(" EP: ", ep); + USBTRACE2(" NAK Power: ",(*ppep)->bmNakPower); + USBTRACE2(" NAK Limit: ", nak_limit); + USBTRACE("\r\n"); + */ + regWr(rPERADDR, addr); //set peripheral address + + uint8_t mode = regRd(rMODE); + + //Serial.print("\r\nMode: "); + //Serial.println( mode, HEX); + //Serial.print("\r\nLS: "); + //Serial.println(p->lowspeed, HEX); + + + + // Set bmLOWSPEED and bmHUBPRE in case of low-speed device, reset them otherwise + regWr(rMODE, (p->lowspeed) ? mode | bmLOWSPEED | bmHubPre : mode & ~(bmHUBPRE | bmLOWSPEED)); + + return 0; +} + +/* Control transfer. Sets address, endpoint, fills control packet with necessary data, dispatches control packet, and initiates bulk IN transfer, */ +/* depending on request. Actual requests are defined as inlines */ +/* return codes: */ +/* 00 = success */ + +/* 01-0f = non-zero HRSLT */ +uint8_t USB::ctrlReq(uint8_t addr, uint8_t ep, uint8_t bmReqType, uint8_t bRequest, uint8_t wValLo, uint8_t wValHi, + uint16_t wInd, uint16_t total, uint16_t nbytes, uint8_t* dataptr, USBReadParser *p) { + bool direction = false; //request direction, IN or OUT + uint8_t rcode; + SETUP_PKT setup_pkt; + + EpInfo *pep = NULL; + uint16_t nak_limit = 0; + + rcode = SetAddress(addr, ep, &pep, &nak_limit); + + if(rcode) + return rcode; + + direction = ((bmReqType & 0x80) > 0); + + /* fill in setup packet */ + setup_pkt.ReqType_u.bmRequestType = bmReqType; + setup_pkt.bRequest = bRequest; + setup_pkt.wVal_u.wValueLo = wValLo; + setup_pkt.wVal_u.wValueHi = wValHi; + setup_pkt.wIndex = wInd; + setup_pkt.wLength = total; + + bytesWr(rSUDFIFO, 8, (uint8_t*) & setup_pkt); //transfer to setup packet FIFO + + rcode = dispatchPkt(tokSETUP, ep, nak_limit); //dispatch packet + + if(rcode) //return HRSLT if not zero + return ( rcode); + + if(dataptr != NULL) //data stage, if present + { + if(direction) //IN transfer + { + uint16_t left = total; + + pep->bmRcvToggle = 1; //bmRCVTOG1; + + while(left) { + // Bytes read into buffer + uint16_t read = nbytes; + //uint16_t read = (leftbmRcvToggle = (regRd(rHRSL) & bmSNDTOGRD) ? 0 : 1; + continue; + } + + if(rcode) + return rcode; + + // Invoke callback function if inTransfer completed successfully and callback function pointer is specified + if(!rcode && p) + ((USBReadParser*)p)->Parse(read, dataptr, total - left); + + left -= read; + + if(read < nbytes) + break; + } + } else //OUT transfer + { + pep->bmSndToggle = 1; //bmSNDTOG1; + rcode = OutTransfer(pep, nak_limit, nbytes, dataptr); + } + if(rcode) //return error + return ( rcode); + } + // Status stage + return dispatchPkt((direction) ? tokOUTHS : tokINHS, ep, nak_limit); //GET if direction +} + +/* IN transfer to arbitrary endpoint. Assumes PERADDR is set. Handles multiple packets if necessary. Transfers 'nbytes' bytes. */ +/* Keep sending INs and writes data to memory area pointed by 'data' */ + +/* rcode 0 if no errors. rcode 01-0f is relayed from dispatchPkt(). Rcode f0 means RCVDAVIRQ error, + fe USB xfer timeout */ +uint8_t USB::inTransfer(uint8_t addr, uint8_t ep, uint16_t *nbytesptr, uint8_t* data) { + EpInfo *pep = NULL; + uint16_t nak_limit = 0; + + uint8_t rcode = SetAddress(addr, ep, &pep, &nak_limit); + + if(rcode) { + USBTRACE3("(USB::InTransfer) SetAddress Failed ", rcode, 0x81); + USBTRACE3("(USB::InTransfer) addr requested ", addr, 0x81); + USBTRACE3("(USB::InTransfer) ep requested ", ep, 0x81); + return rcode; + } + return InTransfer(pep, nak_limit, nbytesptr, data); +} + +uint8_t USB::InTransfer(EpInfo *pep, uint16_t nak_limit, uint16_t *nbytesptr, uint8_t* data) { + uint8_t rcode = 0; + uint8_t pktsize; + + uint16_t nbytes = *nbytesptr; + //printf("Requesting %i bytes ", nbytes); + uint8_t maxpktsize = pep->maxPktSize; + + *nbytesptr = 0; + regWr(rHCTL, (pep->bmRcvToggle) ? bmRCVTOG1 : bmRCVTOG0); //set toggle value + + // use a 'break' to exit this loop + while(1) { + rcode = dispatchPkt(tokIN, pep->epAddr, nak_limit); //IN packet to EP-'endpoint'. Function takes care of NAKS. + if(rcode == hrTOGERR) { + // yes, we flip it wrong here so that next time it is actually correct! + pep->bmRcvToggle = (regRd(rHRSL) & bmSNDTOGRD) ? 0 : 1; + regWr(rHCTL, (pep->bmRcvToggle) ? bmRCVTOG1 : bmRCVTOG0); //set toggle value + continue; + } + if(rcode) { + //printf(">>>>>>>> Problem! dispatchPkt %2.2x\r\n", rcode); + break; //should be 0, indicating ACK. Else return error code. + } + /* check for RCVDAVIRQ and generate error if not present */ + /* the only case when absence of RCVDAVIRQ makes sense is when toggle error occurred. Need to add handling for that */ + if((regRd(rHIRQ) & bmRCVDAVIRQ) == 0) { + //printf(">>>>>>>> Problem! NO RCVDAVIRQ!\r\n"); + rcode = 0xf0; //receive error + break; + } + pktsize = regRd(rRCVBC); //number of received bytes + //printf("Got %i bytes \r\n", pktsize); + // This would be OK, but... + //assert(pktsize <= nbytes); + if(pktsize > nbytes) { + // This can happen. Use of assert on Arduino locks up the Arduino. + // So I will trim the value, and hope for the best. + //printf(">>>>>>>> Problem! Wanted %i bytes but got %i.\r\n", nbytes, pktsize); + pktsize = nbytes; + } + + int16_t mem_left = (int16_t)nbytes - *((int16_t*)nbytesptr); + + if(mem_left < 0) + mem_left = 0; + + data = bytesRd(rRCVFIFO, ((pktsize > mem_left) ? mem_left : pktsize), data); + + regWr(rHIRQ, bmRCVDAVIRQ); // Clear the IRQ & free the buffer + *nbytesptr += pktsize; // add this packet's byte count to total transfer length + + /* The transfer is complete under two conditions: */ + /* 1. The device sent a short packet (L.T. maxPacketSize) */ + /* 2. 'nbytes' have been transferred. */ + if((pktsize < maxpktsize) || (*nbytesptr >= nbytes)) // have we transferred 'nbytes' bytes? + { + // Save toggle value + pep->bmRcvToggle = ((regRd(rHRSL) & bmRCVTOGRD)) ? 1 : 0; + //printf("\r\n"); + rcode = 0; + break; + } // if + } //while( 1 ) + return ( rcode); +} + +/* OUT transfer to arbitrary endpoint. Handles multiple packets if necessary. Transfers 'nbytes' bytes. */ +/* Handles NAK bug per Maxim Application Note 4000 for single buffer transfer */ + +/* rcode 0 if no errors. rcode 01-0f is relayed from HRSL */ +uint8_t USB::outTransfer(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t* data) { + EpInfo *pep = NULL; + uint16_t nak_limit = 0; + + uint8_t rcode = SetAddress(addr, ep, &pep, &nak_limit); + + if(rcode) + return rcode; + + return OutTransfer(pep, nak_limit, nbytes, data); +} + +uint8_t USB::OutTransfer(EpInfo *pep, uint16_t nak_limit, uint16_t nbytes, uint8_t *data) { + uint8_t rcode = hrSUCCESS, retry_count; + uint8_t *data_p = data; //local copy of the data pointer + uint16_t bytes_tosend, nak_count; + uint16_t bytes_left = nbytes; + + uint8_t maxpktsize = pep->maxPktSize; + + if(maxpktsize < 1 || maxpktsize > 64) + return USB_ERROR_INVALID_MAX_PKT_SIZE; + + unsigned long timeout = millis() + USB_XFER_TIMEOUT; + + regWr(rHCTL, (pep->bmSndToggle) ? bmSNDTOG1 : bmSNDTOG0); //set toggle value + + while(bytes_left) { + retry_count = 0; + nak_count = 0; + bytes_tosend = (bytes_left >= maxpktsize) ? maxpktsize : bytes_left; + bytesWr(rSNDFIFO, bytes_tosend, data_p); //filling output FIFO + regWr(rSNDBC, bytes_tosend); //set number of bytes + regWr(rHXFR, (tokOUT | pep->epAddr)); //dispatch packet + while(!(regRd(rHIRQ) & bmHXFRDNIRQ)); //wait for the completion IRQ + regWr(rHIRQ, bmHXFRDNIRQ); //clear IRQ + rcode = (regRd(rHRSL) & 0x0f); + + while(rcode && ((long)(millis() - timeout) < 0L)) { + switch(rcode) { + case hrNAK: + nak_count++; + if(nak_limit && (nak_count == nak_limit)) + goto breakout; + //return ( rcode); + break; + case hrTIMEOUT: + retry_count++; + if(retry_count == USB_RETRY_LIMIT) + goto breakout; + //return ( rcode); + break; + case hrTOGERR: + // yes, we flip it wrong here so that next time it is actually correct! + pep->bmSndToggle = (regRd(rHRSL) & bmSNDTOGRD) ? 0 : 1; + regWr(rHCTL, (pep->bmSndToggle) ? bmSNDTOG1 : bmSNDTOG0); //set toggle value + break; + default: + goto breakout; + }//switch( rcode + + /* process NAK according to Host out NAK bug */ + regWr(rSNDBC, 0); + regWr(rSNDFIFO, *data_p); + regWr(rSNDBC, bytes_tosend); + regWr(rHXFR, (tokOUT | pep->epAddr)); //dispatch packet + while(!(regRd(rHIRQ) & bmHXFRDNIRQ)); //wait for the completion IRQ + regWr(rHIRQ, bmHXFRDNIRQ); //clear IRQ + rcode = (regRd(rHRSL) & 0x0f); + }//while( rcode && .... + bytes_left -= bytes_tosend; + data_p += bytes_tosend; + }//while( bytes_left... +breakout: + + pep->bmSndToggle = (regRd(rHRSL) & bmSNDTOGRD) ? 1 : 0; //bmSNDTOG1 : bmSNDTOG0; //update toggle + return ( rcode); //should be 0 in all cases +} +/* dispatch USB packet. Assumes peripheral address is set and relevant buffer is loaded/empty */ +/* If NAK, tries to re-send up to nak_limit times */ +/* If nak_limit == 0, do not count NAKs, exit after timeout */ +/* If bus timeout, re-sends up to USB_RETRY_LIMIT times */ + +/* return codes 0x00-0x0f are HRSLT( 0x00 being success ), 0xff means timeout */ +uint8_t USB::dispatchPkt(uint8_t token, uint8_t ep, uint16_t nak_limit) { + unsigned long timeout = millis() + USB_XFER_TIMEOUT; + uint8_t tmpdata; + uint8_t rcode = hrSUCCESS; + uint8_t retry_count = 0; + uint16_t nak_count = 0; + + while((long)(millis() - timeout) < 0L) { + regWr(rHXFR, (token | ep)); //launch the transfer + rcode = USB_ERROR_TRANSFER_TIMEOUT; + + while((long)(millis() - timeout) < 0L) //wait for transfer completion + { + tmpdata = regRd(rHIRQ); + + if(tmpdata & bmHXFRDNIRQ) { + regWr(rHIRQ, bmHXFRDNIRQ); //clear the interrupt + rcode = 0x00; + break; + }//if( tmpdata & bmHXFRDNIRQ + + }//while ( millis() < timeout + + //if (rcode != 0x00) //exit if timeout + // return ( rcode); + + rcode = (regRd(rHRSL) & 0x0f); //analyze transfer result + + switch(rcode) { + case hrNAK: + nak_count++; + if(nak_limit && (nak_count == nak_limit)) + return (rcode); + break; + case hrTIMEOUT: + retry_count++; + if(retry_count == USB_RETRY_LIMIT) + return (rcode); + break; + default: + return (rcode); + }//switch( rcode + + }//while( timeout > millis() + return ( rcode); +} + +/* USB main task. Performs enumeration/cleanup */ +void USB::Task(void) //USB state machine +{ + uint8_t rcode; + uint8_t tmpdata; + static unsigned long delay = 0; + //USB_DEVICE_DESCRIPTOR buf; + bool lowspeed = false; + + MAX3421E::Task(); + + tmpdata = getVbusState(); + + /* modify USB task state if Vbus changed */ + switch(tmpdata) { + case SE1: //illegal state + usb_task_state = USB_DETACHED_SUBSTATE_ILLEGAL; + lowspeed = false; + break; + case SE0: //disconnected + if((usb_task_state & USB_STATE_MASK) != USB_STATE_DETACHED) + usb_task_state = USB_DETACHED_SUBSTATE_INITIALIZE; + lowspeed = false; + break; + case LSHOST: + + lowspeed = true; + //intentional fallthrough + case FSHOST: //attached + if((usb_task_state & USB_STATE_MASK) == USB_STATE_DETACHED) { + delay = millis() + USB_SETTLE_DELAY; + usb_task_state = USB_ATTACHED_SUBSTATE_SETTLE; + } + break; + }// switch( tmpdata + + for(uint8_t i = 0; i < USB_NUMDEVICES; i++) + if(devConfig[i]) + rcode = devConfig[i]->Poll(); + + switch(usb_task_state) { + case USB_DETACHED_SUBSTATE_INITIALIZE: + init(); + + for(uint8_t i = 0; i < USB_NUMDEVICES; i++) + if(devConfig[i]) + rcode = devConfig[i]->Release(); + + usb_task_state = USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE; + break; + case USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE: //just sit here + break; + case USB_DETACHED_SUBSTATE_ILLEGAL: //just sit here + break; + case USB_ATTACHED_SUBSTATE_SETTLE: //settle time for just attached device + if((long)(millis() - delay) >= 0L) + usb_task_state = USB_ATTACHED_SUBSTATE_RESET_DEVICE; + else break; // don't fall through + case USB_ATTACHED_SUBSTATE_RESET_DEVICE: + regWr(rHCTL, bmBUSRST); //issue bus reset + usb_task_state = USB_ATTACHED_SUBSTATE_WAIT_RESET_COMPLETE; + break; + case USB_ATTACHED_SUBSTATE_WAIT_RESET_COMPLETE: + if((regRd(rHCTL) & bmBUSRST) == 0) { + tmpdata = regRd(rMODE) | bmSOFKAENAB; //start SOF generation + regWr(rMODE, tmpdata); + usb_task_state = USB_ATTACHED_SUBSTATE_WAIT_SOF; + //delay = millis() + 20; //20ms wait after reset per USB spec + } + break; + case USB_ATTACHED_SUBSTATE_WAIT_SOF: //todo: change check order + if(regRd(rHIRQ) & bmFRAMEIRQ) { + //when first SOF received _and_ 20ms has passed we can continue + /* + if (delay < millis()) //20ms passed + usb_task_state = USB_STATE_CONFIGURING; + */ + usb_task_state = USB_ATTACHED_SUBSTATE_WAIT_RESET; + delay = millis() + 20; + } + break; + case USB_ATTACHED_SUBSTATE_WAIT_RESET: + if((long)(millis() - delay) >= 0L) usb_task_state = USB_STATE_CONFIGURING; + else break; // don't fall through + case USB_STATE_CONFIGURING: + + //Serial.print("\r\nConf.LS: "); + //Serial.println(lowspeed, HEX); + + rcode = Configuring(0, 0, lowspeed); + + if(rcode) { + if(rcode != USB_DEV_CONFIG_ERROR_DEVICE_INIT_INCOMPLETE) { + usb_error = rcode; + usb_task_state = USB_STATE_ERROR; + } + } else + usb_task_state = USB_STATE_RUNNING; + break; + case USB_STATE_RUNNING: + break; + case USB_STATE_ERROR: + //MAX3421E::Init(); + break; + } // switch( usb_task_state ) +} + +uint8_t USB::DefaultAddressing(uint8_t parent, uint8_t port, bool lowspeed) { + //uint8_t buf[12]; + uint8_t rcode; + UsbDevice *p0 = NULL, *p = NULL; + + // Get pointer to pseudo device with address 0 assigned + p0 = addrPool.GetUsbDevicePtr(0); + + if(!p0) + return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; + + if(!p0->epinfo) + return USB_ERROR_EPINFO_IS_NULL; + + p0->lowspeed = (lowspeed) ? true : false; + + // Allocate new address according to device class + uint8_t bAddress = addrPool.AllocAddress(parent, false, port); + + if(!bAddress) + return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL; + + p = addrPool.GetUsbDevicePtr(bAddress); + + if(!p) + return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; + + p->lowspeed = lowspeed; + + // Assign new address to the device + rcode = setAddr(0, 0, bAddress); + + if(rcode) { + addrPool.FreeAddress(bAddress); + bAddress = 0; + return rcode; + } + return 0; +}; + +uint8_t USB::AttemptConfig(uint8_t driver, uint8_t parent, uint8_t port, bool lowspeed) { + //printf("AttemptConfig: parent = %i, port = %i\r\n", parent, port); + uint8_t retries = 0; + +again: + uint8_t rcode = devConfig[driver]->ConfigureDevice(parent, port, lowspeed); + if(rcode == USB_ERROR_CONFIG_REQUIRES_ADDITIONAL_RESET) { + if(parent == 0) { + // Send a bus reset on the root interface. + regWr(rHCTL, bmBUSRST); //issue bus reset + delay(102); // delay 102ms, compensate for clock inaccuracy. + } else { + // reset parent port + devConfig[parent]->ResetHubPort(port); + } + } else if(rcode == hrJERR && retries < 3) { // Some devices returns this when plugged in - trying to initialize the device again usually works + delay(100); + retries++; + goto again; + } else if(rcode) + return rcode; + + rcode = devConfig[driver]->Init(parent, port, lowspeed); + if(rcode == hrJERR && retries < 3) { // Some devices returns this when plugged in - trying to initialize the device again usually works + delay(100); + retries++; + goto again; + } + if(rcode) { + // Issue a bus reset, because the device may be in a limbo state + if(parent == 0) { + // Send a bus reset on the root interface. + regWr(rHCTL, bmBUSRST); //issue bus reset + delay(102); // delay 102ms, compensate for clock inaccuracy. + } else { + // reset parent port + devConfig[parent]->ResetHubPort(port); + } + } + return rcode; +} + +/* + * This is broken. We need to enumerate differently. + * It causes major problems with several devices if detected in an unexpected order. + * + * + * Oleg - I wouldn't do anything before the newly connected device is considered sane. + * i.e.(delays are not indicated for brevity): + * 1. reset + * 2. GetDevDescr(); + * 3a. If ACK, continue with allocating address, addressing, etc. + * 3b. Else reset again, count resets, stop at some number (5?). + * 4. When max.number of resets is reached, toggle power/fail + * If desired, this could be modified by performing two resets with GetDevDescr() in the middle - however, from my experience, if a device answers to GDD() + * it doesn't need to be reset again + * New steps proposal: + * 1: get address pool instance. exit on fail + * 2: pUsb->getDevDescr(0, 0, constBufSize, (uint8_t*)buf). exit on fail. + * 3: bus reset, 100ms delay + * 4: set address + * 5: pUsb->setEpInfoEntry(bAddress, 1, epInfo), exit on fail + * 6: while (configurations) { + * for(each configuration) { + * for (each driver) { + * 6a: Ask device if it likes configuration. Returns 0 on OK. + * If successful, the driver configured device. + * The driver now owns the endpoints, and takes over managing them. + * The following will need codes: + * Everything went well, instance consumed, exit with success. + * Instance already in use, ignore it, try next driver. + * Not a supported device, ignore it, try next driver. + * Not a supported configuration for this device, ignore it, try next driver. + * Could not configure device, fatal, exit with fail. + * } + * } + * } + * 7: for(each driver) { + * 7a: Ask device if it knows this VID/PID. Acts exactly like 6a, but using VID/PID + * 8: if we get here, no driver likes the device plugged in, so exit failure. + * + */ +uint8_t USB::Configuring(uint8_t parent, uint8_t port, bool lowspeed) { + //uint8_t bAddress = 0; + //printf("Configuring: parent = %i, port = %i\r\n", parent, port); + uint8_t devConfigIndex; + uint8_t rcode = 0; + uint8_t buf[sizeof (USB_DEVICE_DESCRIPTOR)]; + USB_DEVICE_DESCRIPTOR *udd = reinterpret_cast(buf); + UsbDevice *p = NULL; + EpInfo *oldep_ptr = NULL; + EpInfo epInfo; + + epInfo.epAddr = 0; + epInfo.maxPktSize = 8; + epInfo.epAttribs = 0; + epInfo.bmNakPower = USB_NAK_MAX_POWER; + + //delay(2000); + AddressPool &addrPool = GetAddressPool(); + // Get pointer to pseudo device with address 0 assigned + p = addrPool.GetUsbDevicePtr(0); + if(!p) { + //printf("Configuring error: USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL\r\n"); + return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; + } + + // Save old pointer to EP_RECORD of address 0 + oldep_ptr = p->epinfo; + + // Temporary assign new pointer to epInfo to p->epinfo in order to + // avoid toggle inconsistence + + p->epinfo = &epInfo; + + p->lowspeed = lowspeed; + // Get device descriptor + rcode = getDevDescr(0, 0, sizeof (USB_DEVICE_DESCRIPTOR), (uint8_t*)buf); + + // Restore p->epinfo + p->epinfo = oldep_ptr; + + if(rcode) { + //printf("Configuring error: Can't get USB_DEVICE_DESCRIPTOR\r\n"); + return rcode; + } + + // to-do? + // Allocate new address according to device class + //bAddress = addrPool.AllocAddress(parent, false, port); + + uint16_t vid = udd->idVendor; + uint16_t pid = udd->idProduct; + uint8_t klass = udd->bDeviceClass; + uint8_t subklass = udd->bDeviceSubClass; + // Attempt to configure if VID/PID or device class matches with a driver + // Qualify with subclass too. + // + // VID/PID & class tests default to false for drivers not yet ported + // subclass defaults to true, so you don't have to define it if you don't have to. + // + for(devConfigIndex = 0; devConfigIndex < USB_NUMDEVICES; devConfigIndex++) { + if(!devConfig[devConfigIndex]) continue; // no driver + if(devConfig[devConfigIndex]->GetAddress()) continue; // consumed + if(devConfig[devConfigIndex]->DEVSUBCLASSOK(subklass) && (devConfig[devConfigIndex]->VIDPIDOK(vid, pid) || devConfig[devConfigIndex]->DEVCLASSOK(klass))) { + rcode = AttemptConfig(devConfigIndex, parent, port, lowspeed); + if(rcode != USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED) + break; + } + } + + if(devConfigIndex < USB_NUMDEVICES) { + return rcode; + } + + + // blindly attempt to configure + for(devConfigIndex = 0; devConfigIndex < USB_NUMDEVICES; devConfigIndex++) { + if(!devConfig[devConfigIndex]) continue; + if(devConfig[devConfigIndex]->GetAddress()) continue; // consumed + if(devConfig[devConfigIndex]->DEVSUBCLASSOK(subklass) && (devConfig[devConfigIndex]->VIDPIDOK(vid, pid) || devConfig[devConfigIndex]->DEVCLASSOK(klass))) continue; // If this is true it means it must have returned USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED above + rcode = AttemptConfig(devConfigIndex, parent, port, lowspeed); + + //printf("ERROR ENUMERATING %2.2x\r\n", rcode); + if(!(rcode == USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED || rcode == USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE)) { + // in case of an error dev_index should be reset to 0 + // in order to start from the very beginning the + // next time the program gets here + //if (rcode != USB_DEV_CONFIG_ERROR_DEVICE_INIT_INCOMPLETE) + // devConfigIndex = 0; + return rcode; + } + } + // if we get here that means that the device class is not supported by any of registered classes + rcode = DefaultAddressing(parent, port, lowspeed); + + return rcode; +} + +uint8_t USB::ReleaseDevice(uint8_t addr) { + if(!addr) + return 0; + + for(uint8_t i = 0; i < USB_NUMDEVICES; i++) { + if(!devConfig[i]) continue; + if(devConfig[i]->GetAddress() == addr) + return devConfig[i]->Release(); + } + return 0; +} + +#if 1 //!defined(USB_METHODS_INLINE) +//get device descriptor + +uint8_t USB::getDevDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t* dataptr) { + return ( ctrlReq(addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, 0x00, USB_DESCRIPTOR_DEVICE, 0x0000, nbytes, nbytes, dataptr, NULL)); +} +//get configuration descriptor + +uint8_t USB::getConfDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t conf, uint8_t* dataptr) { + return ( ctrlReq(addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, conf, USB_DESCRIPTOR_CONFIGURATION, 0x0000, nbytes, nbytes, dataptr, NULL)); +} + +/* Requests Configuration Descriptor. Sends two Get Conf Descr requests. The first one gets the total length of all descriptors, then the second one requests this + total length. The length of the first request can be shorter ( 4 bytes ), however, there are devices which won't work unless this length is set to 9 */ +uint8_t USB::getConfDescr(uint8_t addr, uint8_t ep, uint8_t conf, USBReadParser *p) { + const uint8_t bufSize = 64; + uint8_t buf[bufSize]; + USB_CONFIGURATION_DESCRIPTOR *ucd = reinterpret_cast(buf); + + uint8_t ret = getConfDescr(addr, ep, 9, conf, buf); + + if(ret) + return ret; + + uint16_t total = ucd->wTotalLength; + + //USBTRACE2("\r\ntotal conf.size:", total); + + return ( ctrlReq(addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, conf, USB_DESCRIPTOR_CONFIGURATION, 0x0000, total, bufSize, buf, p)); +} + +//get string descriptor + +uint8_t USB::getStrDescr(uint8_t addr, uint8_t ep, uint16_t ns, uint8_t index, uint16_t langid, uint8_t* dataptr) { + return ( ctrlReq(addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, index, USB_DESCRIPTOR_STRING, langid, ns, ns, dataptr, NULL)); +} +//set address + +uint8_t USB::setAddr(uint8_t oldaddr, uint8_t ep, uint8_t newaddr) { + uint8_t rcode = ctrlReq(oldaddr, ep, bmREQ_SET, USB_REQUEST_SET_ADDRESS, newaddr, 0x00, 0x0000, 0x0000, 0x0000, NULL, NULL); + //delay(2); //per USB 2.0 sect.9.2.6.3 + delay(300); // Older spec says you should wait at least 200ms + return rcode; + //return ( ctrlReq(oldaddr, ep, bmREQ_SET, USB_REQUEST_SET_ADDRESS, newaddr, 0x00, 0x0000, 0x0000, 0x0000, NULL, NULL)); +} +//set configuration + +uint8_t USB::setConf(uint8_t addr, uint8_t ep, uint8_t conf_value) { + return ( ctrlReq(addr, ep, bmREQ_SET, USB_REQUEST_SET_CONFIGURATION, conf_value, 0x00, 0x0000, 0x0000, 0x0000, NULL, NULL)); +} + +#endif // defined(USB_METHODS_INLINE) diff --git a/libraries/USB_Host_Shield/Usb.h b/libraries/USB_Host_Shield/Usb.h new file mode 100755 index 0000000..47bd626 --- /dev/null +++ b/libraries/USB_Host_Shield/Usb.h @@ -0,0 +1,41 @@ +/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved. + +This software may be distributed and modified under the terms of the GNU +General Public License version 2 (GPL2) as published by the Free Software +Foundation and appearing in the file GPL2.TXT included in the packaging of +this file. Please note that GPL2 Section 2[b] requires that all works based +on this software must also be made publicly available under the terms of +the GPL2 ("Copyleft"). + +Contact information +------------------- + +Circuits At Home, LTD +Web : http://www.circuitsathome.com +e-mail : support@circuitsathome.com + */ +/* USB functions */ +#ifndef _usb_h_ +#define _usb_h_ + +// WARNING: Do not change the order of includes, or stuff will break! +#include +#include +#include + +// None of these should ever be included by a driver, or a user's sketch. +#include "settings.h" +#include "printhex.h" +#include "message.h" +#include "hexdump.h" +#include "sink_parser.h" +#include "max3421e.h" +#include "address.h" +#include "avrpins.h" +#include "usb_ch9.h" +#include "usbhost.h" +#include "UsbCore.h" +#include "parsetools.h" +#include "confdescparser.h" + +#endif //_usb_h_ diff --git a/libraries/USB_Host_Shield/UsbCore.h b/libraries/USB_Host_Shield/UsbCore.h new file mode 100755 index 0000000..d326178 --- /dev/null +++ b/libraries/USB_Host_Shield/UsbCore.h @@ -0,0 +1,298 @@ +/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved. + +This software may be distributed and modified under the terms of the GNU +General Public License version 2 (GPL2) as published by the Free Software +Foundation and appearing in the file GPL2.TXT included in the packaging of +this file. Please note that GPL2 Section 2[b] requires that all works based +on this software must also be made publicly available under the terms of +the GPL2 ("Copyleft"). + +Contact information +------------------- + +Circuits At Home, LTD +Web : http://www.circuitsathome.com +e-mail : support@circuitsathome.com + */ + +#if !defined(_usb_h_) || defined(USBCORE_H) +#error "Never include UsbCore.h directly; include Usb.h instead" +#else +#define USBCORE_H + +// Not used anymore? If anyone uses this, please let us know so that this may be +// moved to the proper place, settings.h. +//#define USB_METHODS_INLINE + +/* shield pins. First parameter - SS pin, second parameter - INT pin */ +#ifdef BOARD_BLACK_WIDDOW +typedef MAX3421e MAX3421E; // Black Widow +#elif defined(CORE_TEENSY) && (defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__)) +#if EXT_RAM +typedef MAX3421e MAX3421E; // Teensy++ 2.0 with XMEM2 +#else +typedef MAX3421e MAX3421E; // Teensy++ 1.0 and 2.0 +#endif +#elif defined(BOARD_MEGA_ADK) +typedef MAX3421e MAX3421E; // Arduino Mega ADK +#elif defined(ARDUINO_AVR_BALANDUINO) +typedef MAX3421e MAX3421E; // Balanduino +#elif (defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__)) +typedef MAX3421e MAX3421E; // YaaCWk (Sanguino) +#else +typedef MAX3421e MAX3421E; // Official Arduinos (UNO, Duemilanove, Mega, 2560, Leonardo, Due etc.) or Teensy 2.0 and 3.0 +#endif + +/* Common setup data constant combinations */ +#define bmREQ_GET_DESCR USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_STANDARD|USB_SETUP_RECIPIENT_DEVICE //get descriptor request type +#define bmREQ_SET USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_STANDARD|USB_SETUP_RECIPIENT_DEVICE //set request type for all but 'set feature' and 'set interface' +#define bmREQ_CL_GET_INTF USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_INTERFACE //get interface request type + +// D7 data transfer direction (0 - host-to-device, 1 - device-to-host) +// D6-5 Type (0- standard, 1 - class, 2 - vendor, 3 - reserved) +// D4-0 Recipient (0 - device, 1 - interface, 2 - endpoint, 3 - other, 4..31 - reserved) + +// USB Device Classes +#define USB_CLASS_USE_CLASS_INFO 0x00 // Use Class Info in the Interface Descriptors +#define USB_CLASS_AUDIO 0x01 // Audio +#define USB_CLASS_COM_AND_CDC_CTRL 0x02 // Communications and CDC Control +#define USB_CLASS_HID 0x03 // HID +#define USB_CLASS_PHYSICAL 0x05 // Physical +#define USB_CLASS_IMAGE 0x06 // Image +#define USB_CLASS_PRINTER 0x07 // Printer +#define USB_CLASS_MASS_STORAGE 0x08 // Mass Storage +#define USB_CLASS_HUB 0x09 // Hub +#define USB_CLASS_CDC_DATA 0x0a // CDC-Data +#define USB_CLASS_SMART_CARD 0x0b // Smart-Card +#define USB_CLASS_CONTENT_SECURITY 0x0d // Content Security +#define USB_CLASS_VIDEO 0x0e // Video +#define USB_CLASS_PERSONAL_HEALTH 0x0f // Personal Healthcare +#define USB_CLASS_DIAGNOSTIC_DEVICE 0xdc // Diagnostic Device +#define USB_CLASS_WIRELESS_CTRL 0xe0 // Wireless Controller +#define USB_CLASS_MISC 0xef // Miscellaneous +#define USB_CLASS_APP_SPECIFIC 0xfe // Application Specific +#define USB_CLASS_VENDOR_SPECIFIC 0xff // Vendor Specific + +// Additional Error Codes +#define USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED 0xD1 +#define USB_DEV_CONFIG_ERROR_DEVICE_INIT_INCOMPLETE 0xD2 +#define USB_ERROR_UNABLE_TO_REGISTER_DEVICE_CLASS 0xD3 +#define USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL 0xD4 +#define USB_ERROR_HUB_ADDRESS_OVERFLOW 0xD5 +#define USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL 0xD6 +#define USB_ERROR_EPINFO_IS_NULL 0xD7 +#define USB_ERROR_INVALID_ARGUMENT 0xD8 +#define USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE 0xD9 +#define USB_ERROR_INVALID_MAX_PKT_SIZE 0xDA +#define USB_ERROR_EP_NOT_FOUND_IN_TBL 0xDB +#define USB_ERROR_CONFIG_REQUIRES_ADDITIONAL_RESET 0xE0 +#define USB_ERROR_FailGetDevDescr 0xE1 +#define USB_ERROR_FailSetDevTblEntry 0xE2 +#define USB_ERROR_FailGetConfDescr 0xE3 +#define USB_ERROR_TRANSFER_TIMEOUT 0xFF + +#define USB_XFER_TIMEOUT 5000 // (5000) USB transfer timeout in milliseconds, per section 9.2.6.1 of USB 2.0 spec +//#define USB_NAK_LIMIT 32000 // NAK limit for a transfer. 0 means NAKs are not counted +#define USB_RETRY_LIMIT 3 // 3 retry limit for a transfer +#define USB_SETTLE_DELAY 200 // settle delay in milliseconds + +#define USB_NUMDEVICES 16 //number of USB devices +//#define HUB_MAX_HUBS 7 // maximum number of hubs that can be attached to the host controller +#define HUB_PORT_RESET_DELAY 20 // hub port reset delay 10 ms recomended, can be up to 20 ms + +/* USB state machine states */ +#define USB_STATE_MASK 0xf0 + +#define USB_STATE_DETACHED 0x10 +#define USB_DETACHED_SUBSTATE_INITIALIZE 0x11 +#define USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE 0x12 +#define USB_DETACHED_SUBSTATE_ILLEGAL 0x13 +#define USB_ATTACHED_SUBSTATE_SETTLE 0x20 +#define USB_ATTACHED_SUBSTATE_RESET_DEVICE 0x30 +#define USB_ATTACHED_SUBSTATE_WAIT_RESET_COMPLETE 0x40 +#define USB_ATTACHED_SUBSTATE_WAIT_SOF 0x50 +#define USB_ATTACHED_SUBSTATE_WAIT_RESET 0x51 +#define USB_ATTACHED_SUBSTATE_GET_DEVICE_DESCRIPTOR_SIZE 0x60 +#define USB_STATE_ADDRESSING 0x70 +#define USB_STATE_CONFIGURING 0x80 +#define USB_STATE_RUNNING 0x90 +#define USB_STATE_ERROR 0xa0 + +class USBDeviceConfig { +public: + + virtual uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed) { + return 0; + } + + virtual uint8_t ConfigureDevice(uint8_t parent, uint8_t port, bool lowspeed) { + return 0; + } + + virtual uint8_t Release() { + return 0; + } + + virtual uint8_t Poll() { + return 0; + } + + virtual uint8_t GetAddress() { + return 0; + } + + virtual void ResetHubPort(uint8_t port) { + return; + } // Note used for hubs only! + + virtual bool VIDPIDOK(uint16_t vid, uint16_t pid) { + return false; + } + + virtual bool DEVCLASSOK(uint8_t klass) { + return false; + } + + virtual bool DEVSUBCLASSOK(uint8_t subklass) { + return true; + } + +}; + +/* USB Setup Packet Structure */ +typedef struct { + + union { // offset description + uint8_t bmRequestType; // 0 Bit-map of request type + + struct { + uint8_t recipient : 5; // Recipient of the request + uint8_t type : 2; // Type of request + uint8_t direction : 1; // Direction of data X-fer + } __attribute__((packed)); + } ReqType_u; + uint8_t bRequest; // 1 Request + + union { + uint16_t wValue; // 2 Depends on bRequest + + struct { + uint8_t wValueLo; + uint8_t wValueHi; + } __attribute__((packed)); + } wVal_u; + uint16_t wIndex; // 4 Depends on bRequest + uint16_t wLength; // 6 Depends on bRequest +} __attribute__((packed)) SETUP_PKT, *PSETUP_PKT; + + + +// Base class for incoming data parser + +class USBReadParser { +public: + virtual void Parse(const uint16_t len, const uint8_t *pbuf, const uint16_t &offset) = 0; +}; + +class USB : public MAX3421E { + AddressPoolImpl addrPool; + USBDeviceConfig* devConfig[USB_NUMDEVICES]; + uint8_t bmHubPre; + +public: + USB(void); + + void SetHubPreMask() { + bmHubPre |= bmHUBPRE; + }; + + void ResetHubPreMask() { + bmHubPre &= (~bmHUBPRE); + }; + + AddressPool& GetAddressPool() { + return (AddressPool&)addrPool; + }; + + uint8_t RegisterDeviceClass(USBDeviceConfig *pdev) { + for(uint8_t i = 0; i < USB_NUMDEVICES; i++) { + if(!devConfig[i]) { + devConfig[i] = pdev; + return 0; + } + } + return USB_ERROR_UNABLE_TO_REGISTER_DEVICE_CLASS; + }; + + void ForEachUsbDevice(UsbDeviceHandleFunc pfunc) { + addrPool.ForEachUsbDevice(pfunc); + }; + uint8_t getUsbTaskState(void); + void setUsbTaskState(uint8_t state); + + EpInfo* getEpInfoEntry(uint8_t addr, uint8_t ep); + uint8_t setEpInfoEntry(uint8_t addr, uint8_t epcount, EpInfo* eprecord_ptr); + + /* Control requests */ + uint8_t getDevDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t* dataptr); + uint8_t getConfDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t conf, uint8_t* dataptr); + + uint8_t getConfDescr(uint8_t addr, uint8_t ep, uint8_t conf, USBReadParser *p); + + uint8_t getStrDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t index, uint16_t langid, uint8_t* dataptr); + uint8_t setAddr(uint8_t oldaddr, uint8_t ep, uint8_t newaddr); + uint8_t setConf(uint8_t addr, uint8_t ep, uint8_t conf_value); + /**/ + uint8_t ctrlData(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t* dataptr, bool direction); + uint8_t ctrlStatus(uint8_t ep, bool direction, uint16_t nak_limit); + uint8_t inTransfer(uint8_t addr, uint8_t ep, uint16_t *nbytesptr, uint8_t* data); + uint8_t outTransfer(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t* data); + uint8_t dispatchPkt(uint8_t token, uint8_t ep, uint16_t nak_limit); + + void Task(void); + + uint8_t DefaultAddressing(uint8_t parent, uint8_t port, bool lowspeed); + uint8_t Configuring(uint8_t parent, uint8_t port, bool lowspeed); + uint8_t ReleaseDevice(uint8_t addr); + + uint8_t ctrlReq(uint8_t addr, uint8_t ep, uint8_t bmReqType, uint8_t bRequest, uint8_t wValLo, uint8_t wValHi, + uint16_t wInd, uint16_t total, uint16_t nbytes, uint8_t* dataptr, USBReadParser *p); + +private: + void init(); + uint8_t SetAddress(uint8_t addr, uint8_t ep, EpInfo **ppep, uint16_t *nak_limit); + uint8_t OutTransfer(EpInfo *pep, uint16_t nak_limit, uint16_t nbytes, uint8_t *data); + uint8_t InTransfer(EpInfo *pep, uint16_t nak_limit, uint16_t *nbytesptr, uint8_t *data); + uint8_t AttemptConfig(uint8_t driver, uint8_t parent, uint8_t port, bool lowspeed); +}; + +#if 0 //defined(USB_METHODS_INLINE) +//get device descriptor + +inline uint8_t USB::getDevDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t* dataptr) { + return ( ctrlReq(addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, 0x00, USB_DESCRIPTOR_DEVICE, 0x0000, nbytes, dataptr)); +} +//get configuration descriptor + +inline uint8_t USB::getConfDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t conf, uint8_t* dataptr) { + return ( ctrlReq(addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, conf, USB_DESCRIPTOR_CONFIGURATION, 0x0000, nbytes, dataptr)); +} +//get string descriptor + +inline uint8_t USB::getStrDescr(uint8_t addr, uint8_t ep, uint16_t nuint8_ts, uint8_t index, uint16_t langid, uint8_t* dataptr) { + return ( ctrlReq(addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, index, USB_DESCRIPTOR_STRING, langid, nuint8_ts, dataptr)); +} +//set address + +inline uint8_t USB::setAddr(uint8_t oldaddr, uint8_t ep, uint8_t newaddr) { + return ( ctrlReq(oldaddr, ep, bmREQ_SET, USB_REQUEST_SET_ADDRESS, newaddr, 0x00, 0x0000, 0x0000, NULL)); +} +//set configuration + +inline uint8_t USB::setConf(uint8_t addr, uint8_t ep, uint8_t conf_value) { + return ( ctrlReq(addr, ep, bmREQ_SET, USB_REQUEST_SET_CONFIGURATION, conf_value, 0x00, 0x0000, 0x0000, NULL)); +} + +#endif // defined(USB_METHODS_INLINE) + +#endif /* USBCORE_H */ diff --git a/libraries/USB_Host_Shield/Wii.cpp b/libraries/USB_Host_Shield/Wii.cpp new file mode 100755 index 0000000..008e532 --- /dev/null +++ b/libraries/USB_Host_Shield/Wii.cpp @@ -0,0 +1,1199 @@ +/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved. + + This software may be distributed and modified under the terms of the GNU + General Public License version 2 (GPL2) as published by the Free Software + Foundation and appearing in the file GPL2.TXT included in the packaging of + this file. Please note that GPL2 Section 2[b] requires that all works based + on this software must also be made publicly available under the terms of + the GPL2 ("Copyleft"). + + Contact information + ------------------- + + Kristian Lauszus, TKJ Electronics + Web : http://www.tkjelectronics.com + e-mail : kristianl@tkjelectronics.com + + IR camera support added by Allan Glover (adglover9.81@gmail.com) and Kristian Lauszus + */ + +#include "Wii.h" +// To enable serial debugging see "settings.h" +//#define EXTRADEBUG // Uncomment to get even more debugging data +//#define PRINTREPORT // Uncomment to print the report send by the Wii controllers + +const uint8_t WII_LEDS[] PROGMEM = { + 0x00, // OFF + 0x10, // LED1 + 0x20, // LED2 + 0x40, // LED3 + 0x80, // LED4 + + 0x90, // LED5 + 0xA0, // LED6 + 0xC0, // LED7 + 0xD0, // LED8 + 0xE0, // LED9 + 0xF0, // LED10 +}; + +const uint32_t WII_BUTTONS[] PROGMEM = { + 0x00008, // UP + 0x00002, // RIGHT + 0x00004, // DOWN + 0x00001, // LEFT + + 0, // Skip + 0x00010, // PLUS + 0x00100, // TWO + 0x00200, // ONE + + 0x01000, // MINUS + 0x08000, // HOME + 0x10000, // Z + 0x20000, // C + + 0x00400, // B + 0x00800, // A +}; +const uint32_t WII_PROCONTROLLER_BUTTONS[] PROGMEM = { + 0x00100, // UP + 0x00080, // RIGHT + 0x00040, // DOWN + 0x00200, // LEFT + + 0, // Skip + 0x00004, // PLUS + 0x20000, // L3 + 0x10000, // R3 + + 0x00010, // MINUS + 0x00008, // HOME + 0, 0, // Skip + + 0x04000, // B + 0x01000, // A + 0x00800, // X + 0x02000, // Y + + 0x00020, // L + 0x00002, // R + 0x08000, // ZL + 0x00400, // ZR +}; + +WII::WII(BTD *p, bool pair) : +BluetoothService(p) // Pointer to USB class instance - mandatory +{ + pBtd->pairWithWii = pair; + + HIDBuffer[0] = 0xA2; // HID BT DATA_request (0xA0) | Report Type (Output 0x02) + + /* Set device cid for the control and intterrupt channelse - LSB */ + control_dcid[0] = 0x60; // 0x0060 + control_dcid[1] = 0x00; + interrupt_dcid[0] = 0x61; // 0x0061 + interrupt_dcid[1] = 0x00; + + Reset(); +} + +void WII::Reset() { + wiimoteConnected = false; + nunchuckConnected = false; + motionPlusConnected = false; + activateNunchuck = false; + motionValuesReset = false; + activeConnection = false; + motionPlusInside = false; + pBtd->wiiUProController = false; + wiiUProControllerConnected = false; + l2cap_event_flag = 0; // Reset flags + l2cap_state = L2CAP_WAIT; +} + +void WII::disconnect() { // Use this void to disconnect any of the controllers + if(!motionPlusInside) { // The old Wiimote needs a delay after the first command or it will automatically reconnect + if(motionPlusConnected) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nDeactivating Motion Plus"), 0x80); +#endif + initExtension1(); // This will disable the Motion Plus extension + } + timer = millis() + 1000; // We have to wait for the message before the rest of the channels can be deactivated + } else + timer = millis(); // Don't wait + // First the HID interrupt channel has to be disconnected, then the HID control channel and finally the HCI connection + pBtd->l2cap_disconnection_request(hci_handle, ++identifier, interrupt_scid, interrupt_dcid); + Reset(); + l2cap_state = L2CAP_INTERRUPT_DISCONNECT; +} + +void WII::ACLData(uint8_t* l2capinbuf) { + if(!pBtd->l2capConnectionClaimed && pBtd->incomingWii && !wiimoteConnected && !activeConnection) { + if(l2capinbuf[8] == L2CAP_CMD_CONNECTION_REQUEST) { + if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == HID_CTRL_PSM) { + motionPlusInside = pBtd->motionPlusInside; + pBtd->incomingWii = false; + pBtd->l2capConnectionClaimed = true; // Claim that the incoming connection belongs to this service + activeConnection = true; + hci_handle = pBtd->hci_handle; // Store the HCI Handle for the connection + l2cap_state = L2CAP_WAIT; + } + } + } + + if(checkHciHandle(l2capinbuf, hci_handle)) { // acl_handle_ok + if((l2capinbuf[6] | (l2capinbuf[7] << 8)) == 0x0001U) { // l2cap_control - Channel ID for ACL-U + if(l2capinbuf[8] == L2CAP_CMD_COMMAND_REJECT) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nL2CAP Command Rejected - Reason: "), 0x80); + D_PrintHex (l2capinbuf[13], 0x80); + Notify(PSTR(" "), 0x80); + D_PrintHex (l2capinbuf[12], 0x80); + Notify(PSTR(" "), 0x80); + D_PrintHex (l2capinbuf[17], 0x80); + Notify(PSTR(" "), 0x80); + D_PrintHex (l2capinbuf[16], 0x80); + Notify(PSTR(" "), 0x80); + D_PrintHex (l2capinbuf[15], 0x80); + Notify(PSTR(" "), 0x80); + D_PrintHex (l2capinbuf[14], 0x80); +#endif + } else if(l2capinbuf[8] == L2CAP_CMD_CONNECTION_RESPONSE) { + if(((l2capinbuf[16] | (l2capinbuf[17] << 8)) == 0x0000) && ((l2capinbuf[18] | (l2capinbuf[19] << 8)) == SUCCESSFUL)) { // Success + if(l2capinbuf[14] == control_dcid[0] && l2capinbuf[15] == control_dcid[1]) { + //Notify(PSTR("\r\nHID Control Connection Complete"), 0x80); + identifier = l2capinbuf[9]; + control_scid[0] = l2capinbuf[12]; + control_scid[1] = l2capinbuf[13]; + l2cap_set_flag(L2CAP_FLAG_CONTROL_CONNECTED); + } else if(l2capinbuf[14] == interrupt_dcid[0] && l2capinbuf[15] == interrupt_dcid[1]) { + //Notify(PSTR("\r\nHID Interrupt Connection Complete"), 0x80); + identifier = l2capinbuf[9]; + interrupt_scid[0] = l2capinbuf[12]; + interrupt_scid[1] = l2capinbuf[13]; + l2cap_set_flag(L2CAP_FLAG_INTERRUPT_CONNECTED); + } + } + } else if(l2capinbuf[8] == L2CAP_CMD_CONNECTION_REQUEST) { +#ifdef EXTRADEBUG + Notify(PSTR("\r\nL2CAP Connection Request - PSM: "), 0x80); + D_PrintHex (l2capinbuf[13], 0x80); + Notify(PSTR(" "), 0x80); + D_PrintHex (l2capinbuf[12], 0x80); + Notify(PSTR(" SCID: "), 0x80); + D_PrintHex (l2capinbuf[15], 0x80); + Notify(PSTR(" "), 0x80); + D_PrintHex (l2capinbuf[14], 0x80); + Notify(PSTR(" Identifier: "), 0x80); + D_PrintHex (l2capinbuf[9], 0x80); +#endif + if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == HID_CTRL_PSM) { + identifier = l2capinbuf[9]; + control_scid[0] = l2capinbuf[14]; + control_scid[1] = l2capinbuf[15]; + l2cap_set_flag(L2CAP_FLAG_CONNECTION_CONTROL_REQUEST); + } else if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == HID_INTR_PSM) { + identifier = l2capinbuf[9]; + interrupt_scid[0] = l2capinbuf[14]; + interrupt_scid[1] = l2capinbuf[15]; + l2cap_set_flag(L2CAP_FLAG_CONNECTION_INTERRUPT_REQUEST); + } + } else if(l2capinbuf[8] == L2CAP_CMD_CONFIG_RESPONSE) { + if((l2capinbuf[16] | (l2capinbuf[17] << 8)) == 0x0000) { // Success + if(l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) { + //Notify(PSTR("\r\nHID Control Configuration Complete"), 0x80); + identifier = l2capinbuf[9]; + l2cap_set_flag(L2CAP_FLAG_CONFIG_CONTROL_SUCCESS); + } else if(l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) { + //Notify(PSTR("\r\nHID Interrupt Configuration Complete"), 0x80); + identifier = l2capinbuf[9]; + l2cap_set_flag(L2CAP_FLAG_CONFIG_INTERRUPT_SUCCESS); + } + } + } else if(l2capinbuf[8] == L2CAP_CMD_CONFIG_REQUEST) { + if(l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) { + //Notify(PSTR("\r\nHID Control Configuration Request"), 0x80); + pBtd->l2cap_config_response(hci_handle, l2capinbuf[9], control_scid); + } else if(l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) { + //Notify(PSTR("\r\nHID Interrupt Configuration Request"), 0x80); + pBtd->l2cap_config_response(hci_handle, l2capinbuf[9], interrupt_scid); + } + } else if(l2capinbuf[8] == L2CAP_CMD_DISCONNECT_REQUEST) { + if(l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nDisconnect Request: Control Channel"), 0x80); +#endif + identifier = l2capinbuf[9]; + pBtd->l2cap_disconnection_response(hci_handle, identifier, control_dcid, control_scid); + Reset(); + } else if(l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nDisconnect Request: Interrupt Channel"), 0x80); +#endif + identifier = l2capinbuf[9]; + pBtd->l2cap_disconnection_response(hci_handle, identifier, interrupt_dcid, interrupt_scid); + Reset(); + } + } else if(l2capinbuf[8] == L2CAP_CMD_DISCONNECT_RESPONSE) { + if(l2capinbuf[12] == control_scid[0] && l2capinbuf[13] == control_scid[1]) { + //Notify(PSTR("\r\nDisconnect Response: Control Channel"), 0x80); + identifier = l2capinbuf[9]; + l2cap_set_flag(L2CAP_FLAG_DISCONNECT_CONTROL_RESPONSE); + } else if(l2capinbuf[12] == interrupt_scid[0] && l2capinbuf[13] == interrupt_scid[1]) { + //Notify(PSTR("\r\nDisconnect Response: Interrupt Channel"), 0x80); + identifier = l2capinbuf[9]; + l2cap_set_flag(L2CAP_FLAG_DISCONNECT_INTERRUPT_RESPONSE); + } + } +#ifdef EXTRADEBUG + else { + identifier = l2capinbuf[9]; + Notify(PSTR("\r\nL2CAP Unknown Signaling Command: "), 0x80); + D_PrintHex (l2capinbuf[8], 0x80); + } +#endif + } else if(l2capinbuf[6] == interrupt_dcid[0] && l2capinbuf[7] == interrupt_dcid[1]) { // l2cap_interrupt + //Notify(PSTR("\r\nL2CAP Interrupt"), 0x80); + if(l2capinbuf[8] == 0xA1) { // HID_THDR_DATA_INPUT + if((l2capinbuf[9] >= 0x20 && l2capinbuf[9] <= 0x22) || (l2capinbuf[9] >= 0x30 && l2capinbuf[9] <= 0x37) || l2capinbuf[9] == 0x3e || l2capinbuf[9] == 0x3f) { // These reports include the buttons + if((l2capinbuf[9] >= 0x20 && l2capinbuf[9] <= 0x22) || l2capinbuf[9] == 0x31 || l2capinbuf[9] == 0x33) // These reports have no extensions bytes + ButtonState = (uint32_t)((l2capinbuf[10] & 0x1F) | ((uint16_t)(l2capinbuf[11] & 0x9F) << 8)); + else if(wiiUProControllerConnected) + ButtonState = (uint32_t)(((~l2capinbuf[23]) & 0xFE) | ((uint16_t)(~l2capinbuf[24]) << 8) | ((uint32_t)((~l2capinbuf[25]) & 0x03) << 16)); + else if(motionPlusConnected) { + if(l2capinbuf[20] & 0x02) // Only update the wiimote buttons, since the extension bytes are from the Motion Plus + ButtonState = (uint32_t)((l2capinbuf[10] & 0x1F) | ((uint16_t)(l2capinbuf[11] & 0x9F) << 8) | ((uint32_t)(ButtonState & 0xFFFF0000))); + else if(nunchuckConnected) // Update if it's a report from the Nunchuck + ButtonState = (uint32_t)((l2capinbuf[10] & 0x1F) | ((uint16_t)(l2capinbuf[11] & 0x9F) << 8) | ((uint32_t)((~l2capinbuf[20]) & 0x0C) << 14)); + //else if(classicControllerConnected) // Update if it's a report from the Classic Controller + } else if(nunchuckConnected) // The Nunchuck is directly connected + ButtonState = (uint32_t)((l2capinbuf[10] & 0x1F) | ((uint16_t)(l2capinbuf[11] & 0x9F) << 8) | ((uint32_t)((~l2capinbuf[20]) & 0x03) << 16)); + //else if(classicControllerConnected) // The Classic Controller is directly connected + else if(!unknownExtensionConnected) + ButtonState = (uint32_t)((l2capinbuf[10] & 0x1F) | ((uint16_t)(l2capinbuf[11] & 0x9F) << 8)); +#ifdef PRINTREPORT + Notify(PSTR("ButtonState: "), 0x80); + D_PrintHex (ButtonState, 0x80); + Notify(PSTR("\r\n"), 0x80); +#endif + if(ButtonState != OldButtonState) { + ButtonClickState = ButtonState & ~OldButtonState; // Update click state variable + OldButtonState = ButtonState; + } + } + if(l2capinbuf[9] == 0x31 || l2capinbuf[9] == 0x33 || l2capinbuf[9] == 0x35 || l2capinbuf[9] == 0x37) { // Read the accelerometer + accXwiimote = ((l2capinbuf[12] << 2) | (l2capinbuf[10] & 0x60 >> 5)) - 500; + accYwiimote = ((l2capinbuf[13] << 2) | (l2capinbuf[11] & 0x20 >> 4)) - 500; + accZwiimote = ((l2capinbuf[14] << 2) | (l2capinbuf[11] & 0x40 >> 5)) - 500; + } + switch(l2capinbuf[9]) { + case 0x20: // Status Information - (a1) 20 BB BB LF 00 00 VV +#ifdef EXTRADEBUG + Notify(PSTR("\r\nStatus report was received"), 0x80); +#endif + wiiState = l2capinbuf[12]; // (0x01: Battery is nearly empty), (0x02: An Extension Controller is connected), (0x04: Speaker enabled), (0x08: IR enabled), (0x10: LED1, 0x20: LED2, 0x40: LED3, 0x80: LED4) + batteryLevel = l2capinbuf[15]; // Update battery level +#ifdef DEBUG_USB_HOST + if(l2capinbuf[12] & 0x01) + Notify(PSTR("\r\nWARNING: Battery is nearly empty"), 0x80); +#endif + if(checkExtension) { // If this is false it means that the user must have called getBatteryLevel() + if(l2capinbuf[12] & 0x02) { // Check if a extension is connected +#ifdef DEBUG_USB_HOST + if(!unknownExtensionConnected) + Notify(PSTR("\r\nExtension connected"), 0x80); +#endif + unknownExtensionConnected = true; +#ifdef WIICAMERA + if(!isIRCameraEnabled()) // Don't activate the Motion Plus if we are trying to initialize the IR camera +#endif + setReportMode(false, 0x35); // Also read the extension + } else { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nExtension disconnected"), 0x80); +#endif + if(motionPlusConnected) { +#ifdef DEBUG_USB_HOST + Notify(PSTR(" - from Motion Plus"), 0x80); +#endif + wii_clear_flag(WII_FLAG_NUNCHUCK_CONNECTED); + if(!activateNunchuck) // If it's already trying to initialize the Nunchuck don't set it to false + nunchuckConnected = false; + //else if(classicControllerConnected) + } else if(nunchuckConnected) { +#ifdef DEBUG_USB_HOST + Notify(PSTR(" - Nunchuck"), 0x80); +#endif + nunchuckConnected = false; // It must be the Nunchuck controller then + wii_clear_flag(WII_FLAG_NUNCHUCK_CONNECTED); + onInit(); + setReportMode(false, 0x31); // If there is no extension connected we will read the buttons and accelerometer + } else + setReportMode(false, 0x31); // If there is no extension connected we will read the buttons and accelerometer + } + } else + checkExtension = true; // Check for extensions by default + break; + case 0x21: // Read Memory Data + if((l2capinbuf[12] & 0x0F) == 0) { // No error + // See: http://wiibrew.org/wiki/Wiimote/Extension_Controllers + if(l2capinbuf[16] == 0x00 && l2capinbuf[17] == 0xA4 && l2capinbuf[18] == 0x20 && l2capinbuf[19] == 0x00 && l2capinbuf[20] == 0x00) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nNunchuck connected"), 0x80); +#endif + wii_set_flag(WII_FLAG_NUNCHUCK_CONNECTED); + } else if(l2capinbuf[16] == 0x00 && (l2capinbuf[17] == 0xA6 || l2capinbuf[17] == 0xA4) && l2capinbuf[18] == 0x20 && l2capinbuf[19] == 0x00 && l2capinbuf[20] == 0x05) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nMotion Plus connected"), 0x80); +#endif + wii_set_flag(WII_FLAG_MOTION_PLUS_CONNECTED); + } else if(l2capinbuf[16] == 0x00 && l2capinbuf[17] == 0xA4 && l2capinbuf[18] == 0x20 && l2capinbuf[19] == 0x04 && l2capinbuf[20] == 0x05) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nMotion Plus activated in normal mode"), 0x80); +#endif + motionPlusConnected = true; +#ifdef WIICAMERA + if(!isIRCameraEnabled()) // Don't activate the Motion Plus if we are trying to initialize the IR camera +#endif + setReportMode(false, 0x35); // Also read the extension + } else if(l2capinbuf[16] == 0x00 && l2capinbuf[17] == 0xA4 && l2capinbuf[18] == 0x20 && l2capinbuf[19] == 0x05 && l2capinbuf[20] == 0x05) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nMotion Plus activated in Nunchuck pass-through mode"), 0x80); +#endif + activateNunchuck = false; + motionPlusConnected = true; + nunchuckConnected = true; +#ifdef WIICAMERA + if(!isIRCameraEnabled()) // Don't activate the Motion Plus if we are trying to initialize the IR camera +#endif + setReportMode(false, 0x35); // Also read the extension + } else if(l2capinbuf[16] == 0x00 && l2capinbuf[17] == 0xA6 && l2capinbuf[18] == 0x20 && (l2capinbuf[19] == 0x00 || l2capinbuf[19] == 0x04 || l2capinbuf[19] == 0x05 || l2capinbuf[19] == 0x07) && l2capinbuf[20] == 0x05) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nInactive Wii Motion Plus"), 0x80); + Notify(PSTR("\r\nPlease unplug the Motion Plus, disconnect the Wiimote and then replug the Motion Plus Extension"), 0x80); +#endif + stateCounter = 300; // Skip the rest in "WII_CHECK_MOTION_PLUS_STATE" + } else if(l2capinbuf[16] == 0x00 && l2capinbuf[17] == 0xA4 && l2capinbuf[18] == 0x20 && l2capinbuf[19] == 0x01 && l2capinbuf[20] == 0x20) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nWii U Pro Controller connected"), 0x80); +#endif + wiiUProControllerConnected = true; + } +#ifdef DEBUG_USB_HOST + else { + Notify(PSTR("\r\nUnknown Device: "), 0x80); + D_PrintHex (l2capinbuf[13], 0x80); + D_PrintHex (l2capinbuf[14], 0x80); + Notify(PSTR("\r\nData: "), 0x80); + for(uint8_t i = 0; i < ((l2capinbuf[12] >> 4) + 1); i++) { // bit 4-7 is the length-1 + D_PrintHex (l2capinbuf[15 + i], 0x80); + Notify(PSTR(" "), 0x80); + } + } +#endif + } +#ifdef EXTRADEBUG + else { + Notify(PSTR("\r\nReport Error: "), 0x80); + D_PrintHex (l2capinbuf[13], 0x80); + D_PrintHex (l2capinbuf[14], 0x80); + } +#endif + break; + case 0x22: // Acknowledge output report, return function result +#ifdef DEBUG_USB_HOST + if(l2capinbuf[13] != 0x00) { // Check if there is an error + Notify(PSTR("\r\nCommand failed: "), 0x80); + D_PrintHex (l2capinbuf[12], 0x80); + } +#endif + break; + case 0x30: // Core buttons - (a1) 30 BB BB + break; + case 0x31: // Core Buttons and Accelerometer - (a1) 31 BB BB AA AA AA + break; + case 0x32: // Core Buttons with 8 Extension bytes - (a1) 32 BB BB EE EE EE EE EE EE EE EE + break; + case 0x33: // Core Buttons with Accelerometer and 12 IR bytes - (a1) 33 BB BB AA AA AA II II II II II II II II II II II II +#ifdef WIICAMERA + // Read the IR data + IR_object_x1 = (l2capinbuf[15] | ((uint16_t)(l2capinbuf[17] & 0x30) << 4)); // x position + IR_object_y1 = (l2capinbuf[16] | ((uint16_t)(l2capinbuf[17] & 0xC0) << 2)); // y position + IR_object_s1 = (l2capinbuf[17] & 0x0F); // size value, 0-15 + + IR_object_x2 = (l2capinbuf[18] | ((uint16_t)(l2capinbuf[20] & 0x30) << 4)); + IR_object_y2 = (l2capinbuf[19] | ((uint16_t)(l2capinbuf[20] & 0xC0) << 2)); + IR_object_s2 = (l2capinbuf[20] & 0x0F); + + IR_object_x3 = (l2capinbuf[21] | ((uint16_t)(l2capinbuf[23] & 0x30) << 4)); + IR_object_y3 = (l2capinbuf[22] | ((uint16_t)(l2capinbuf[23] & 0xC0) << 2)); + IR_object_s3 = (l2capinbuf[23] & 0x0F); + + IR_object_x4 = (l2capinbuf[24] | ((uint16_t)(l2capinbuf[26] & 0x30) << 4)); + IR_object_y4 = (l2capinbuf[25] | ((uint16_t)(l2capinbuf[26] & 0xC0) << 2)); + IR_object_s4 = (l2capinbuf[26] & 0x0F); +#endif + break; + case 0x34: // Core Buttons with 19 Extension bytes - (a1) 34 BB BB EE EE EE EE EE EE EE EE EE EE EE EE EE EE EE EE EE EE EE + break; + /* 0x3e and 0x3f both give unknown report types when report mode is 0x3e or 0x3f with mode number 0x05 */ + case 0x3E: // Core Buttons with Accelerometer and 32 IR bytes + // (a1) 31 BB BB AA AA AA II II II II II II II II II II II II II II II II II II II II II II II II II II II II II II II II + // corresponds to output report mode 0x3e + + /**** for reading in full mode: DOES NOT WORK YET ****/ + /* When it works it will also have intensity and bounding box data */ + /* + IR_object_x1 = (l2capinbuf[13] | ((uint16_t)(l2capinbuf[15] & 0x30) << 4)); + IR_object_y1 = (l2capinbuf[14] | ((uint16_t)(l2capinbuf[15] & 0xC0) << 2)); + IR_object_s1 = (l2capinbuf[15] & 0x0F); + */ + break; + case 0x3F: + /* + IR_object_x1 = (l2capinbuf[13] | ((uint16_t)(l2capinbuf[15] & 0x30) << 4)); + IR_object_y1 = (l2capinbuf[14] | ((uint16_t)(l2capinbuf[15] & 0xC0) << 2)); + IR_object_s1 = (l2capinbuf[15] & 0x0F); + */ + break; + case 0x35: // Core Buttons and Accelerometer with 16 Extension Bytes + // (a1) 35 BB BB AA AA AA EE EE EE EE EE EE EE EE EE EE EE EE EE EE EE EE + if(motionPlusConnected) { + if(l2capinbuf[20] & 0x02) { // Check if it's a report from the Motion controller or the extension + if(motionValuesReset) { // We will only use the values when the gyro value has been set + gyroYawRaw = ((l2capinbuf[15] | ((l2capinbuf[18] & 0xFC) << 6)) - gyroYawZero); + gyroRollRaw = ((l2capinbuf[16] | ((l2capinbuf[19] & 0xFC) << 6)) - gyroRollZero); + gyroPitchRaw = ((l2capinbuf[17] | ((l2capinbuf[20] & 0xFC) << 6)) - gyroPitchZero); + + yawGyroSpeed = (double)gyroYawRaw / ((double)gyroYawZero / yawGyroScale); + rollGyroSpeed = -(double)gyroRollRaw / ((double)gyroRollZero / rollGyroScale); // We invert these values so they will fit the acc values + pitchGyroSpeed = (double)gyroPitchRaw / ((double)gyroPitchZero / pitchGyroScale); + + /* The onboard gyro has two ranges for slow and fast mode */ + if(!(l2capinbuf[18] & 0x02)) // Check if fast mode is used + yawGyroSpeed *= 4.545; + if(!(l2capinbuf[18] & 0x01)) // Check if fast mode is used + pitchGyroSpeed *= 4.545; + if(!(l2capinbuf[19] & 0x02)) // Check if fast mode is used + rollGyroSpeed *= 4.545; + + compPitch = (0.93 * (compPitch + (pitchGyroSpeed * (double)(micros() - timer) / 1000000)))+(0.07 * getWiimotePitch()); // Use a complimentary filter to calculate the angle + compRoll = (0.93 * (compRoll + (rollGyroSpeed * (double)(micros() - timer) / 1000000)))+(0.07 * getWiimoteRoll()); + + gyroYaw += (yawGyroSpeed * ((double)(micros() - timer) / 1000000)); + gyroRoll += (rollGyroSpeed * ((double)(micros() - timer) / 1000000)); + gyroPitch += (pitchGyroSpeed * ((double)(micros() - timer) / 1000000)); + timer = micros(); + /* + // Uncomment these lines to tune the gyro scale variabels + Notify(PSTR("\r\ngyroYaw: "), 0x80); + Notify(gyroYaw, 0x80); + Notify(PSTR("\tgyroRoll: "), 0x80); + Notify(gyroRoll, 0x80); + Notify(PSTR("\tgyroPitch: "), 0x80); + Notify(gyroPitch, 0x80); + */ + /* + Notify(PSTR("\twiimoteRoll: "), 0x80); + Notify(wiimoteRoll, 0x80); + Notify(PSTR("\twiimotePitch: "), 0x80); + Notify(wiimotePitch, 0x80); + */ + } else { + if((micros() - timer) > 1000000) { // Loop for 1 sec before resetting the values +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nThe gyro values has been reset"), 0x80); +#endif + gyroYawZero = (l2capinbuf[15] | ((l2capinbuf[18] & 0xFC) << 6)); + gyroRollZero = (l2capinbuf[16] | ((l2capinbuf[19] & 0xFC) << 6)); + gyroPitchZero = (l2capinbuf[17] | ((l2capinbuf[20] & 0xFC) << 6)); + + rollGyroScale = 500; // You might need to adjust these + pitchGyroScale = 400; + yawGyroScale = 415; + + gyroYaw = 0; + gyroRoll = 0; + gyroPitch = 0; + + motionValuesReset = true; + timer = micros(); + } + } + } else { + if(nunchuckConnected) { + hatValues[HatX] = l2capinbuf[15]; + hatValues[HatY] = l2capinbuf[16]; + accXnunchuck = ((l2capinbuf[17] << 2) | (l2capinbuf[20] & 0x10 >> 3)) - 416; + accYnunchuck = ((l2capinbuf[18] << 2) | (l2capinbuf[20] & 0x20 >> 4)) - 416; + accZnunchuck = (((l2capinbuf[19] & 0xFE) << 2) | (l2capinbuf[20] & 0xC0 >> 5)) - 416; + } + //else if(classicControllerConnected) { } + } + if(l2capinbuf[19] & 0x01) { + if(!extensionConnected) { + extensionConnected = true; + unknownExtensionConnected = true; +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nExtension connected to Motion Plus"), 0x80); +#endif + } + } else { + if(extensionConnected && !unknownExtensionConnected) { + extensionConnected = false; + unknownExtensionConnected = true; +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nExtension disconnected from Motion Plus"), 0x80); +#endif + nunchuckConnected = false; // There is no extension connected to the Motion Plus if this report is sent + } + } + + } else if(nunchuckConnected) { + hatValues[HatX] = l2capinbuf[15]; + hatValues[HatY] = l2capinbuf[16]; + accXnunchuck = ((l2capinbuf[17] << 2) | (l2capinbuf[20] & 0x0C >> 2)) - 416; + accYnunchuck = ((l2capinbuf[18] << 2) | (l2capinbuf[20] & 0x30 >> 4)) - 416; + accZnunchuck = ((l2capinbuf[19] << 2) | (l2capinbuf[20] & 0xC0 >> 6)) - 416; + } else if(wiiUProControllerConnected) { + hatValues[LeftHatX] = (l2capinbuf[15] | l2capinbuf[16] << 8); + hatValues[RightHatX] = (l2capinbuf[17] | l2capinbuf[18] << 8); + hatValues[LeftHatY] = (l2capinbuf[19] | l2capinbuf[20] << 8); + hatValues[RightHatY] = (l2capinbuf[21] | l2capinbuf[22] << 8); + } + break; +#ifdef DEBUG_USB_HOST + default: + Notify(PSTR("\r\nUnknown Report type: "), 0x80); + D_PrintHex (l2capinbuf[9], 0x80); + break; +#endif + } + } + } + L2CAP_task(); + } +} + +void WII::L2CAP_task() { + switch(l2cap_state) { + /* These states are used if the Wiimote is the host */ + case L2CAP_CONTROL_SUCCESS: + if(l2cap_check_flag(L2CAP_FLAG_CONFIG_CONTROL_SUCCESS)) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nHID Control Successfully Configured"), 0x80); +#endif + l2cap_state = L2CAP_INTERRUPT_SETUP; + } + break; + + case L2CAP_INTERRUPT_SETUP: + if(l2cap_check_flag(L2CAP_FLAG_CONNECTION_INTERRUPT_REQUEST)) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nHID Interrupt Incoming Connection Request"), 0x80); +#endif + pBtd->l2cap_connection_response(hci_handle, identifier, interrupt_dcid, interrupt_scid, PENDING); + delay(1); + pBtd->l2cap_connection_response(hci_handle, identifier, interrupt_dcid, interrupt_scid, SUCCESSFUL); + identifier++; + delay(1); + pBtd->l2cap_config_request(hci_handle, identifier, interrupt_scid); + + l2cap_state = L2CAP_INTERRUPT_CONFIG_REQUEST; + } + break; + + /* These states are used if the Arduino is the host */ + case L2CAP_CONTROL_CONNECT_REQUEST: + if(l2cap_check_flag(L2CAP_FLAG_CONTROL_CONNECTED)) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nSend HID Control Config Request"), 0x80); +#endif + identifier++; + pBtd->l2cap_config_request(hci_handle, identifier, control_scid); + l2cap_state = L2CAP_CONTROL_CONFIG_REQUEST; + } + break; + + case L2CAP_CONTROL_CONFIG_REQUEST: + if(l2cap_check_flag(L2CAP_FLAG_CONFIG_CONTROL_SUCCESS)) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nSend HID Interrupt Connection Request"), 0x80); +#endif + identifier++; + pBtd->l2cap_connection_request(hci_handle, identifier, interrupt_dcid, HID_INTR_PSM); + l2cap_state = L2CAP_INTERRUPT_CONNECT_REQUEST; + } + break; + + case L2CAP_INTERRUPT_CONNECT_REQUEST: + if(l2cap_check_flag(L2CAP_FLAG_INTERRUPT_CONNECTED)) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nSend HID Interrupt Config Request"), 0x80); +#endif + identifier++; + pBtd->l2cap_config_request(hci_handle, identifier, interrupt_scid); + l2cap_state = L2CAP_INTERRUPT_CONFIG_REQUEST; + } + break; + + case L2CAP_INTERRUPT_CONFIG_REQUEST: + if(l2cap_check_flag(L2CAP_FLAG_CONFIG_INTERRUPT_SUCCESS)) { // Now the HID channels is established +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nHID Channels Established"), 0x80); +#endif + pBtd->connectToWii = false; + pBtd->pairWithWii = false; + stateCounter = 0; + l2cap_state = WII_CHECK_MOTION_PLUS_STATE; + } + break; + + /* The next states are in run() */ + + case L2CAP_INTERRUPT_DISCONNECT: + if(l2cap_check_flag(L2CAP_FLAG_DISCONNECT_INTERRUPT_RESPONSE) && ((long)(millis() - timer) >= 0L)) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nDisconnected Interrupt Channel"), 0x80); +#endif + identifier++; + pBtd->l2cap_disconnection_request(hci_handle, identifier, control_scid, control_dcid); + l2cap_state = L2CAP_CONTROL_DISCONNECT; + } + break; + + case L2CAP_CONTROL_DISCONNECT: + if(l2cap_check_flag(L2CAP_FLAG_DISCONNECT_CONTROL_RESPONSE)) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nDisconnected Control Channel"), 0x80); +#endif + pBtd->hci_disconnect(hci_handle); + hci_handle = -1; // Reset handle + l2cap_event_flag = 0; // Reset flags + l2cap_state = L2CAP_WAIT; + } + break; + } +} + +void WII::Run() { + if(l2cap_state == L2CAP_INTERRUPT_DISCONNECT && ((long)(millis() - timer) >= 0L)) + L2CAP_task(); // Call the rest of the disconnection routine after we have waited long enough + + switch(l2cap_state) { + case L2CAP_WAIT: + if(pBtd->connectToWii && !pBtd->l2capConnectionClaimed && !wiimoteConnected && !activeConnection) { + pBtd->l2capConnectionClaimed = true; + activeConnection = true; + motionPlusInside = pBtd->motionPlusInside; +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nSend HID Control Connection Request"), 0x80); +#endif + hci_handle = pBtd->hci_handle; // Store the HCI Handle for the connection + l2cap_event_flag = 0; // Reset flags + identifier = 0; + pBtd->l2cap_connection_request(hci_handle, identifier, control_dcid, HID_CTRL_PSM); + l2cap_state = L2CAP_CONTROL_CONNECT_REQUEST; + } else if(l2cap_check_flag(L2CAP_FLAG_CONNECTION_CONTROL_REQUEST)) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nHID Control Incoming Connection Request"), 0x80); +#endif + pBtd->l2cap_connection_response(hci_handle, identifier, control_dcid, control_scid, PENDING); + delay(1); + pBtd->l2cap_connection_response(hci_handle, identifier, control_dcid, control_scid, SUCCESSFUL); + identifier++; + delay(1); + pBtd->l2cap_config_request(hci_handle, identifier, control_scid); + l2cap_state = L2CAP_CONTROL_SUCCESS; + } + break; + + case WII_CHECK_MOTION_PLUS_STATE: +#ifdef DEBUG_USB_HOST + if(stateCounter == 0) // Only print onnce + Notify(PSTR("\r\nChecking if a Motion Plus is connected"), 0x80); +#endif + stateCounter++; + if(stateCounter % 200 == 0) + checkMotionPresent(); // Check if there is a motion plus connected + if(wii_check_flag(WII_FLAG_MOTION_PLUS_CONNECTED)) { + stateCounter = 0; + l2cap_state = WII_INIT_MOTION_PLUS_STATE; + timer = micros(); + + if(unknownExtensionConnected) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nA extension is also connected"), 0x80); +#endif + activateNunchuck = true; // For we will just set this to true as this the only extension supported so far + } + + } else if(stateCounter == 601) { // We will try three times to check for the motion plus +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nNo Motion Plus was detected"), 0x80); +#endif + stateCounter = 0; + l2cap_state = WII_CHECK_EXTENSION_STATE; + } + break; + + case WII_CHECK_EXTENSION_STATE: // This is used to check if there is anything plugged in to the extension port +#ifdef DEBUG_USB_HOST + if(stateCounter == 0) // Only print onnce + Notify(PSTR("\r\nChecking if there is any extension connected"), 0x80); +#endif + stateCounter++; // We use this counter as there has to be a short delay between the commands + if(stateCounter == 1) + statusRequest(); // See if a new device has connected + if(stateCounter == 100) { + if(unknownExtensionConnected) // Check if there is a extension is connected to the port + initExtension1(); + else + stateCounter = 399; + } else if(stateCounter == 200) + initExtension2(); + else if(stateCounter == 300) { + readExtensionType(); + unknownExtensionConnected = false; + } else if(stateCounter == 400) { + stateCounter = 0; + l2cap_state = TURN_ON_LED; + } + break; + + case WII_INIT_MOTION_PLUS_STATE: + stateCounter++; + if(stateCounter == 1) + initMotionPlus(); + else if(stateCounter == 100) + activateMotionPlus(); + else if(stateCounter == 200) + readExtensionType(); // Check if it has been activated + else if(stateCounter == 300) { + stateCounter = 0; + unknownExtensionConnected = false; // The motion plus will send a status report when it's activated, we will set this to false so it doesn't reinitialize the Motion Plus + l2cap_state = TURN_ON_LED; + } + break; + + case TURN_ON_LED: + if(wii_check_flag(WII_FLAG_NUNCHUCK_CONNECTED)) + nunchuckConnected = true; + wiimoteConnected = true; + onInit(); + l2cap_state = L2CAP_DONE; + break; + + case L2CAP_DONE: + if(unknownExtensionConnected) { +#ifdef DEBUG_USB_HOST + if(stateCounter == 0) // Only print once + Notify(PSTR("\r\nChecking extension port"), 0x80); +#endif + stateCounter++; // We will use this counter as there has to be a short delay between the commands + if(stateCounter == 50) + statusRequest(); + else if(stateCounter == 100) + initExtension1(); + else if(stateCounter == 150) + if((extensionConnected && motionPlusConnected) || (unknownExtensionConnected && !motionPlusConnected)) + initExtension2(); + else + stateCounter = 299; // There is no extension connected + else if(stateCounter == 200) + readExtensionType(); + else if(stateCounter == 250) { + if(wii_check_flag(WII_FLAG_NUNCHUCK_CONNECTED)) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nNunchuck was reconnected"), 0x80); +#endif + activateNunchuck = true; + nunchuckConnected = true; + } + if(!motionPlusConnected) + stateCounter = 449; + } else if(stateCounter == 300) { + if(motionPlusConnected) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nReactivating the Motion Plus"), 0x80); +#endif + initMotionPlus(); + } else + stateCounter = 449; + } else if(stateCounter == 350) + activateMotionPlus(); + else if(stateCounter == 400) + readExtensionType(); // Check if it has been activated + else if(stateCounter == 450) { + onInit(); + stateCounter = 0; + unknownExtensionConnected = false; + } + } else + stateCounter = 0; + break; + } +} + +/************************************************************/ +/* HID Commands */ + +/************************************************************/ +void WII::HID_Command(uint8_t* data, uint8_t nbytes) { + if(motionPlusInside) + pBtd->L2CAP_Command(hci_handle, data, nbytes, interrupt_scid[0], interrupt_scid[1]); // It's the new Wiimote with the Motion Plus Inside or Wii U Pro controller + else + pBtd->L2CAP_Command(hci_handle, data, nbytes, control_scid[0], control_scid[1]); +} + +void WII::setAllOff() { + HIDBuffer[1] = 0x11; + HIDBuffer[2] = 0x00; + HID_Command(HIDBuffer, 3); +} + +void WII::setRumbleOff() { + HIDBuffer[1] = 0x11; + HIDBuffer[2] &= ~0x01; // Bit 0 control the rumble + HID_Command(HIDBuffer, 3); +} + +void WII::setRumbleOn() { + HIDBuffer[1] = 0x11; + HIDBuffer[2] |= 0x01; // Bit 0 control the rumble + HID_Command(HIDBuffer, 3); +} + +void WII::setRumbleToggle() { + HIDBuffer[1] = 0x11; + HIDBuffer[2] ^= 0x01; // Bit 0 control the rumble + HID_Command(HIDBuffer, 3); +} + +void WII::setLedRaw(uint8_t value) { + HIDBuffer[1] = 0x11; + HIDBuffer[2] = value | (HIDBuffer[2] & 0x01); // Keep the rumble bit + HID_Command(HIDBuffer, 3); +} + +void WII::setLedOff(LEDEnum a) { + HIDBuffer[1] = 0x11; + HIDBuffer[2] &= ~(pgm_read_byte(&WII_LEDS[(uint8_t)a])); + HID_Command(HIDBuffer, 3); +} + +void WII::setLedOn(LEDEnum a) { + if(a == OFF) + setLedRaw(0); + else { + HIDBuffer[1] = 0x11; + HIDBuffer[2] |= pgm_read_byte(&WII_LEDS[(uint8_t)a]); + HID_Command(HIDBuffer, 3); + } +} + +void WII::setLedToggle(LEDEnum a) { + HIDBuffer[1] = 0x11; + HIDBuffer[2] ^= pgm_read_byte(&WII_LEDS[(uint8_t)a]); + HID_Command(HIDBuffer, 3); +} + +void WII::setLedStatus() { + HIDBuffer[1] = 0x11; + HIDBuffer[2] = (HIDBuffer[2] & 0x01); // Keep the rumble bit + if(wiimoteConnected) + HIDBuffer[2] |= 0x10; // If it's connected LED1 will light up + if(motionPlusConnected) + HIDBuffer[2] |= 0x20; // If it's connected LED2 will light up + if(nunchuckConnected) + HIDBuffer[2] |= 0x40; // If it's connected LED3 will light up + + HID_Command(HIDBuffer, 3); +} + +uint8_t WII::getBatteryLevel() { + checkExtension = false; // This is needed so the library knows that the status response is a response to this function + statusRequest(); // This will update the battery level + return batteryLevel; +}; + +void WII::setReportMode(bool continuous, uint8_t mode) { + uint8_t cmd_buf[4]; + cmd_buf[0] = 0xA2; // HID BT DATA_request (0xA0) | Report Type (Output 0x02) + cmd_buf[1] = 0x12; + if(continuous) + cmd_buf[2] = 0x04 | (HIDBuffer[2] & 0x01); // Keep the rumble bit + else + cmd_buf[2] = 0x00 | (HIDBuffer[2] & 0x01); // Keep the rumble bit + cmd_buf[3] = mode; + HID_Command(cmd_buf, 4); +} + +void WII::statusRequest() { + uint8_t cmd_buf[3]; + cmd_buf[0] = 0xA2; // HID BT DATA_request (0xA0) | Report Type (Output 0x02) + cmd_buf[1] = 0x15; + cmd_buf[2] = (HIDBuffer[2] & 0x01); // Keep the rumble bit + HID_Command(cmd_buf, 3); +} + +/************************************************************/ +/* Memmory Commands */ + +/************************************************************/ +void WII::writeData(uint32_t offset, uint8_t size, uint8_t* data) { + uint8_t cmd_buf[23]; + cmd_buf[0] = 0xA2; // HID BT DATA_request (0xA0) | Report Type (Output 0x02) + cmd_buf[1] = 0x16; // Write data + cmd_buf[2] = 0x04 | (HIDBuffer[2] & 0x01); // Write to memory, clear bit 2 to write to EEPROM + cmd_buf[3] = (uint8_t)((offset & 0xFF0000) >> 16); + cmd_buf[4] = (uint8_t)((offset & 0xFF00) >> 8); + cmd_buf[5] = (uint8_t)(offset & 0xFF); + cmd_buf[6] = size; + uint8_t i = 0; + for(; i < size; i++) + cmd_buf[7 + i] = data[i]; + for(; i < 16; i++) // Set the rest to zero + cmd_buf[7 + i] = 0x00; + HID_Command(cmd_buf, 23); +} + +void WII::initExtension1() { + uint8_t buf[1]; + buf[0] = 0x55; + writeData(0xA400F0, 1, buf); +} + +void WII::initExtension2() { + uint8_t buf[1]; + buf[0] = 0x00; + writeData(0xA400FB, 1, buf); +} + +void WII::initMotionPlus() { + uint8_t buf[1]; + buf[0] = 0x55; + writeData(0xA600F0, 1, buf); +} + +void WII::activateMotionPlus() { + uint8_t buf[1]; + if(pBtd->wiiUProController) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nActivating Wii U Pro Controller"), 0x80); +#endif + buf[0] = 0x00; // It seems like you can send anything but 0x04, 0x05, and 0x07 + } else if(activateNunchuck) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nActivating Motion Plus in pass-through mode"), 0x80); +#endif + buf[0] = 0x05; // Activate nunchuck pass-through mode + }//else if(classicControllerConnected && extensionConnected) + //buf[0] = 0x07; + else { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nActivating Motion Plus in normal mode"), 0x80); +#endif + buf[0] = 0x04; // Don't use any extension + } + writeData(0xA600FE, 1, buf); +} + +void WII::readData(uint32_t offset, uint16_t size, bool EEPROM) { + uint8_t cmd_buf[8]; + cmd_buf[0] = 0xA2; // HID BT DATA_request (0xA0) | Report Type (Output 0x02) + cmd_buf[1] = 0x17; // Read data + if(EEPROM) + cmd_buf[2] = 0x00 | (HIDBuffer[2] & 0x01); // Read from EEPROM + else + cmd_buf[2] = 0x04 | (HIDBuffer[2] & 0x01); // Read from memory + cmd_buf[3] = (uint8_t)((offset & 0xFF0000) >> 16); + cmd_buf[4] = (uint8_t)((offset & 0xFF00) >> 8); + cmd_buf[5] = (uint8_t)(offset & 0xFF); + cmd_buf[6] = (uint8_t)((size & 0xFF00) >> 8); + cmd_buf[7] = (uint8_t)(size & 0xFF); + + HID_Command(cmd_buf, 8); +} + +void WII::readExtensionType() { + readData(0xA400FA, 6, false); +} + +void WII::readCalData() { + readData(0x0016, 8, true); +} + +void WII::checkMotionPresent() { + readData(0xA600FA, 6, false); +} + +/************************************************************/ +/* WII Commands */ + +/************************************************************/ + +bool WII::getButtonPress(ButtonEnum b) { // Return true when a button is pressed + if(wiiUProControllerConnected) + return (ButtonState & pgm_read_dword(&WII_PROCONTROLLER_BUTTONS[(uint8_t)b])); + else + return (ButtonState & pgm_read_dword(&WII_BUTTONS[(uint8_t)b])); +} + +bool WII::getButtonClick(ButtonEnum b) { // Only return true when a button is clicked + uint32_t button; + if(wiiUProControllerConnected) + button = pgm_read_dword(&WII_PROCONTROLLER_BUTTONS[(uint8_t)b]); + else + button = pgm_read_dword(&WII_BUTTONS[(uint8_t)b]); + bool click = (ButtonClickState & button); + ButtonClickState &= ~button; // clear "click" event + return click; +} + +uint8_t WII::getAnalogHat(HatEnum a) { + if(!nunchuckConnected) + return 127; // Return center position + else { + uint8_t output = hatValues[(uint8_t)a]; + if(output == 0xFF || output == 0x00) // The joystick will only read 255 or 0 when the cable is unplugged or initializing, so we will just return the center position + return 127; + else + return output; + } +} + +uint16_t WII::getAnalogHat(AnalogHatEnum a) { + if(!wiiUProControllerConnected) + return 2000; + else { + uint16_t output = hatValues[(uint8_t)a]; + if(output == 0x00) // The joystick will only read 0 when it is first initializing, so we will just return the center position + return 2000; + else + return output; + } +} + +void WII::onInit() { + if(pFuncOnInit) + pFuncOnInit(); // Call the user function + else + setLedStatus(); +} + +/************************************************************/ +/* The following functions are for the IR camera */ +/************************************************************/ + +#ifdef WIICAMERA + +void WII::IRinitialize() { // Turns on and initialises the IR camera + + enableIRCamera1(); +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nEnable IR Camera1 Complete"), 0x80); +#endif + delay(80); + + enableIRCamera2(); +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nEnable IR Camera2 Complete"), 0x80); +#endif + delay(80); + + write0x08Value(); +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nWrote hex number 0x08"), 0x80); +#endif + delay(80); + + writeSensitivityBlock1(); +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nWrote Sensitivity Block 1"), 0x80); +#endif + delay(80); + + writeSensitivityBlock2(); +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nWrote Sensitivity Block 2"), 0x80); +#endif + delay(80); + + uint8_t mode_num = 0x03; + setWiiModeNumber(mode_num); // Change input for whatever mode you want i.e. 0x01, 0x03, or 0x05 +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nSet Wii Mode Number To 0x"), 0x80); + D_PrintHex (mode_num, 0x80); +#endif + delay(80); + + write0x08Value(); +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nWrote Hex Number 0x08"), 0x80); +#endif + delay(80); + + setReportMode(false, 0x33); + //setReportMode(false, 0x3f); // For full reporting mode, doesn't work yet +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nSet Report Mode to 0x33"), 0x80); +#endif + delay(80); + + statusRequest(); // Used to update wiiState - call isIRCameraEnabled() afterwards to check if it actually worked +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nIR Initialized"), 0x80); +#endif +} + +void WII::enableIRCamera1() { + uint8_t cmd_buf[3]; + cmd_buf[0] = 0xA2; // HID BT DATA_request (0xA0) | Report Type (Output 0x02) + cmd_buf[1] = 0x13; // Output report 13 + cmd_buf[2] = 0x04 | (HIDBuffer[2] & 0x01); // Keep the rumble bit and sets bit 2 + HID_Command(cmd_buf, 3); +} + +void WII::enableIRCamera2() { + uint8_t cmd_buf[3]; + cmd_buf[0] = 0xA2; // HID BT DATA_request (0xA0) | Report Type (Output 0x02) + cmd_buf[1] = 0x1A; // Output report 1A + cmd_buf[2] = 0x04 | (HIDBuffer[2] & 0x01); // Keep the rumble bit and sets bit 2 + HID_Command(cmd_buf, 3); +} + +void WII::writeSensitivityBlock1() { + uint8_t buf[9]; + buf[0] = 0x00; + buf[1] = 0x00; + buf[2] = 0x00; + buf[3] = 0x00; + buf[4] = 0x00; + buf[5] = 0x00; + buf[6] = 0x90; + buf[7] = 0x00; + buf[8] = 0x41; + + writeData(0xB00000, 9, buf); +} + +void WII::writeSensitivityBlock2() { + uint8_t buf[2]; + buf[0] = 0x40; + buf[1] = 0x00; + + writeData(0xB0001A, 2, buf); +} + +void WII::write0x08Value() { + uint8_t cmd = 0x08; + writeData(0xb00030, 1, &cmd); +} + +void WII::setWiiModeNumber(uint8_t mode_number) { // mode_number in hex i.e. 0x03 for extended mode + writeData(0xb00033, 1, &mode_number); +} +#endif diff --git a/libraries/USB_Host_Shield/Wii.h b/libraries/USB_Host_Shield/Wii.h new file mode 100755 index 0000000..13ba8d5 --- /dev/null +++ b/libraries/USB_Host_Shield/Wii.h @@ -0,0 +1,478 @@ +/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved. + + This software may be distributed and modified under the terms of the GNU + General Public License version 2 (GPL2) as published by the Free Software + Foundation and appearing in the file GPL2.TXT included in the packaging of + this file. Please note that GPL2 Section 2[b] requires that all works based + on this software must also be made publicly available under the terms of + the GPL2 ("Copyleft"). + + Contact information + ------------------- + + Kristian Lauszus, TKJ Electronics + Web : http://www.tkjelectronics.com + e-mail : kristianl@tkjelectronics.com + + IR camera support added by Allan Glover (adglover9.81@gmail.com) and Kristian Lauszus + */ + +#ifndef _wii_h_ +#define _wii_h_ + +#include "BTD.h" +#include "controllerEnums.h" + +/* Wii event flags */ +#define WII_FLAG_MOTION_PLUS_CONNECTED 0x01 +#define WII_FLAG_NUNCHUCK_CONNECTED 0x02 + +#define wii_check_flag(flag) (wii_event_flag & (flag)) +#define wii_set_flag(flag) (wii_event_flag |= (flag)) +#define wii_clear_flag(flag) (wii_event_flag &= ~(flag)) + +/** Enum used to read the joystick on the Nunchuck. */ +enum HatEnum { + /** Read the x-axis on the Nunchuck joystick. */ + HatX = 0, + /** Read the y-axis on the Nunchuck joystick. */ + HatY = 1, +}; + +/** + * This BluetoothService class implements support for the Wiimote including the Nunchuck and Motion Plus extension. + * + * It also support the Wii U Pro Controller. + */ +class WII : public BluetoothService { +public: + /** + * Constructor for the WII class. + * @param p Pointer to BTD class instance. + * @param pair Set this to true in order to pair with the Wiimote. If the argument is omitted then it won't pair with it. + * One can use ::PAIR to set it to true. + */ + WII(BTD *p, bool pair = false); + + /** @name BluetoothService implementation */ + /** Used this to disconnect any of the controllers. */ + void disconnect(); + /**@}*/ + + /** @name Wii Controller functions */ + /** + * getButtonPress(Button b) will return true as long as the button is held down. + * + * While getButtonClick(Button b) will only return it once. + * + * So you instance if you need to increase a variable once you would use getButtonClick(Button b), + * but if you need to drive a robot forward you would use getButtonPress(Button b). + * @param b ::ButtonEnum to read. + * @return getButtonPress(ButtonEnum b) will return a true as long as a button is held down, while getButtonClick(ButtonEnum b) will return true once for each button press. + */ + bool getButtonPress(ButtonEnum b); + bool getButtonClick(ButtonEnum b); + /**@}*/ + + /** @name Wii Controller functions */ + + /** Call this to start the paring sequence with a controller */ + void pair(void) { + if(pBtd) + pBtd->pairWithWiimote(); + }; + /** + * Used to read the joystick of the Nunchuck. + * @param a Either ::HatX or ::HatY. + * @return Return the analog value in the range from approximately 25-230. + */ + uint8_t getAnalogHat(HatEnum a); + /** + * Used to read the joystick of the Wii U Pro Controller. + * @param a Either ::LeftHatX, ::LeftHatY, ::RightHatX or ::RightHatY. + * @return Return the analog value in the range from approximately 800-3200. + */ + uint16_t getAnalogHat(AnalogHatEnum a); + + /** + * Pitch calculated from the Wiimote. A complimentary filter is used if the Motion Plus is connected. + * @return Pitch in the range from 0-360. + */ + double getPitch() { + if(motionPlusConnected) + return compPitch; + return getWiimotePitch(); + }; + + /** + * Roll calculated from the Wiimote. A complimentary filter is used if the Motion Plus is connected. + * @return Roll in the range from 0-360. + */ + double getRoll() { + if(motionPlusConnected) + return compRoll; + return getWiimoteRoll(); + }; + + /** + * This is the yaw calculated by the gyro. + * + * NOTE: This angle will drift a lot and is only available if the Motion Plus extension is connected. + * @return The angle calculated using the gyro. + */ + double getYaw() { + return gyroYaw; + }; + + /** Used to set all LEDs and rumble off. */ + void setAllOff(); + /** Turn off rumble. */ + void setRumbleOff(); + /** Turn on rumble. */ + void setRumbleOn(); + /** Toggle rumble. */ + void setRumbleToggle(); + + /** + * Set LED value without using the ::LEDEnum. + * @param value See: ::LEDEnum. + */ + void setLedRaw(uint8_t value); + + /** Turn all LEDs off. */ + void setLedOff() { + setLedRaw(0); + }; + /** + * Turn the specific ::LEDEnum off. + * @param a The ::LEDEnum to turn off. + */ + void setLedOff(LEDEnum a); + /** + * Turn the specific ::LEDEnum on. + * @param a The ::LEDEnum to turn on. + */ + void setLedOn(LEDEnum a); + /** + * Toggle the specific ::LEDEnum. + * @param a The ::LEDEnum to toggle. + */ + void setLedToggle(LEDEnum a); + /** + * This will set the LEDs, so the user can see which connections are active. + * + * The first ::LEDEnum indicate that the Wiimote is connected, + * the second ::LEDEnum indicate indicate that a Motion Plus is also connected + * the third ::LEDEnum will indicate that a Nunchuck controller is also connected. + */ + void setLedStatus(); + + /** + * Return the battery level of the Wiimote. + * @return The battery level in the range 0-255. + */ + uint8_t getBatteryLevel(); + + /** + * Return the Wiimote state. + * @return See: http://wiibrew.org/wiki/Wiimote#0x20:_Status. + */ + uint8_t getWiiState() { + return wiiState; + }; + /**@}*/ + + /**@{*/ + /** Variable used to indicate if a Wiimote is connected. */ + bool wiimoteConnected; + /** Variable used to indicate if a Nunchuck controller is connected. */ + bool nunchuckConnected; + /** Variable used to indicate if a Nunchuck controller is connected. */ + bool motionPlusConnected; + /** Variable used to indicate if a Wii U Pro controller is connected. */ + bool wiiUProControllerConnected; + /**@}*/ + + /* IMU Data, might be usefull if you need to do something more advanced than just calculating the angle */ + + /**@{*/ + + /** Pitch and roll calculated from the accelerometer inside the Wiimote. */ + double getWiimotePitch() { + return (atan2(accYwiimote, accZwiimote) + PI) * RAD_TO_DEG; + }; + + double getWiimoteRoll() { + return (atan2(accXwiimote, accZwiimote) + PI) * RAD_TO_DEG; + }; + /**@}*/ + + /**@{*/ + + /** Pitch and roll calculated from the accelerometer inside the Nunchuck. */ + double getNunchuckPitch() { + return (atan2(accYnunchuck, accZnunchuck) + PI) * RAD_TO_DEG; + }; + + double getNunchuckRoll() { + return (atan2(accXnunchuck, accZnunchuck) + PI) * RAD_TO_DEG; + }; + /**@}*/ + + /**@{*/ + /** Accelerometer values used to calculate pitch and roll. */ + int16_t accXwiimote, accYwiimote, accZwiimote; + int16_t accXnunchuck, accYnunchuck, accZnunchuck; + /**@}*/ + + /* Variables for the gyro inside the Motion Plus */ + /** This is the pitch calculated by the gyro - use this to tune WII#pitchGyroScale. */ + double gyroPitch; + /** This is the roll calculated by the gyro - use this to tune WII#rollGyroScale. */ + double gyroRoll; + /** This is the yaw calculated by the gyro - use this to tune WII#yawGyroScale. */ + double gyroYaw; + + /**@{*/ + /** The speed in deg/s from the gyro. */ + double pitchGyroSpeed; + double rollGyroSpeed; + double yawGyroSpeed; + /**@}*/ + + /**@{*/ + /** You might need to fine-tune these values. */ + uint16_t pitchGyroScale; + uint16_t rollGyroScale; + uint16_t yawGyroScale; + /**@}*/ + + /**@{*/ + /** Raw value read directly from the Motion Plus. */ + int16_t gyroYawRaw; + int16_t gyroRollRaw; + int16_t gyroPitchRaw; + /**@}*/ + + /**@{*/ + /** These values are set when the controller is first initialized. */ + int16_t gyroYawZero; + int16_t gyroRollZero; + int16_t gyroPitchZero; + /**@}*/ + +#ifdef WIICAMERA + /** @name Wiimote IR camera functions + * You will have to set ::ENABLE_WII_IR_CAMERA in settings.h to 1 in order use the IR camera. + */ + /** Initialises the camera as per the steps from: http://wiibrew.org/wiki/Wiimote#IR_Camera */ + void IRinitialize(); + + /** + * IR object 1 x-position read from the Wii IR camera. + * @return The x-position of the object in the range 0-1023. + */ + uint16_t getIRx1() { + return IR_object_x1; + }; + + /** + * IR object 1 y-position read from the Wii IR camera. + * @return The y-position of the object in the range 0-767. + */ + uint16_t getIRy1() { + return IR_object_y1; + }; + + /** + * IR object 1 size read from the Wii IR camera. + * @return The size of the object in the range 0-15. + */ + uint8_t getIRs1() { + return IR_object_s1; + }; + + /** + * IR object 2 x-position read from the Wii IR camera. + * @return The x-position of the object in the range 0-1023. + */ + uint16_t getIRx2() { + return IR_object_x2; + }; + + /** + * IR object 2 y-position read from the Wii IR camera. + * @return The y-position of the object in the range 0-767. + */ + uint16_t getIRy2() { + return IR_object_y2; + }; + + /** + * IR object 2 size read from the Wii IR camera. + * @return The size of the object in the range 0-15. + */ + uint8_t getIRs2() { + return IR_object_s2; + }; + + /** + * IR object 3 x-position read from the Wii IR camera. + * @return The x-position of the object in the range 0-1023. + */ + uint16_t getIRx3() { + return IR_object_x3; + }; + + /** + * IR object 3 y-position read from the Wii IR camera. + * @return The y-position of the object in the range 0-767. + */ + uint16_t getIRy3() { + return IR_object_y3; + }; + + /** + * IR object 3 size read from the Wii IR camera. + * @return The size of the object in the range 0-15. + */ + uint8_t getIRs3() { + return IR_object_s3; + }; + + /** + * IR object 4 x-position read from the Wii IR camera. + * @return The x-position of the object in the range 0-1023. + */ + uint16_t getIRx4() { + return IR_object_x4; + }; + + /** + * IR object 4 y-position read from the Wii IR camera. + * @return The y-position of the object in the range 0-767. + */ + uint16_t getIRy4() { + return IR_object_y4; + }; + + /** + * IR object 4 size read from the Wii IR camera. + * @return The size of the object in the range 0-15. + */ + uint8_t getIRs4() { + return IR_object_s4; + }; + + /** + * Use this to check if the camera is enabled or not. + * If not call WII#IRinitialize to initialize the IR camera. + * @return True if it's enabled, false if not. + */ + bool isIRCameraEnabled() { + return (wiiState & 0x08); + }; + /**@}*/ +#endif + +protected: + /** @name BluetoothService implementation */ + /** + * Used to pass acldata to the services. + * @param ACLData Incoming acldata. + */ + void ACLData(uint8_t* ACLData); + /** Used to run part of the state machine. */ + void Run(); + /** Use this to reset the service. */ + void Reset(); + /** + * Called when the controller is successfully initialized. + * Use attachOnInit(void (*funcOnInit)(void)) to call your own function. + * This is useful for instance if you want to set the LEDs in a specific way. + */ + void onInit(); + /**@}*/ + +private: + + void L2CAP_task(); // L2CAP state machine + + /* Variables filled from HCI event management */ + bool activeConnection; // Used to indicate if it's already has established a connection + + /* Variables used by high level L2CAP task */ + uint8_t l2cap_state; + uint8_t wii_event_flag; // Used for Wii flags + + uint32_t ButtonState; + uint32_t OldButtonState; + uint32_t ButtonClickState; + uint16_t hatValues[4]; + + uint8_t HIDBuffer[3]; // Used to store HID commands + + uint16_t stateCounter; + bool unknownExtensionConnected; + bool extensionConnected; + bool checkExtension; // Set to false when getBatteryLevel() is called otherwise if should be true + bool motionPlusInside; // True if it's a new Wiimote with the Motion Plus extension build into it + + /* L2CAP Channels */ + uint8_t control_scid[2]; // L2CAP source CID for HID_Control + uint8_t control_dcid[2]; // 0x0060 + uint8_t interrupt_scid[2]; // L2CAP source CID for HID_Interrupt + uint8_t interrupt_dcid[2]; // 0x0061 + + /* HID Commands */ + void HID_Command(uint8_t* data, uint8_t nbytes); + void setReportMode(bool continuous, uint8_t mode); + + void writeData(uint32_t offset, uint8_t size, uint8_t* data); + void initExtension1(); + void initExtension2(); + + void statusRequest(); // Used to update the Wiimote state and battery level + + void readData(uint32_t offset, uint16_t size, bool EEPROM); + void readExtensionType(); + void readCalData(); + + void checkMotionPresent(); // Used to see if a Motion Plus is connected to the Wiimote + void initMotionPlus(); + void activateMotionPlus(); + + double compPitch; // Fusioned angle using a complimentary filter if the Motion Plus is connected + double compRoll; // Fusioned angle using a complimentary filter if the Motion Plus is connected + + bool activateNunchuck; + bool motionValuesReset; // This bool is true when the gyro values has been reset + uint32_t timer; + + uint8_t wiiState; // Stores the value in l2capinbuf[12] - (0x01: Battery is nearly empty), (0x02: An Extension Controller is connected), (0x04: Speaker enabled), (0x08: IR enabled), (0x10: LED1, 0x20: LED2, 0x40: LED3, 0x80: LED4) + uint8_t batteryLevel; + +#ifdef WIICAMERA + /* Private function and variables for the readings from the IR Camera */ + void enableIRCamera1(); // Sets bit 2 of output report 13 + void enableIRCamera2(); // Sets bit 2 of output report 1A + void writeSensitivityBlock1(); + void writeSensitivityBlock2(); + void write0x08Value(); + void setWiiModeNumber(uint8_t mode_number); + + uint16_t IR_object_x1; // IR x position 10 bits + uint16_t IR_object_y1; // IR y position 10 bits + uint8_t IR_object_s1; // IR size value + uint16_t IR_object_x2; + uint16_t IR_object_y2; + uint8_t IR_object_s2; + uint16_t IR_object_x3; // IR x position 10 bits + uint16_t IR_object_y3; // IR y position 10 bits + uint8_t IR_object_s3; // IR size value + uint16_t IR_object_x4; + uint16_t IR_object_y4; + uint8_t IR_object_s4; +#endif +}; +#endif diff --git a/libraries/USB_Host_Shield/WiiCameraReadme.md b/libraries/USB_Host_Shield/WiiCameraReadme.md new file mode 100755 index 0000000..8577d73 --- /dev/null +++ b/libraries/USB_Host_Shield/WiiCameraReadme.md @@ -0,0 +1,13 @@ +Please see for the complete capabilities of the Wii camera. The IR camera code was written based on the above website and with support from Kristian Lauszus. + +This library is large, if you run into memory problems when uploading to the Arduino, disable serial debugging. + +To enable the IR camera code, simply set ```ENABLE_WII_IR_CAMERA``` to 1 in [settings.h](settings.h). + +This library implements the following settings: + +* Report sensitivity mode: 00 00 00 00 00 00 90 00 41 40 00 Suggested by inio (high sensitivity) +* Data Format: Extended mode (0x03). Full mode is not working yet. The output reports 0x3e and 0x3f need tampering with + * In this mode the camera outputs x and y coordinates and a size dimension for the 4 brightest points. + +Again, read through to get an understanding of the camera and its settings. diff --git a/libraries/USB_Host_Shield/XBOXOLD.cpp b/libraries/USB_Host_Shield/XBOXOLD.cpp new file mode 100755 index 0000000..78e6e9a --- /dev/null +++ b/libraries/USB_Host_Shield/XBOXOLD.cpp @@ -0,0 +1,337 @@ +/* Copyright (C) 2013 Kristian Lauszus, TKJ Electronics. All rights reserved. + + This software may be distributed and modified under the terms of the GNU + General Public License version 2 (GPL2) as published by the Free Software + Foundation and appearing in the file GPL2.TXT included in the packaging of + this file. Please note that GPL2 Section 2[b] requires that all works based + on this software must also be made publicly available under the terms of + the GPL2 ("Copyleft"). + + Contact information + ------------------- + + Kristian Lauszus, TKJ Electronics + Web : http://www.tkjelectronics.com + e-mail : kristianl@tkjelectronics.com + */ + +#include "XBOXOLD.h" +// To enable serial debugging see "settings.h" +//#define EXTRADEBUG // Uncomment to get even more debugging data +//#define PRINTREPORT // Uncomment to print the report send by the Xbox controller + +/** Buttons on the controllers */ +const uint8_t XBOXOLD_BUTTONS[] PROGMEM = { + 0x01, // UP + 0x08, // RIGHT + 0x02, // DOWN + 0x04, // LEFT + + 0x20, // BACK + 0x10, // START + 0x40, // L3 + 0x80, // R3 + + // A, B, X, Y, BLACK, WHITE, L1, and R1 are analog buttons + 4, // BLACK + 5, // WHTIE + 6, // L1 + 7, // R1 + + 1, // B + 0, // A + 2, // X + 3, // Y +}; + +XBOXOLD::XBOXOLD(USB *p) : +pUsb(p), // pointer to USB class instance - mandatory +bAddress(0), // device address - mandatory +bPollEnable(false) { // don't start polling before dongle is connected + for(uint8_t i = 0; i < XBOX_MAX_ENDPOINTS; i++) { + epInfo[i].epAddr = 0; + epInfo[i].maxPktSize = (i) ? 0 : 8; + epInfo[i].epAttribs = 0; + epInfo[i].bmNakPower = (i) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER; + } + + if(pUsb) // register in USB subsystem + pUsb->RegisterDeviceClass(this); //set devConfig[] entry +} + +uint8_t XBOXOLD::Init(uint8_t parent, uint8_t port, bool lowspeed) { + uint8_t buf[sizeof (USB_DEVICE_DESCRIPTOR)]; + USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast(buf); + uint8_t rcode; + UsbDevice *p = NULL; + EpInfo *oldep_ptr = NULL; + uint16_t PID; + uint16_t VID; + + // get memory address of USB device address pool + AddressPool &addrPool = pUsb->GetAddressPool(); +#ifdef EXTRADEBUG + Notify(PSTR("\r\nXBOXUSB Init"), 0x80); +#endif + // check if address has already been assigned to an instance + if(bAddress) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nAddress in use"), 0x80); +#endif + return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE; + } + + // Get pointer to pseudo device with address 0 assigned + p = addrPool.GetUsbDevicePtr(0); + + if(!p) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nAddress not found"), 0x80); +#endif + return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; + } + + if(!p->epinfo) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nepinfo is null"), 0x80); +#endif + return USB_ERROR_EPINFO_IS_NULL; + } + + // Save old pointer to EP_RECORD of address 0 + oldep_ptr = p->epinfo; + + // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence + p->epinfo = epInfo; + + p->lowspeed = lowspeed; + + // Get device descriptor + rcode = pUsb->getDevDescr(0, 0, sizeof (USB_DEVICE_DESCRIPTOR), (uint8_t*)buf); // Get device descriptor - addr, ep, nbytes, data + // Restore p->epinfo + p->epinfo = oldep_ptr; + + if(rcode) + goto FailGetDevDescr; + + VID = udd->idVendor; + PID = udd->idProduct; + + if((VID != XBOX_VID && VID != MADCATZ_VID && VID != JOYTECH_VID) || (PID != XBOX_OLD_PID1 && PID != XBOX_OLD_PID2 && PID != XBOX_OLD_PID3 && PID != XBOX_OLD_PID4)) // Check if VID and PID match + goto FailUnknownDevice; + + // Allocate new address according to device class + bAddress = addrPool.AllocAddress(parent, false, port); + + if(!bAddress) + return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL; + + // Extract Max Packet Size from device descriptor + epInfo[0].maxPktSize = udd->bMaxPacketSize0; + + // Assign new address to the device + rcode = pUsb->setAddr(0, 0, bAddress); + if(rcode) { + p->lowspeed = false; + addrPool.FreeAddress(bAddress); + bAddress = 0; +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nsetAddr: "), 0x80); + D_PrintHex (rcode, 0x80); +#endif + return rcode; + } +#ifdef EXTRADEBUG + Notify(PSTR("\r\nAddr: "), 0x80); + D_PrintHex (bAddress, 0x80); +#endif + //delay(300); // Spec says you should wait at least 200ms + + p->lowspeed = false; + + //get pointer to assigned address record + p = addrPool.GetUsbDevicePtr(bAddress); + if(!p) + return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; + + p->lowspeed = lowspeed; + + // Assign epInfo to epinfo pointer - only EP0 is known + rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo); + if(rcode) + goto FailSetDevTblEntry; + + /* The application will work in reduced host mode, so we can save program and data + memory space. After verifying the VID we will use known values for the + configuration values for device, interface, endpoints and HID for the XBOX controllers */ + + /* Initialize data structures for endpoints of device */ + epInfo[ XBOX_INPUT_PIPE ].epAddr = 0x01; // XBOX report endpoint + epInfo[ XBOX_INPUT_PIPE ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT; + epInfo[ XBOX_INPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints + epInfo[ XBOX_INPUT_PIPE ].maxPktSize = EP_MAXPKTSIZE; + epInfo[ XBOX_INPUT_PIPE ].bmSndToggle = 0; + epInfo[ XBOX_INPUT_PIPE ].bmRcvToggle = 0; + epInfo[ XBOX_OUTPUT_PIPE ].epAddr = 0x02; // XBOX output endpoint + epInfo[ XBOX_OUTPUT_PIPE ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT; + epInfo[ XBOX_OUTPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints + epInfo[ XBOX_OUTPUT_PIPE ].maxPktSize = EP_MAXPKTSIZE; + epInfo[ XBOX_OUTPUT_PIPE ].bmSndToggle = 0; + epInfo[ XBOX_OUTPUT_PIPE ].bmRcvToggle = 0; + + rcode = pUsb->setEpInfoEntry(bAddress, 3, epInfo); + if(rcode) + goto FailSetDevTblEntry; + + delay(200); // Give time for address change + + rcode = pUsb->setConf(bAddress, epInfo[ XBOX_CONTROL_PIPE ].epAddr, 1); + if(rcode) + goto FailSetConfDescr; + +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nXbox Controller Connected\r\n"), 0x80); +#endif + if(pFuncOnInit) + pFuncOnInit(); // Call the user function + XboxConnected = true; + bPollEnable = true; + return 0; // Successful configuration + + /* Diagnostic messages */ +FailGetDevDescr: +#ifdef DEBUG_USB_HOST + NotifyFailGetDevDescr(); + goto Fail; +#endif + +FailSetDevTblEntry: +#ifdef DEBUG_USB_HOST + NotifyFailSetDevTblEntry(); + goto Fail; +#endif + +FailSetConfDescr: +#ifdef DEBUG_USB_HOST + NotifyFailSetConfDescr(); +#endif + goto Fail; + +FailUnknownDevice: +#ifdef DEBUG_USB_HOST + NotifyFailUnknownDevice(VID, PID); +#endif + rcode = USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED; + +Fail: +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nXbox Init Failed, error code: "), 0x80); + NotifyFail(rcode); +#endif + Release(); + return rcode; +} + +/* Performs a cleanup after failed Init() attempt */ +uint8_t XBOXOLD::Release() { + XboxConnected = false; + pUsb->GetAddressPool().FreeAddress(bAddress); + bAddress = 0; + bPollEnable = false; + return 0; +} + +uint8_t XBOXOLD::Poll() { + if(!bPollEnable) + return 0; + uint16_t BUFFER_SIZE = EP_MAXPKTSIZE; + pUsb->inTransfer(bAddress, epInfo[ XBOX_INPUT_PIPE ].epAddr, &BUFFER_SIZE, readBuf); // input on endpoint 1 + readReport(); +#ifdef PRINTREPORT + printReport(BUFFER_SIZE); // Uncomment "#define PRINTREPORT" to print the report send by the Xbox controller +#endif + return 0; +} + +void XBOXOLD::readReport() { + ButtonState = readBuf[2]; + + for(uint8_t i = 0; i < sizeof (buttonValues); i++) + buttonValues[i] = readBuf[i + 4]; // A, B, X, Y, BLACK, WHITE, L1, and R1 + + hatValue[LeftHatX] = (int16_t)(((uint16_t)readBuf[12] << 8) | readBuf[13]); + hatValue[LeftHatY] = (int16_t)(((uint16_t)readBuf[14] << 8) | readBuf[15]); + hatValue[RightHatX] = (int16_t)(((uint16_t)readBuf[16] << 8) | readBuf[17]); + hatValue[RightHatY] = (int16_t)(((uint16_t)readBuf[18] << 8) | readBuf[19]); + + //Notify(PSTR("\r\nButtonState"), 0x80); + //PrintHex(ButtonState, 0x80); + + if(ButtonState != OldButtonState || memcmp(buttonValues, oldButtonValues, sizeof (buttonValues)) != 0) { + ButtonClickState = ButtonState & ~OldButtonState; // Update click state variable + OldButtonState = ButtonState; + + for(uint8_t i = 0; i < sizeof (buttonValues); i++) { + if(oldButtonValues[i] == 0 && buttonValues[i] != 0) + buttonClicked[i] = true; // Update A, B, X, Y, BLACK, WHITE, L1, and R1 click state + oldButtonValues[i] = buttonValues[i]; + } + } +} + +void XBOXOLD::printReport(uint16_t length) { //Uncomment "#define PRINTREPORT" to print the report send by the Xbox controller +#ifdef PRINTREPORT + if(readBuf == NULL) + return; + for(uint8_t i = 0; i < length; i++) { + D_PrintHex (readBuf[i], 0x80); + Notify(PSTR(" "), 0x80); + } + Notify(PSTR("\r\n"), 0x80); +#endif +} + +uint8_t XBOXOLD::getButtonPress(ButtonEnum b) { + uint8_t button = pgm_read_byte(&XBOXOLD_BUTTONS[(uint8_t)b]); + if(b == A || b == B || b == X || b == Y || b == BLACK || b == WHITE || b == L1 || b == R1) // A, B, X, Y, BLACK, WHITE, L1, and R1 are analog buttons + return buttonValues[button]; // Analog buttons + return (ButtonState & button); // Digital buttons +} + +bool XBOXOLD::getButtonClick(ButtonEnum b) { + uint8_t button = pgm_read_byte(&XBOXOLD_BUTTONS[(uint8_t)b]); + if(b == A || b == B || b == X || b == Y || b == BLACK || b == WHITE || b == L1 || b == R1) { // A, B, X, Y, BLACK, WHITE, L1, and R1 are analog buttons + if(buttonClicked[button]) { + buttonClicked[button] = false; + return true; + } + return false; + } + + bool click = (ButtonClickState & button); + ButtonClickState &= ~button; // clear "click" event + return click; +} + +int16_t XBOXOLD::getAnalogHat(AnalogHatEnum a) { + return hatValue[a]; +} + +/* Xbox Controller commands */ +void XBOXOLD::XboxCommand(uint8_t* data, uint16_t nbytes) { + //bmRequest = Host to device (0x00) | Class (0x20) | Interface (0x01) = 0x21, bRequest = Set Report (0x09), Report ID (0x00), Report Type (Output 0x02), interface (0x00), datalength, datalength, data) + pUsb->ctrlReq(bAddress, epInfo[XBOX_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0x00, 0x02, 0x00, nbytes, nbytes, data, NULL); +} + +void XBOXOLD::setRumbleOn(uint8_t lValue, uint8_t rValue) { + uint8_t writeBuf[6]; + + writeBuf[0] = 0x00; + writeBuf[1] = 0x06; + writeBuf[2] = 0x00; + writeBuf[3] = rValue; // small weight + writeBuf[4] = 0x00; + writeBuf[5] = lValue; // big weight + + XboxCommand(writeBuf, 6); +} diff --git a/libraries/USB_Host_Shield/XBOXOLD.h b/libraries/USB_Host_Shield/XBOXOLD.h new file mode 100755 index 0000000..9a36b5c --- /dev/null +++ b/libraries/USB_Host_Shield/XBOXOLD.h @@ -0,0 +1,185 @@ +/* Copyright (C) 2013 Kristian Lauszus, TKJ Electronics. All rights reserved. + + This software may be distributed and modified under the terms of the GNU + General Public License version 2 (GPL2) as published by the Free Software + Foundation and appearing in the file GPL2.TXT included in the packaging of + this file. Please note that GPL2 Section 2[b] requires that all works based + on this software must also be made publicly available under the terms of + the GPL2 ("Copyleft"). + + Contact information + ------------------- + + Kristian Lauszus, TKJ Electronics + Web : http://www.tkjelectronics.com + e-mail : kristianl@tkjelectronics.com + */ + +#ifndef _xboxold_h_ +#define _xboxold_h_ + +#include "Usb.h" +#include "hid.h" +#include "controllerEnums.h" + +/* Data Xbox taken from descriptors */ +#define EP_MAXPKTSIZE 32 // Max size for data via USB + +/* Names we give to the 3 Xbox pipes */ +#define XBOX_CONTROL_PIPE 0 +#define XBOX_INPUT_PIPE 1 +#define XBOX_OUTPUT_PIPE 2 + +// PID and VID of the different devices +#define XBOX_VID 0x045E // Microsoft Corporation +#define MADCATZ_VID 0x1BAD // For unofficial Mad Catz controllers +#define JOYTECH_VID 0x162E // For unofficial Joytech controllers + +#define XBOX_OLD_PID1 0x0202 // Original Microsoft Xbox controller (US) +#define XBOX_OLD_PID2 0x0285 // Original Microsoft Xbox controller (Japan) +#define XBOX_OLD_PID3 0x0287 // Microsoft Microsoft Xbox Controller S +#define XBOX_OLD_PID4 0x0289 // Smaller Microsoft Xbox controller (US) + +#define XBOX_MAX_ENDPOINTS 3 + +/** This class implements support for a the original Xbox controller via USB. */ +class XBOXOLD : public USBDeviceConfig { +public: + /** + * Constructor for the XBOXOLD class. + * @param pUsb Pointer to USB class instance. + */ + XBOXOLD(USB *pUsb); + + /** @name USBDeviceConfig implementation */ + /** + * Initialize the Xbox Controller. + * @param parent Hub number. + * @param port Port number on the hub. + * @param lowspeed Speed of the device. + * @return 0 on success. + */ + uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed); + /** + * Release the USB device. + * @return 0 on success. + */ + uint8_t Release(); + /** + * Poll the USB Input endpoins and run the state machines. + * @return 0 on success. + */ + uint8_t Poll(); + + /** + * Get the device address. + * @return The device address. + */ + virtual uint8_t GetAddress() { + return bAddress; + }; + + /** + * Used to check if the controller has been initialized. + * @return True if it's ready. + */ + virtual bool isReady() { + return bPollEnable; + }; + + /** + * Used by the USB core to check what this driver support. + * @param vid The device's VID. + * @param pid The device's PID. + * @return Returns true if the device's VID and PID matches this driver. + */ + virtual bool VIDPIDOK(uint16_t vid, uint16_t pid) { + return ((vid == XBOX_VID || vid == MADCATZ_VID || vid == JOYTECH_VID) && (pid == XBOX_OLD_PID1 || pid == XBOX_OLD_PID2 || pid == XBOX_OLD_PID3 || pid == XBOX_OLD_PID4)); + }; + /**@}*/ + + /** @name Xbox Controller functions */ + /** + * getButtonPress(ButtonEnum b) will return true as long as the button is held down. + * + * While getButtonClick(ButtonEnum b) will only return it once. + * + * So you instance if you need to increase a variable once you would use getButtonClick(ButtonEnum b), + * but if you need to drive a robot forward you would use getButtonPress(ButtonEnum b). + * @param b ::ButtonEnum to read. + * @return getButtonClick(ButtonEnum b) will return a bool, while getButtonPress(ButtonEnum b) will return a byte if reading ::L2 or ::R2. + */ + uint8_t getButtonPress(ButtonEnum b); + bool getButtonClick(ButtonEnum b); + /**@}*/ + + /** @name Xbox Controller functions */ + /** + * Return the analog value from the joysticks on the controller. + * @param a Either ::LeftHatX, ::LeftHatY, ::RightHatX or ::RightHatY. + * @return Returns a signed 16-bit integer. + */ + int16_t getAnalogHat(AnalogHatEnum a); + + /** Turn rumble off the controller. */ + void setRumbleOff() { + setRumbleOn(0, 0); + }; + /** + * Turn rumble on. + * @param lValue Left motor (big weight) inside the controller. + * @param rValue Right motor (small weight) inside the controller. + */ + void setRumbleOn(uint8_t lValue, uint8_t rValue); + + /** + * Used to call your own function when the controller is successfully initialized. + * @param funcOnInit Function to call. + */ + void attachOnInit(void (*funcOnInit)(void)) { + pFuncOnInit = funcOnInit; + }; + /**@}*/ + + /** True if a Xbox controller is connected. */ + bool XboxConnected; + +protected: + /** Pointer to USB class instance. */ + USB *pUsb; + /** Device address. */ + uint8_t bAddress; + /** Endpoint info structure. */ + EpInfo epInfo[XBOX_MAX_ENDPOINTS]; + +private: + /** + * Called when the controller is successfully initialized. + * Use attachOnInit(void (*funcOnInit)(void)) to call your own function. + * This is useful for instance if you want to set the LEDs in a specific way. + */ + void (*pFuncOnInit)(void); // Pointer to function called in onInit() + + bool bPollEnable; + + /* Variables to store the digital buttons */ + uint8_t ButtonState; + uint8_t OldButtonState; + uint8_t ButtonClickState; + + /* Variables to store the analog buttons */ + uint8_t buttonValues[8]; // A, B, X, Y, BLACK, WHITE, L1, and R1 + uint8_t oldButtonValues[8]; + bool buttonClicked[8]; + + int16_t hatValue[4]; // Joystick values + + uint8_t readBuf[EP_MAXPKTSIZE]; // General purpose buffer for input data + + void readReport(); // Read incoming data + void printReport(uint16_t length); // Print incoming date + + /* Private commands */ + void XboxCommand(uint8_t* data, uint16_t nbytes); +}; +#endif diff --git a/libraries/USB_Host_Shield/XBOXRECV.cpp b/libraries/USB_Host_Shield/XBOXRECV.cpp new file mode 100755 index 0000000..41f1ff5 --- /dev/null +++ b/libraries/USB_Host_Shield/XBOXRECV.cpp @@ -0,0 +1,583 @@ +/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved. + + This software may be distributed and modified under the terms of the GNU + General Public License version 2 (GPL2) as published by the Free Software + Foundation and appearing in the file GPL2.TXT included in the packaging of + this file. Please note that GPL2 Section 2[b] requires that all works based + on this software must also be made publicly available under the terms of + the GPL2 ("Copyleft"). + + Contact information + ------------------- + + Kristian Lauszus, TKJ Electronics + Web : http://www.tkjelectronics.com + e-mail : kristianl@tkjelectronics.com + + getBatteryLevel and checkStatus functions made by timstamp.co.uk found using BusHound from Perisoft.net + */ + +#include "XBOXRECV.h" +// To enable serial debugging see "settings.h" +//#define EXTRADEBUG // Uncomment to get even more debugging data +//#define PRINTREPORT // Uncomment to print the report send by the Xbox 360 Controller + +XBOXRECV::XBOXRECV(USB *p) : +pUsb(p), // pointer to USB class instance - mandatory +bAddress(0), // device address - mandatory +bPollEnable(false) { // don't start polling before dongle is connected + for(uint8_t i = 0; i < XBOX_MAX_ENDPOINTS; i++) { + epInfo[i].epAddr = 0; + epInfo[i].maxPktSize = (i) ? 0 : 8; + epInfo[i].epAttribs = 0; + epInfo[i].bmNakPower = (i) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER; + } + + if(pUsb) // register in USB subsystem + pUsb->RegisterDeviceClass(this); //set devConfig[] entry +} + +uint8_t XBOXRECV::ConfigureDevice(uint8_t parent, uint8_t port, bool lowspeed) { + const uint8_t constBufSize = sizeof (USB_DEVICE_DESCRIPTOR); + uint8_t buf[constBufSize]; + USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast(buf); + uint8_t rcode; + UsbDevice *p = NULL; + EpInfo *oldep_ptr = NULL; + uint16_t PID, VID; + + AddressPool &addrPool = pUsb->GetAddressPool(); // Get memory address of USB device address pool +#ifdef EXTRADEBUG + Notify(PSTR("\r\nXBOXRECV Init"), 0x80); +#endif + + if(bAddress) { // Check if address has already been assigned to an instance +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nAddress in use"), 0x80); +#endif + return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE; + } + + p = addrPool.GetUsbDevicePtr(0); // Get pointer to pseudo device with address 0 assigned + + if(!p) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nAddress not found"), 0x80); +#endif + return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; + } + + if(!p->epinfo) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nepinfo is null"), 0x80); +#endif + return USB_ERROR_EPINFO_IS_NULL; + } + + oldep_ptr = p->epinfo; // Save old pointer to EP_RECORD of address 0 + p->epinfo = epInfo; // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence + p->lowspeed = lowspeed; + + rcode = pUsb->getDevDescr(0, 0, constBufSize, (uint8_t*)buf); // Get device descriptor - addr, ep, nbytes, data + + p->epinfo = oldep_ptr; // Restore p->epinfo + + if(rcode) + goto FailGetDevDescr; + + VID = udd->idVendor; + PID = udd->idProduct; + + if((VID != XBOX_VID && VID != MADCATZ_VID && VID != JOYTECH_VID) || (PID != XBOX_WIRELESS_RECEIVER_PID && PID != XBOX_WIRELESS_RECEIVER_THIRD_PARTY_PID)) { // Check if it's a Xbox receiver using the Vendor ID and Product ID +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nYou'll need a wireless receiver for this libary to work"), 0x80); +#endif + goto FailUnknownDevice; + } + + bAddress = addrPool.AllocAddress(parent, false, port); // Allocate new address according to device class + + if(!bAddress) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nOut of address space"), 0x80); +#endif + return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL; + } + + epInfo[0].maxPktSize = udd->bMaxPacketSize0; // Extract Max Packet Size from device descriptor + + delay(20); // Wait a little before resetting device + + return USB_ERROR_CONFIG_REQUIRES_ADDITIONAL_RESET; + + /* Diagnostic messages */ +FailGetDevDescr: +#ifdef DEBUG_USB_HOST + NotifyFailGetDevDescr(rcode); +#endif + if(rcode != hrJERR) + rcode = USB_ERROR_FailGetDevDescr; + goto Fail; + +FailUnknownDevice: +#ifdef DEBUG_USB_HOST + NotifyFailUnknownDevice(VID, PID); +#endif + rcode = USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED; + +Fail: +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nXbox 360 Init Failed, error code: "), 0x80); + NotifyFail(rcode); +#endif + Release(); + return rcode; +}; + +uint8_t XBOXRECV::Init(uint8_t parent, uint8_t port, bool lowspeed) { + uint8_t rcode; + + AddressPool &addrPool = pUsb->GetAddressPool(); +#ifdef EXTRADEBUG + Notify(PSTR("\r\nBTD Init"), 0x80); +#endif + UsbDevice *p = addrPool.GetUsbDevicePtr(bAddress); // Get pointer to assigned address record + + if(!p) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nAddress not found"), 0x80); +#endif + return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; + } + + delay(300); // Assign new address to the device + + rcode = pUsb->setAddr(0, 0, bAddress); // Assign new address to the device + if(rcode) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nsetAddr: "), 0x80); + D_PrintHex (rcode, 0x80); +#endif + p->lowspeed = false; + goto Fail; + } +#ifdef EXTRADEBUG + Notify(PSTR("\r\nAddr: "), 0x80); + D_PrintHex (bAddress, 0x80); +#endif + + p->lowspeed = false; + + p = addrPool.GetUsbDevicePtr(bAddress); // Get pointer to assigned address record + if(!p) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nAddress not found"), 0x80); +#endif + return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; + } + + p->lowspeed = lowspeed; + + rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo); // Assign epInfo to epinfo pointer - only EP0 is known + if(rcode) + goto FailSetDevTblEntry; + + /* The application will work in reduced host mode, so we can save program and data + memory space. After verifying the VID we will use known values for the + configuration values for device, interface, endpoints and HID for the XBOX360 Wireless receiver */ + + /* Initialize data structures for endpoints of device */ + epInfo[ XBOX_INPUT_PIPE_1 ].epAddr = 0x01; // XBOX 360 report endpoint - poll interval 1ms + epInfo[ XBOX_INPUT_PIPE_1 ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT; + epInfo[ XBOX_INPUT_PIPE_1 ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints + epInfo[ XBOX_INPUT_PIPE_1 ].maxPktSize = EP_MAXPKTSIZE; + epInfo[ XBOX_INPUT_PIPE_1 ].bmSndToggle = 0; + epInfo[ XBOX_INPUT_PIPE_1 ].bmRcvToggle = 0; + epInfo[ XBOX_OUTPUT_PIPE_1 ].epAddr = 0x01; // XBOX 360 output endpoint - poll interval 8ms + epInfo[ XBOX_OUTPUT_PIPE_1 ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT; + epInfo[ XBOX_OUTPUT_PIPE_1 ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints + epInfo[ XBOX_OUTPUT_PIPE_1 ].maxPktSize = EP_MAXPKTSIZE; + epInfo[ XBOX_OUTPUT_PIPE_1 ].bmSndToggle = 0; + epInfo[ XBOX_OUTPUT_PIPE_1 ].bmRcvToggle = 0; + + epInfo[ XBOX_INPUT_PIPE_2 ].epAddr = 0x03; // XBOX 360 report endpoint - poll interval 1ms + epInfo[ XBOX_INPUT_PIPE_2 ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT; + epInfo[ XBOX_INPUT_PIPE_2 ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints + epInfo[ XBOX_INPUT_PIPE_2 ].maxPktSize = EP_MAXPKTSIZE; + epInfo[ XBOX_INPUT_PIPE_2 ].bmSndToggle = 0; + epInfo[ XBOX_INPUT_PIPE_2 ].bmRcvToggle = 0; + epInfo[ XBOX_OUTPUT_PIPE_2 ].epAddr = 0x03; // XBOX 360 output endpoint - poll interval 8ms + epInfo[ XBOX_OUTPUT_PIPE_2 ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT; + epInfo[ XBOX_OUTPUT_PIPE_2 ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints + epInfo[ XBOX_OUTPUT_PIPE_2 ].maxPktSize = EP_MAXPKTSIZE; + epInfo[ XBOX_OUTPUT_PIPE_2 ].bmSndToggle = 0; + epInfo[ XBOX_OUTPUT_PIPE_2 ].bmRcvToggle = 0; + + epInfo[ XBOX_INPUT_PIPE_3 ].epAddr = 0x05; // XBOX 360 report endpoint - poll interval 1ms + epInfo[ XBOX_INPUT_PIPE_3 ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT; + epInfo[ XBOX_INPUT_PIPE_3 ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints + epInfo[ XBOX_INPUT_PIPE_3 ].maxPktSize = EP_MAXPKTSIZE; + epInfo[ XBOX_INPUT_PIPE_3 ].bmSndToggle = 0; + epInfo[ XBOX_INPUT_PIPE_3 ].bmRcvToggle = 0; + epInfo[ XBOX_OUTPUT_PIPE_3 ].epAddr = 0x05; // XBOX 360 output endpoint - poll interval 8ms + epInfo[ XBOX_OUTPUT_PIPE_3 ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT; + epInfo[ XBOX_OUTPUT_PIPE_3 ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints + epInfo[ XBOX_OUTPUT_PIPE_3 ].maxPktSize = EP_MAXPKTSIZE; + epInfo[ XBOX_OUTPUT_PIPE_3 ].bmSndToggle = 0; + epInfo[ XBOX_OUTPUT_PIPE_3 ].bmRcvToggle = 0; + + epInfo[ XBOX_INPUT_PIPE_4 ].epAddr = 0x07; // XBOX 360 report endpoint - poll interval 1ms + epInfo[ XBOX_INPUT_PIPE_4 ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT; + epInfo[ XBOX_INPUT_PIPE_4 ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints + epInfo[ XBOX_INPUT_PIPE_4 ].maxPktSize = EP_MAXPKTSIZE; + epInfo[ XBOX_INPUT_PIPE_4 ].bmSndToggle = 0; + epInfo[ XBOX_INPUT_PIPE_4 ].bmRcvToggle = 0; + epInfo[ XBOX_OUTPUT_PIPE_4 ].epAddr = 0x07; // XBOX 360 output endpoint - poll interval 8ms + epInfo[ XBOX_OUTPUT_PIPE_4 ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT; + epInfo[ XBOX_OUTPUT_PIPE_4 ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints + epInfo[ XBOX_OUTPUT_PIPE_4 ].maxPktSize = EP_MAXPKTSIZE; + epInfo[ XBOX_OUTPUT_PIPE_4 ].bmSndToggle = 0; + epInfo[ XBOX_OUTPUT_PIPE_4 ].bmRcvToggle = 0; + + rcode = pUsb->setEpInfoEntry(bAddress, 9, epInfo); + if(rcode) + goto FailSetDevTblEntry; + + delay(200); //Give time for address change + + rcode = pUsb->setConf(bAddress, epInfo[ XBOX_CONTROL_PIPE ].epAddr, 1); + if(rcode) + goto FailSetConfDescr; + +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nXbox Wireless Receiver Connected\r\n"), 0x80); +#endif + XboxReceiverConnected = true; + bPollEnable = true; + checkStatusTimer = 0; // Reset timer + return 0; // Successful configuration + + /* Diagnostic messages */ +FailSetDevTblEntry: +#ifdef DEBUG_USB_HOST + NotifyFailSetDevTblEntry(); + goto Fail; +#endif + +FailSetConfDescr: +#ifdef DEBUG_USB_HOST + NotifyFailSetConfDescr(); +#endif + +Fail: +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nXbox 360 Init Failed, error code: "), 0x80); + NotifyFail(rcode); +#endif + Release(); + return rcode; +} + +/* Performs a cleanup after failed Init() attempt */ +uint8_t XBOXRECV::Release() { + XboxReceiverConnected = false; + for(uint8_t i = 0; i < 4; i++) + Xbox360Connected[i] = 0x00; + pUsb->GetAddressPool().FreeAddress(bAddress); + bAddress = 0; + bPollEnable = false; + return 0; +} + +uint8_t XBOXRECV::Poll() { + if(!bPollEnable) + return 0; + if(!checkStatusTimer || ((millis() - checkStatusTimer) > 3000)) { // Run checkStatus every 3 seconds + checkStatusTimer = millis(); + checkStatus(); + } + + uint8_t inputPipe; + uint16_t bufferSize; + for(uint8_t i = 0; i < 4; i++) { + if(i == 0) + inputPipe = XBOX_INPUT_PIPE_1; + else if(i == 1) + inputPipe = XBOX_INPUT_PIPE_2; + else if(i == 2) + inputPipe = XBOX_INPUT_PIPE_3; + else + inputPipe = XBOX_INPUT_PIPE_4; + + bufferSize = EP_MAXPKTSIZE; // This is the maximum number of bytes we want to receive + pUsb->inTransfer(bAddress, epInfo[ inputPipe ].epAddr, &bufferSize, readBuf); + if(bufferSize > 0) { // The number of received bytes +#ifdef EXTRADEBUG + Notify(PSTR("Bytes Received: "), 0x80); + D_PrintHex (bufferSize, 0x80); + Notify(PSTR("\r\n"), 0x80); +#endif + readReport(i); +#ifdef PRINTREPORT + printReport(i, bufferSize); // Uncomment "#define PRINTREPORT" to print the report send by the Xbox 360 Controller +#endif + } + } + return 0; +} + +void XBOXRECV::readReport(uint8_t controller) { + if(readBuf == NULL) + return; + // This report is send when a controller is connected and disconnected + if(readBuf[0] == 0x08 && readBuf[1] != Xbox360Connected[controller]) { + Xbox360Connected[controller] = readBuf[1]; +#ifdef DEBUG_USB_HOST + Notify(PSTR("Controller "), 0x80); + Notify(controller, 0x80); +#endif + if(Xbox360Connected[controller]) { +#ifdef DEBUG_USB_HOST + const char* str = 0; + switch(readBuf[1]) { + case 0x80: str = PSTR(" as controller\r\n"); + break; + case 0x40: str = PSTR(" as headset\r\n"); + break; + case 0xC0: str = PSTR(" as controller+headset\r\n"); + break; + } + Notify(PSTR(": connected"), 0x80); + Notify(str, 0x80); +#endif + onInit(controller); + } +#ifdef DEBUG_USB_HOST + else + Notify(PSTR(": disconnected\r\n"), 0x80); +#endif + return; + } + // Controller status report + if(readBuf[1] == 0x00 && readBuf[3] & 0x13 && readBuf[4] >= 0x22) { + controllerStatus[controller] = ((uint16_t)readBuf[3] << 8) | readBuf[4]; + return; + } + if(readBuf[1] != 0x01) // Check if it's the correct report - the receiver also sends different status reports + return; + + // A controller must be connected if it's sending data + if(!Xbox360Connected[controller]) + Xbox360Connected[controller] |= 0x80; + + ButtonState[controller] = (uint32_t)(readBuf[9] | ((uint16_t)readBuf[8] << 8) | ((uint32_t)readBuf[7] << 16) | ((uint32_t)readBuf[6] << 24)); + + hatValue[controller][LeftHatX] = (int16_t)(((uint16_t)readBuf[11] << 8) | readBuf[10]); + hatValue[controller][LeftHatY] = (int16_t)(((uint16_t)readBuf[13] << 8) | readBuf[12]); + hatValue[controller][RightHatX] = (int16_t)(((uint16_t)readBuf[15] << 8) | readBuf[14]); + hatValue[controller][RightHatY] = (int16_t)(((uint16_t)readBuf[17] << 8) | readBuf[16]); + + //Notify(PSTR("\r\nButtonState: "), 0x80); + //PrintHex(ButtonState[controller], 0x80); + + if(ButtonState[controller] != OldButtonState[controller]) { + buttonStateChanged[controller] = true; + ButtonClickState[controller] = (ButtonState[controller] >> 16) & ((~OldButtonState[controller]) >> 16); // Update click state variable, but don't include the two trigger buttons L2 and R2 + if(((uint8_t)OldButtonState[controller]) == 0 && ((uint8_t)ButtonState[controller]) != 0) // The L2 and R2 buttons are special as they are analog buttons + R2Clicked[controller] = true; + if((uint8_t)(OldButtonState[controller] >> 8) == 0 && (uint8_t)(ButtonState[controller] >> 8) != 0) + L2Clicked[controller] = true; + OldButtonState[controller] = ButtonState[controller]; + } +} + +void XBOXRECV::printReport(uint8_t controller, uint8_t nBytes) { //Uncomment "#define PRINTREPORT" to print the report send by the Xbox 360 Controller +#ifdef PRINTREPORT + if(readBuf == NULL) + return; + Notify(PSTR("Controller "), 0x80); + Notify(controller, 0x80); + Notify(PSTR(": "), 0x80); + for(uint8_t i = 0; i < nBytes; i++) { + D_PrintHex (readBuf[i], 0x80); + Notify(PSTR(" "), 0x80); + } + Notify(PSTR("\r\n"), 0x80); +#endif +} + +uint8_t XBOXRECV::getButtonPress(ButtonEnum b, uint8_t controller) { + if(b == L2) // These are analog buttons + return (uint8_t)(ButtonState[controller] >> 8); + else if(b == R2) + return (uint8_t)ButtonState[controller]; + return (bool)(ButtonState[controller] & ((uint32_t)pgm_read_word(&XBOX_BUTTONS[(uint8_t)b]) << 16)); +} + +bool XBOXRECV::getButtonClick(ButtonEnum b, uint8_t controller) { + if(b == L2) { + if(L2Clicked[controller]) { + L2Clicked[controller] = false; + return true; + } + return false; + } else if(b == R2) { + if(R2Clicked[controller]) { + R2Clicked[controller] = false; + return true; + } + return false; + } + uint16_t button = pgm_read_word(&XBOX_BUTTONS[(uint8_t)b]); + bool click = (ButtonClickState[controller] & button); + ButtonClickState[controller] &= ~button; // clear "click" event + return click; +} + +int16_t XBOXRECV::getAnalogHat(AnalogHatEnum a, uint8_t controller) { + return hatValue[controller][a]; +} + +bool XBOXRECV::buttonChanged(uint8_t controller) { + bool state = buttonStateChanged[controller]; + buttonStateChanged[controller] = false; + return state; +} + +/* +ControllerStatus Breakdown +ControllerStatus[controller] & 0x0001 // 0 +ControllerStatus[controller] & 0x0002 // normal batteries, no rechargeable battery pack +ControllerStatus[controller] & 0x0004 // controller starting up / settling +ControllerStatus[controller] & 0x0008 // headset adapter plugged in, but no headphones connected (mute?) +ControllerStatus[controller] & 0x0010 // 0 +ControllerStatus[controller] & 0x0020 // 1 +ControllerStatus[controller] & 0x0040 // battery level (high bit) +ControllerStatus[controller] & 0x0080 // battery level (low bit) +ControllerStatus[controller] & 0x0100 // 1 +ControllerStatus[controller] & 0x0200 // 1 +ControllerStatus[controller] & 0x0400 // headset adapter plugged in +ControllerStatus[controller] & 0x0800 // 0 +ControllerStatus[controller] & 0x1000 // 1 +ControllerStatus[controller] & 0x2000 // 0 +ControllerStatus[controller] & 0x4000 // 0 +ControllerStatus[controller] & 0x8000 // 0 + */ +uint8_t XBOXRECV::getBatteryLevel(uint8_t controller) { + return ((controllerStatus[controller] & 0x00C0) >> 6); +} + +void XBOXRECV::XboxCommand(uint8_t controller, uint8_t* data, uint16_t nbytes) { +#ifdef EXTRADEBUG + uint8_t rcode; +#endif + uint8_t outputPipe; + switch(controller) { + case 0: outputPipe = XBOX_OUTPUT_PIPE_1; + break; + case 1: outputPipe = XBOX_OUTPUT_PIPE_2; + break; + case 2: outputPipe = XBOX_OUTPUT_PIPE_3; + break; + case 3: outputPipe = XBOX_OUTPUT_PIPE_4; + break; + default: + return; + } +#ifdef EXTRADEBUG + rcode = +#endif + pUsb->outTransfer(bAddress, epInfo[ outputPipe ].epAddr, nbytes, data); +#ifdef EXTRADEBUG + if(rcode) + Notify(PSTR("Error sending Xbox message\r\n"), 0x80); +#endif +} + +void XBOXRECV::disconnect(uint8_t controller) { + writeBuf[0] = 0x00; + writeBuf[1] = 0x00; + writeBuf[2] = 0x08; + writeBuf[3] = 0xC0; + + XboxCommand(controller, writeBuf, 4); +} + +void XBOXRECV::setLedRaw(uint8_t value, uint8_t controller) { + writeBuf[0] = 0x00; + writeBuf[1] = 0x00; + writeBuf[2] = 0x08; + writeBuf[3] = value | 0x40; + + XboxCommand(controller, writeBuf, 4); +} + +void XBOXRECV::setLedOn(LEDEnum led, uint8_t controller) { + if(led == OFF) + setLedRaw(0, controller); + else if(led != ALL) // All LEDs can't be on a the same time + setLedRaw(pgm_read_byte(&XBOX_LEDS[(uint8_t)led]) + 4, controller); +} + +void XBOXRECV::setLedBlink(LEDEnum led, uint8_t controller) { + setLedRaw(pgm_read_byte(&XBOX_LEDS[(uint8_t)led]), controller); +} + +void XBOXRECV::setLedMode(LEDModeEnum ledMode, uint8_t controller) { // This function is used to do some speciel LED stuff the controller supports + setLedRaw((uint8_t)ledMode, controller); +} + +/* PC runs this at interval of approx 2 seconds +Thanks to BusHound from Perisoft.net for the Windows USB Analysis output +Found by timstamp.co.uk + */ +void XBOXRECV::checkStatus() { + if(!bPollEnable) + return; + // Get controller info + writeBuf[0] = 0x08; + writeBuf[1] = 0x00; + writeBuf[2] = 0x0f; + writeBuf[3] = 0xc0; + for(uint8_t i = 0; i < 4; i++) { + XboxCommand(i, writeBuf, 4); + } + // Get battery status + writeBuf[0] = 0x00; + writeBuf[1] = 0x00; + writeBuf[2] = 0x00; + writeBuf[3] = 0x40; + for(uint8_t i = 0; i < 4; i++) { + if(Xbox360Connected[i]) + XboxCommand(i, writeBuf, 4); + } +} + +void XBOXRECV::setRumbleOn(uint8_t lValue, uint8_t rValue, uint8_t controller) { + writeBuf[0] = 0x00; + writeBuf[1] = 0x01; + writeBuf[2] = 0x0f; + writeBuf[3] = 0xc0; + writeBuf[4] = 0x00; + writeBuf[5] = lValue; // big weight + writeBuf[6] = rValue; // small weight + + XboxCommand(controller, writeBuf, 7); +} + +void XBOXRECV::onInit(uint8_t controller) { + if(pFuncOnInit) + pFuncOnInit(); // Call the user function + else { + LEDEnum led; + if(controller == 0) + led = LED1; + else if(controller == 1) + led = LED2; + else if(controller == 2) + led = LED3; + else + led = LED4; + setLedOn(led, controller); + } +} diff --git a/libraries/USB_Host_Shield/XBOXRECV.h b/libraries/USB_Host_Shield/XBOXRECV.h new file mode 100755 index 0000000..4f92146 --- /dev/null +++ b/libraries/USB_Host_Shield/XBOXRECV.h @@ -0,0 +1,276 @@ +/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved. + + This software may be distributed and modified under the terms of the GNU + General Public License version 2 (GPL2) as published by the Free Software + Foundation and appearing in the file GPL2.TXT included in the packaging of + this file. Please note that GPL2 Section 2[b] requires that all works based + on this software must also be made publicly available under the terms of + the GPL2 ("Copyleft"). + + Contact information + ------------------- + + Kristian Lauszus, TKJ Electronics + Web : http://www.tkjelectronics.com + e-mail : kristianl@tkjelectronics.com + + getBatteryLevel and checkStatus functions made by timstamp.co.uk found using BusHound from Perisoft.net + */ + +#ifndef _xboxrecv_h_ +#define _xboxrecv_h_ + +#include "Usb.h" +#include "xboxEnums.h" + +/* Data Xbox 360 taken from descriptors */ +#define EP_MAXPKTSIZE 32 // max size for data via USB + +/* Names we give to the 9 Xbox360 pipes */ +#define XBOX_CONTROL_PIPE 0 +#define XBOX_INPUT_PIPE_1 1 +#define XBOX_OUTPUT_PIPE_1 2 +#define XBOX_INPUT_PIPE_2 3 +#define XBOX_OUTPUT_PIPE_2 4 +#define XBOX_INPUT_PIPE_3 5 +#define XBOX_OUTPUT_PIPE_3 6 +#define XBOX_INPUT_PIPE_4 7 +#define XBOX_OUTPUT_PIPE_4 8 + +// PID and VID of the different devices +#define XBOX_VID 0x045E // Microsoft Corporation +#define MADCATZ_VID 0x1BAD // For unofficial Mad Catz receivers +#define JOYTECH_VID 0x162E // For unofficial Joytech controllers + +#define XBOX_WIRELESS_RECEIVER_PID 0x0719 // Microsoft Wireless Gaming Receiver +#define XBOX_WIRELESS_RECEIVER_THIRD_PARTY_PID 0x0291 // Third party Wireless Gaming Receiver + +#define XBOX_MAX_ENDPOINTS 9 + +/** + * This class implements support for a Xbox Wireless receiver. + * + * Up to four controllers can connect to one receiver, if more is needed one can use a second receiver via the USBHub class. + */ +class XBOXRECV : public USBDeviceConfig { +public: + /** + * Constructor for the XBOXRECV class. + * @param pUsb Pointer to USB class instance. + */ + XBOXRECV(USB *pUsb); + + /** @name USBDeviceConfig implementation */ + /** + * Address assignment and basic initilization is done here. + * @param parent Hub number. + * @param port Port number on the hub. + * @param lowspeed Speed of the device. + * @return 0 on success. + */ + uint8_t ConfigureDevice(uint8_t parent, uint8_t port, bool lowspeed); + /** + * Initialize the Xbox wireless receiver. + * @param parent Hub number. + * @param port Port number on the hub. + * @param lowspeed Speed of the device. + * @return 0 on success. + */ + uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed); + /** + * Release the USB device. + * @return 0 on success. + */ + uint8_t Release(); + /** + * Poll the USB Input endpoins and run the state machines. + * @return 0 on success. + */ + uint8_t Poll(); + + /** + * Get the device address. + * @return The device address. + */ + virtual uint8_t GetAddress() { + return bAddress; + }; + + /** + * Used to check if the controller has been initialized. + * @return True if it's ready. + */ + virtual bool isReady() { + return bPollEnable; + }; + + /** + * Used by the USB core to check what this driver support. + * @param vid The device's VID. + * @param pid The device's PID. + * @return Returns true if the device's VID and PID matches this driver. + */ + virtual bool VIDPIDOK(uint16_t vid, uint16_t pid) { + return ((vid == XBOX_VID || vid == MADCATZ_VID || vid == JOYTECH_VID) && (pid == XBOX_WIRELESS_RECEIVER_PID || pid == XBOX_WIRELESS_RECEIVER_THIRD_PARTY_PID)); + }; + /**@}*/ + + /** @name Xbox Controller functions */ + /** + * getButtonPress(uint8_t controller, ButtonEnum b) will return true as long as the button is held down. + * + * While getButtonClick(uint8_t controller, ButtonEnum b) will only return it once. + * + * So you instance if you need to increase a variable once you would use getButtonClick(uint8_t controller, ButtonEnum b), + * but if you need to drive a robot forward you would use getButtonPress(uint8_t controller, ButtonEnum b). + * @param b ::ButtonEnum to read. + * @param controller The controller to read from. Default to 0. + * @return getButtonClick(uint8_t controller, ButtonEnum b) will return a bool, while getButtonPress(uint8_t controller, ButtonEnum b) will return a byte if reading ::L2 or ::R2. + */ + uint8_t getButtonPress(ButtonEnum b, uint8_t controller = 0); + bool getButtonClick(ButtonEnum b, uint8_t controller = 0); + /**@}*/ + + /** @name Xbox Controller functions */ + /** + * Return the analog value from the joysticks on the controller. + * @param a Either ::LeftHatX, ::LeftHatY, ::RightHatX or ::RightHatY. + * @param controller The controller to read from. Default to 0. + * @return Returns a signed 16-bit integer. + */ + int16_t getAnalogHat(AnalogHatEnum a, uint8_t controller = 0); + + /** + * Used to disconnect any of the controllers. + * @param controller The controller to disconnect. Default to 0. + */ + void disconnect(uint8_t controller = 0); + + /** + * Turn rumble off and all the LEDs on the specific controller. + * @param controller The controller to write to. Default to 0. + */ + void setAllOff(uint8_t controller = 0) { + setRumbleOn(0, 0, controller); + setLedOff(controller); + }; + + /** + * Turn rumble off the specific controller. + * @param controller The controller to write to. Default to 0. + */ + void setRumbleOff(uint8_t controller = 0) { + setRumbleOn(0, 0, controller); + }; + /** + * Turn rumble on. + * @param lValue Left motor (big weight) inside the controller. + * @param rValue Right motor (small weight) inside the controller. + * @param controller The controller to write to. Default to 0. + */ + void setRumbleOn(uint8_t lValue, uint8_t rValue, uint8_t controller = 0); + /** + * Set LED value. Without using the ::LEDEnum or ::LEDModeEnum. + * @param value See: + * setLedOff(uint8_t controller), setLedOn(uint8_t controller, LED l), + * setLedBlink(uint8_t controller, LED l), and setLedMode(uint8_t controller, LEDMode lm). + * @param controller The controller to write to. Default to 0. + */ + void setLedRaw(uint8_t value, uint8_t controller = 0); + + /** + * Turn all LEDs off the specific controller. + * @param controller The controller to write to. Default to 0. + */ + void setLedOff(uint8_t controller = 0) { + setLedRaw(0, controller); + }; + /** + * Turn on a LED by using ::LEDEnum. + * @param l ::OFF, ::LED1, ::LED2, ::LED3 and ::LED4 is supported by the Xbox controller. + * @param controller The controller to write to. Default to 0. + */ + void setLedOn(LEDEnum l, uint8_t controller = 0); + /** + * Turn on a LED by using ::LEDEnum. + * @param l ::ALL, ::LED1, ::LED2, ::LED3 and ::LED4 is supported by the Xbox controller. + * @param controller The controller to write to. Default to 0. + */ + void setLedBlink(LEDEnum l, uint8_t controller = 0); + /** + * Used to set special LED modes supported by the Xbox controller. + * @param lm See ::LEDModeEnum. + * @param controller The controller to write to. Default to 0. + */ + void setLedMode(LEDModeEnum lm, uint8_t controller = 0); + /** + * Used to get the battery level from the controller. + * @param controller The controller to read from. Default to 0. + * @return Returns the battery level as an integer in the range of 0-3. + */ + uint8_t getBatteryLevel(uint8_t controller = 0); + /** + * Used to check if a button has changed. + * @param controller The controller to read from. Default to 0. + * @return True if a button has changed. + */ + bool buttonChanged(uint8_t controller = 0); + + /** + * Used to call your own function when the controller is successfully initialized. + * @param funcOnInit Function to call. + */ + void attachOnInit(void (*funcOnInit)(void)) { + pFuncOnInit = funcOnInit; + }; + /**@}*/ + + /** True if a wireless receiver is connected. */ + bool XboxReceiverConnected; + /** Variable used to indicate if the XBOX 360 controller is successfully connected. */ + uint8_t Xbox360Connected[4]; + +protected: + /** Pointer to USB class instance. */ + USB *pUsb; + /** Device address. */ + uint8_t bAddress; + /** Endpoint info structure. */ + EpInfo epInfo[XBOX_MAX_ENDPOINTS]; + +private: + /** + * Called when the controller is successfully initialized. + * Use attachOnInit(void (*funcOnInit)(void)) to call your own function. + * This is useful for instance if you want to set the LEDs in a specific way. + * @param controller The initialized controller. + */ + void onInit(uint8_t controller); + void (*pFuncOnInit)(void); // Pointer to function called in onInit() + + bool bPollEnable; + + /* Variables to store the buttons */ + uint32_t ButtonState[4]; + uint32_t OldButtonState[4]; + uint16_t ButtonClickState[4]; + int16_t hatValue[4][4]; + uint16_t controllerStatus[4]; + bool buttonStateChanged[4]; // True if a button has changed + + bool L2Clicked[4]; // These buttons are analog, so we use we use these bools to check if they where clicked or not + bool R2Clicked[4]; + + uint32_t checkStatusTimer; // Timing for checkStatus() signals + + uint8_t readBuf[EP_MAXPKTSIZE]; // General purpose buffer for input data + uint8_t writeBuf[7]; // General purpose buffer for output data + + void readReport(uint8_t controller); // read incoming data + void printReport(uint8_t controller, uint8_t nBytes); // print incoming date - Uncomment for debugging + + /* Private commands */ + void XboxCommand(uint8_t controller, uint8_t* data, uint16_t nbytes); + void checkStatus(); +}; +#endif diff --git a/libraries/USB_Host_Shield/XBOXUSB.cpp b/libraries/USB_Host_Shield/XBOXUSB.cpp new file mode 100755 index 0000000..ddece21 --- /dev/null +++ b/libraries/USB_Host_Shield/XBOXUSB.cpp @@ -0,0 +1,361 @@ +/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved. + + This software may be distributed and modified under the terms of the GNU + General Public License version 2 (GPL2) as published by the Free Software + Foundation and appearing in the file GPL2.TXT included in the packaging of + this file. Please note that GPL2 Section 2[b] requires that all works based + on this software must also be made publicly available under the terms of + the GPL2 ("Copyleft"). + + Contact information + ------------------- + + Kristian Lauszus, TKJ Electronics + Web : http://www.tkjelectronics.com + e-mail : kristianl@tkjelectronics.com + */ + +#include "XBOXUSB.h" +// To enable serial debugging see "settings.h" +//#define EXTRADEBUG // Uncomment to get even more debugging data +//#define PRINTREPORT // Uncomment to print the report send by the Xbox 360 Controller + +XBOXUSB::XBOXUSB(USB *p) : +pUsb(p), // pointer to USB class instance - mandatory +bAddress(0), // device address - mandatory +bPollEnable(false) { // don't start polling before dongle is connected + for(uint8_t i = 0; i < XBOX_MAX_ENDPOINTS; i++) { + epInfo[i].epAddr = 0; + epInfo[i].maxPktSize = (i) ? 0 : 8; + epInfo[i].epAttribs = 0; + epInfo[i].bmNakPower = (i) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER; + } + + if(pUsb) // register in USB subsystem + pUsb->RegisterDeviceClass(this); //set devConfig[] entry +} + +uint8_t XBOXUSB::Init(uint8_t parent, uint8_t port, bool lowspeed) { + uint8_t buf[sizeof (USB_DEVICE_DESCRIPTOR)]; + USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast(buf); + uint8_t rcode; + UsbDevice *p = NULL; + EpInfo *oldep_ptr = NULL; + uint16_t PID; + uint16_t VID; + + // get memory address of USB device address pool + AddressPool &addrPool = pUsb->GetAddressPool(); +#ifdef EXTRADEBUG + Notify(PSTR("\r\nXBOXUSB Init"), 0x80); +#endif + // check if address has already been assigned to an instance + if(bAddress) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nAddress in use"), 0x80); +#endif + return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE; + } + + // Get pointer to pseudo device with address 0 assigned + p = addrPool.GetUsbDevicePtr(0); + + if(!p) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nAddress not found"), 0x80); +#endif + return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; + } + + if(!p->epinfo) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nepinfo is null"), 0x80); +#endif + return USB_ERROR_EPINFO_IS_NULL; + } + + // Save old pointer to EP_RECORD of address 0 + oldep_ptr = p->epinfo; + + // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence + p->epinfo = epInfo; + + p->lowspeed = lowspeed; + + // Get device descriptor + rcode = pUsb->getDevDescr(0, 0, sizeof (USB_DEVICE_DESCRIPTOR), (uint8_t*)buf); // Get device descriptor - addr, ep, nbytes, data + // Restore p->epinfo + p->epinfo = oldep_ptr; + + if(rcode) + goto FailGetDevDescr; + + VID = udd->idVendor; + PID = udd->idProduct; + + if(VID != XBOX_VID && VID != MADCATZ_VID && VID != JOYTECH_VID && VID != GAMESTOP_VID) // Check VID + goto FailUnknownDevice; + if(PID == XBOX_WIRELESS_PID) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nYou have plugged in a wireless Xbox 360 controller - it doesn't support USB communication"), 0x80); +#endif + goto FailUnknownDevice; + } else if(PID == XBOX_WIRELESS_RECEIVER_PID || PID == XBOX_WIRELESS_RECEIVER_THIRD_PARTY_PID) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nThis library only supports Xbox 360 controllers via USB"), 0x80); +#endif + goto FailUnknownDevice; + } else if(PID != XBOX_WIRED_PID && PID != MADCATZ_WIRED_PID && PID != GAMESTOP_WIRED_PID && PID != AFTERGLOW_WIRED_PID && PID != JOYTECH_WIRED_PID) // Check PID + goto FailUnknownDevice; + + // Allocate new address according to device class + bAddress = addrPool.AllocAddress(parent, false, port); + + if(!bAddress) + return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL; + + // Extract Max Packet Size from device descriptor + epInfo[0].maxPktSize = udd->bMaxPacketSize0; + + // Assign new address to the device + rcode = pUsb->setAddr(0, 0, bAddress); + if(rcode) { + p->lowspeed = false; + addrPool.FreeAddress(bAddress); + bAddress = 0; +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nsetAddr: "), 0x80); + D_PrintHex (rcode, 0x80); +#endif + return rcode; + } +#ifdef EXTRADEBUG + Notify(PSTR("\r\nAddr: "), 0x80); + D_PrintHex (bAddress, 0x80); +#endif + //delay(300); // Spec says you should wait at least 200ms + + p->lowspeed = false; + + //get pointer to assigned address record + p = addrPool.GetUsbDevicePtr(bAddress); + if(!p) + return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; + + p->lowspeed = lowspeed; + + // Assign epInfo to epinfo pointer - only EP0 is known + rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo); + if(rcode) + goto FailSetDevTblEntry; + + /* The application will work in reduced host mode, so we can save program and data + memory space. After verifying the VID we will use known values for the + configuration values for device, interface, endpoints and HID for the XBOX360 Controllers */ + + /* Initialize data structures for endpoints of device */ + epInfo[ XBOX_INPUT_PIPE ].epAddr = 0x01; // XBOX 360 report endpoint + epInfo[ XBOX_INPUT_PIPE ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT; + epInfo[ XBOX_INPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints + epInfo[ XBOX_INPUT_PIPE ].maxPktSize = EP_MAXPKTSIZE; + epInfo[ XBOX_INPUT_PIPE ].bmSndToggle = 0; + epInfo[ XBOX_INPUT_PIPE ].bmRcvToggle = 0; + epInfo[ XBOX_OUTPUT_PIPE ].epAddr = 0x02; // XBOX 360 output endpoint + epInfo[ XBOX_OUTPUT_PIPE ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT; + epInfo[ XBOX_OUTPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints + epInfo[ XBOX_OUTPUT_PIPE ].maxPktSize = EP_MAXPKTSIZE; + epInfo[ XBOX_OUTPUT_PIPE ].bmSndToggle = 0; + epInfo[ XBOX_OUTPUT_PIPE ].bmRcvToggle = 0; + + rcode = pUsb->setEpInfoEntry(bAddress, 3, epInfo); + if(rcode) + goto FailSetDevTblEntry; + + delay(200); // Give time for address change + + rcode = pUsb->setConf(bAddress, epInfo[ XBOX_CONTROL_PIPE ].epAddr, 1); + if(rcode) + goto FailSetConfDescr; + +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nXbox 360 Controller Connected\r\n"), 0x80); +#endif + onInit(); + Xbox360Connected = true; + bPollEnable = true; + return 0; // Successful configuration + + /* Diagnostic messages */ +FailGetDevDescr: +#ifdef DEBUG_USB_HOST + NotifyFailGetDevDescr(); + goto Fail; +#endif + +FailSetDevTblEntry: +#ifdef DEBUG_USB_HOST + NotifyFailSetDevTblEntry(); + goto Fail; +#endif + +FailSetConfDescr: +#ifdef DEBUG_USB_HOST + NotifyFailSetConfDescr(); +#endif + goto Fail; + +FailUnknownDevice: +#ifdef DEBUG_USB_HOST + NotifyFailUnknownDevice(VID, PID); +#endif + rcode = USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED; + +Fail: +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nXbox 360 Init Failed, error code: "), 0x80); + NotifyFail(rcode); +#endif + Release(); + return rcode; +} + +/* Performs a cleanup after failed Init() attempt */ +uint8_t XBOXUSB::Release() { + Xbox360Connected = false; + pUsb->GetAddressPool().FreeAddress(bAddress); + bAddress = 0; + bPollEnable = false; + return 0; +} + +uint8_t XBOXUSB::Poll() { + if(!bPollEnable) + return 0; + uint16_t BUFFER_SIZE = EP_MAXPKTSIZE; + pUsb->inTransfer(bAddress, epInfo[ XBOX_INPUT_PIPE ].epAddr, &BUFFER_SIZE, readBuf); // input on endpoint 1 + readReport(); +#ifdef PRINTREPORT + printReport(); // Uncomment "#define PRINTREPORT" to print the report send by the Xbox 360 Controller +#endif + return 0; +} + +void XBOXUSB::readReport() { + if(readBuf == NULL) + return; + if(readBuf[0] != 0x00 || readBuf[1] != 0x14) { // Check if it's the correct report - the controller also sends different status reports + return; + } + + ButtonState = (uint32_t)(readBuf[5] | ((uint16_t)readBuf[4] << 8) | ((uint32_t)readBuf[3] << 16) | ((uint32_t)readBuf[2] << 24)); + + hatValue[LeftHatX] = (int16_t)(((uint16_t)readBuf[7] << 8) | readBuf[6]); + hatValue[LeftHatY] = (int16_t)(((uint16_t)readBuf[9] << 8) | readBuf[8]); + hatValue[RightHatX] = (int16_t)(((uint16_t)readBuf[11] << 8) | readBuf[10]); + hatValue[RightHatY] = (int16_t)(((uint16_t)readBuf[13] << 8) | readBuf[12]); + + //Notify(PSTR("\r\nButtonState"), 0x80); + //PrintHex(ButtonState, 0x80); + + if(ButtonState != OldButtonState) { + ButtonClickState = (ButtonState >> 16) & ((~OldButtonState) >> 16); // Update click state variable, but don't include the two trigger buttons L2 and R2 + if(((uint8_t)OldButtonState) == 0 && ((uint8_t)ButtonState) != 0) // The L2 and R2 buttons are special as they are analog buttons + R2Clicked = true; + if((uint8_t)(OldButtonState >> 8) == 0 && (uint8_t)(ButtonState >> 8) != 0) + L2Clicked = true; + OldButtonState = ButtonState; + } +} + +void XBOXUSB::printReport() { //Uncomment "#define PRINTREPORT" to print the report send by the Xbox 360 Controller +#ifdef PRINTREPORT + if(readBuf == NULL) + return; + for(uint8_t i = 0; i < XBOX_REPORT_BUFFER_SIZE; i++) { + D_PrintHex (readBuf[i], 0x80); + Notify(PSTR(" "), 0x80); + } + Notify(PSTR("\r\n"), 0x80); +#endif +} + +uint8_t XBOXUSB::getButtonPress(ButtonEnum b) { + if(b == L2) // These are analog buttons + return (uint8_t)(ButtonState >> 8); + else if(b == R2) + return (uint8_t)ButtonState; + return (bool)(ButtonState & ((uint32_t)pgm_read_word(&XBOX_BUTTONS[(uint8_t)b]) << 16)); +} + +bool XBOXUSB::getButtonClick(ButtonEnum b) { + if(b == L2) { + if(L2Clicked) { + L2Clicked = false; + return true; + } + return false; + } else if(b == R2) { + if(R2Clicked) { + R2Clicked = false; + return true; + } + return false; + } + uint16_t button = pgm_read_word(&XBOX_BUTTONS[(uint8_t)b]); + bool click = (ButtonClickState & button); + ButtonClickState &= ~button; // clear "click" event + return click; +} + +int16_t XBOXUSB::getAnalogHat(AnalogHatEnum a) { + return hatValue[a]; +} + +/* Xbox Controller commands */ +void XBOXUSB::XboxCommand(uint8_t* data, uint16_t nbytes) { + //bmRequest = Host to device (0x00) | Class (0x20) | Interface (0x01) = 0x21, bRequest = Set Report (0x09), Report ID (0x00), Report Type (Output 0x02), interface (0x00), datalength, datalength, data) + pUsb->ctrlReq(bAddress, epInfo[XBOX_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0x00, 0x02, 0x00, nbytes, nbytes, data, NULL); +} + +void XBOXUSB::setLedRaw(uint8_t value) { + writeBuf[0] = 0x01; + writeBuf[1] = 0x03; + writeBuf[2] = value; + + XboxCommand(writeBuf, 3); +} + +void XBOXUSB::setLedOn(LEDEnum led) { + if(led == OFF) + setLedRaw(0); + else if(led != ALL) // All LEDs can't be on a the same time + setLedRaw(pgm_read_byte(&XBOX_LEDS[(uint8_t)led]) + 4); +} + +void XBOXUSB::setLedBlink(LEDEnum led) { + setLedRaw(pgm_read_byte(&XBOX_LEDS[(uint8_t)led])); +} + +void XBOXUSB::setLedMode(LEDModeEnum ledMode) { // This function is used to do some special LED stuff the controller supports + setLedRaw((uint8_t)ledMode); +} + +void XBOXUSB::setRumbleOn(uint8_t lValue, uint8_t rValue) { + writeBuf[0] = 0x00; + writeBuf[1] = 0x08; + writeBuf[2] = 0x00; + writeBuf[3] = lValue; // big weight + writeBuf[4] = rValue; // small weight + writeBuf[5] = 0x00; + writeBuf[6] = 0x00; + writeBuf[7] = 0x00; + + XboxCommand(writeBuf, 8); +} + +void XBOXUSB::onInit() { + if(pFuncOnInit) + pFuncOnInit(); // Call the user function + else + setLedOn(LED1); +} diff --git a/libraries/USB_Host_Shield/XBOXUSB.h b/libraries/USB_Host_Shield/XBOXUSB.h new file mode 100755 index 0000000..1ab3785 --- /dev/null +++ b/libraries/USB_Host_Shield/XBOXUSB.h @@ -0,0 +1,225 @@ +/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved. + + This software may be distributed and modified under the terms of the GNU + General Public License version 2 (GPL2) as published by the Free Software + Foundation and appearing in the file GPL2.TXT included in the packaging of + this file. Please note that GPL2 Section 2[b] requires that all works based + on this software must also be made publicly available under the terms of + the GPL2 ("Copyleft"). + + Contact information + ------------------- + + Kristian Lauszus, TKJ Electronics + Web : http://www.tkjelectronics.com + e-mail : kristianl@tkjelectronics.com + */ + +#ifndef _xboxusb_h_ +#define _xboxusb_h_ + +#include "Usb.h" +#include "hid.h" +#include "xboxEnums.h" + +/* Data Xbox 360 taken from descriptors */ +#define EP_MAXPKTSIZE 32 // max size for data via USB + +/* Names we give to the 3 Xbox360 pipes */ +#define XBOX_CONTROL_PIPE 0 +#define XBOX_INPUT_PIPE 1 +#define XBOX_OUTPUT_PIPE 2 + +// PID and VID of the different devices +#define XBOX_VID 0x045E // Microsoft Corporation +#define MADCATZ_VID 0x1BAD // For unofficial Mad Catz controllers +#define JOYTECH_VID 0x162E // For unofficial Joytech controllers +#define GAMESTOP_VID 0x0E6F // Gamestop controller + +#define XBOX_WIRED_PID 0x028E // Microsoft 360 Wired controller +#define XBOX_WIRELESS_PID 0x028F // Wireless controller only support charging +#define XBOX_WIRELESS_RECEIVER_PID 0x0719 // Microsoft Wireless Gaming Receiver +#define XBOX_WIRELESS_RECEIVER_THIRD_PARTY_PID 0x0291 // Third party Wireless Gaming Receiver +#define MADCATZ_WIRED_PID 0xF016 // Mad Catz wired controller +#define JOYTECH_WIRED_PID 0xBEEF // For Joytech wired controller +#define GAMESTOP_WIRED_PID 0x0401 // Gamestop wired controller +#define AFTERGLOW_WIRED_PID 0x0213 // Afterglow wired controller - it uses the same VID as a Gamestop controller + +#define XBOX_REPORT_BUFFER_SIZE 14 // Size of the input report buffer + +#define XBOX_MAX_ENDPOINTS 3 + +/** This class implements support for a Xbox wired controller via USB. */ +class XBOXUSB : public USBDeviceConfig { +public: + /** + * Constructor for the XBOXUSB class. + * @param pUsb Pointer to USB class instance. + */ + XBOXUSB(USB *pUsb); + + /** @name USBDeviceConfig implementation */ + /** + * Initialize the Xbox Controller. + * @param parent Hub number. + * @param port Port number on the hub. + * @param lowspeed Speed of the device. + * @return 0 on success. + */ + uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed); + /** + * Release the USB device. + * @return 0 on success. + */ + uint8_t Release(); + /** + * Poll the USB Input endpoins and run the state machines. + * @return 0 on success. + */ + uint8_t Poll(); + + /** + * Get the device address. + * @return The device address. + */ + virtual uint8_t GetAddress() { + return bAddress; + }; + + /** + * Used to check if the controller has been initialized. + * @return True if it's ready. + */ + virtual bool isReady() { + return bPollEnable; + }; + + /** + * Used by the USB core to check what this driver support. + * @param vid The device's VID. + * @param pid The device's PID. + * @return Returns true if the device's VID and PID matches this driver. + */ + virtual bool VIDPIDOK(uint16_t vid, uint16_t pid) { + return ((vid == XBOX_VID || vid == MADCATZ_VID || vid == JOYTECH_VID || vid == GAMESTOP_VID) && (pid == XBOX_WIRED_PID || pid == MADCATZ_WIRED_PID || pid == GAMESTOP_WIRED_PID || pid == AFTERGLOW_WIRED_PID || pid == JOYTECH_WIRED_PID)); + }; + /**@}*/ + + /** @name Xbox Controller functions */ + /** + * getButtonPress(ButtonEnum b) will return true as long as the button is held down. + * + * While getButtonClick(ButtonEnum b) will only return it once. + * + * So you instance if you need to increase a variable once you would use getButtonClick(ButtonEnum b), + * but if you need to drive a robot forward you would use getButtonPress(ButtonEnum b). + * @param b ::ButtonEnum to read. + * @return getButtonClick(ButtonEnum b) will return a bool, while getButtonPress(ButtonEnum b) will return a byte if reading ::L2 or ::R2. + */ + uint8_t getButtonPress(ButtonEnum b); + bool getButtonClick(ButtonEnum b); + /**@}*/ + + /** @name Xbox Controller functions */ + /** + * Return the analog value from the joysticks on the controller. + * @param a Either ::LeftHatX, ::LeftHatY, ::RightHatX or ::RightHatY. + * @return Returns a signed 16-bit integer. + */ + int16_t getAnalogHat(AnalogHatEnum a); + + /** Turn rumble off and all the LEDs on the controller. */ + void setAllOff() { + setRumbleOn(0, 0); + setLedRaw(0); + }; + + /** Turn rumble off the controller. */ + void setRumbleOff() { + setRumbleOn(0, 0); + }; + /** + * Turn rumble on. + * @param lValue Left motor (big weight) inside the controller. + * @param rValue Right motor (small weight) inside the controller. + */ + void setRumbleOn(uint8_t lValue, uint8_t rValue); + /** + * Set LED value. Without using the ::LEDEnum or ::LEDModeEnum. + * @param value See: + * setLedOff(), setLedOn(LEDEnum l), + * setLedBlink(LEDEnum l), and setLedMode(LEDModeEnum lm). + */ + void setLedRaw(uint8_t value); + + /** Turn all LEDs off the controller. */ + void setLedOff() { + setLedRaw(0); + }; + /** + * Turn on a LED by using ::LEDEnum. + * @param l ::OFF, ::LED1, ::LED2, ::LED3 and ::LED4 is supported by the Xbox controller. + */ + void setLedOn(LEDEnum l); + /** + * Turn on a LED by using ::LEDEnum. + * @param l ::ALL, ::LED1, ::LED2, ::LED3 and ::LED4 is supported by the Xbox controller. + */ + void setLedBlink(LEDEnum l); + /** + * Used to set special LED modes supported by the Xbox controller. + * @param lm See ::LEDModeEnum. + */ + void setLedMode(LEDModeEnum lm); + + /** + * Used to call your own function when the controller is successfully initialized. + * @param funcOnInit Function to call. + */ + void attachOnInit(void (*funcOnInit)(void)) { + pFuncOnInit = funcOnInit; + }; + /**@}*/ + + /** True if a Xbox 360 controller is connected. */ + bool Xbox360Connected; + +protected: + /** Pointer to USB class instance. */ + USB *pUsb; + /** Device address. */ + uint8_t bAddress; + /** Endpoint info structure. */ + EpInfo epInfo[XBOX_MAX_ENDPOINTS]; + +private: + /** + * Called when the controller is successfully initialized. + * Use attachOnInit(void (*funcOnInit)(void)) to call your own function. + * This is useful for instance if you want to set the LEDs in a specific way. + */ + void onInit(); + void (*pFuncOnInit)(void); // Pointer to function called in onInit() + + bool bPollEnable; + + /* Variables to store the buttons */ + uint32_t ButtonState; + uint32_t OldButtonState; + uint16_t ButtonClickState; + int16_t hatValue[4]; + uint16_t controllerStatus; + + bool L2Clicked; // These buttons are analog, so we use we use these bools to check if they where clicked or not + bool R2Clicked; + + uint8_t readBuf[EP_MAXPKTSIZE]; // General purpose buffer for input data + uint8_t writeBuf[8]; // General purpose buffer for output data + + void readReport(); // read incoming data + void printReport(); // print incoming date - Uncomment for debugging + + /* Private commands */ + void XboxCommand(uint8_t* data, uint16_t nbytes); +}; +#endif diff --git a/libraries/USB_Host_Shield/address.h b/libraries/USB_Host_Shield/address.h new file mode 100755 index 0000000..c3e1b31 --- /dev/null +++ b/libraries/USB_Host_Shield/address.h @@ -0,0 +1,282 @@ +/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved. + +This software may be distributed and modified under the terms of the GNU +General Public License version 2 (GPL2) as published by the Free Software +Foundation and appearing in the file GPL2.TXT included in the packaging of +this file. Please note that GPL2 Section 2[b] requires that all works based +on this software must also be made publicly available under the terms of +the GPL2 ("Copyleft"). + +Contact information +------------------- + +Circuits At Home, LTD +Web : http://www.circuitsathome.com +e-mail : support@circuitsathome.com + */ + +#if !defined(_usb_h_) || defined(__ADDRESS_H__) +#error "Never include address.h directly; include Usb.h instead" +#else +#define __ADDRESS_H__ + + + +/* NAK powers. To save space in endpoint data structure, amount of retries before giving up and returning 0x4 is stored in */ +/* bmNakPower as a power of 2. The actual nak_limit is then calculated as nak_limit = ( 2^bmNakPower - 1) */ +#define USB_NAK_MAX_POWER 15 //NAK binary order maximum value +#define USB_NAK_DEFAULT 14 //default 32K-1 NAKs before giving up +#define USB_NAK_NOWAIT 1 //Single NAK stops transfer +#define USB_NAK_NONAK 0 //Do not count NAKs, stop retrying after USB Timeout + +struct EpInfo { + uint8_t epAddr; // Endpoint address + uint8_t maxPktSize; // Maximum packet size + + union { + uint8_t epAttribs; + + struct { + uint8_t bmSndToggle : 1; // Send toggle, when zero bmSNDTOG0, bmSNDTOG1 otherwise + uint8_t bmRcvToggle : 1; // Send toggle, when zero bmRCVTOG0, bmRCVTOG1 otherwise + uint8_t bmNakPower : 6; // Binary order for NAK_LIMIT value + } __attribute__((packed)); + }; +} __attribute__((packed)); + +// 7 6 5 4 3 2 1 0 +// --------------------------------- +// | | H | P | P | P | A | A | A | +// --------------------------------- +// +// H - if 1 the address is a hub address +// P - parent hub address +// A - device address / port number in case of hub +// + +struct UsbDeviceAddress { + + union { + + struct { + uint8_t bmAddress : 3; // device address/port number + uint8_t bmParent : 3; // parent hub address + uint8_t bmHub : 1; // hub flag + uint8_t bmReserved : 1; // reserved, must be zero + } __attribute__((packed)); + uint8_t devAddress; + }; +} __attribute__((packed)); + +#define bmUSB_DEV_ADDR_ADDRESS 0x07 +#define bmUSB_DEV_ADDR_PARENT 0x38 +#define bmUSB_DEV_ADDR_HUB 0x40 + +struct UsbDevice { + EpInfo *epinfo; // endpoint info pointer + UsbDeviceAddress address; + uint8_t epcount; // number of endpoints + bool lowspeed; // indicates if a device is the low speed one + // uint8_t devclass; // device class +} __attribute__((packed)); + +class AddressPool { +public: + virtual UsbDevice* GetUsbDevicePtr(uint8_t addr) = 0; + virtual uint8_t AllocAddress(uint8_t parent, bool is_hub = false, uint8_t port = 0) = 0; + virtual void FreeAddress(uint8_t addr) = 0; +}; + +typedef void (*UsbDeviceHandleFunc)(UsbDevice *pdev); + +#define ADDR_ERROR_INVALID_INDEX 0xFF +#define ADDR_ERROR_INVALID_ADDRESS 0xFF + +template +class AddressPoolImpl : public AddressPool { + EpInfo dev0ep; //Endpoint data structure used during enumeration for uninitialized device + + uint8_t hubCounter; // hub counter is kept + // in order to avoid hub address duplication + + UsbDevice thePool[MAX_DEVICES_ALLOWED]; + + // Initializes address pool entry + + void InitEntry(uint8_t index) { + thePool[index].address.devAddress = 0; + thePool[index].epcount = 1; + thePool[index].lowspeed = 0; + thePool[index].epinfo = &dev0ep; + }; + + // Returns thePool index for a given address + + uint8_t FindAddressIndex(uint8_t address = 0) { + for(uint8_t i = 1; i < MAX_DEVICES_ALLOWED; i++) { + if(thePool[i].address.devAddress == address) + return i; + } + return 0; + }; + + // Returns thePool child index for a given parent + + uint8_t FindChildIndex(UsbDeviceAddress addr, uint8_t start = 1) { + for(uint8_t i = (start < 1 || start >= MAX_DEVICES_ALLOWED) ? 1 : start; i < MAX_DEVICES_ALLOWED; i++) { + if(thePool[i].address.bmParent == addr.bmAddress) + return i; + } + return 0; + }; + + // Frees address entry specified by index parameter + + void FreeAddressByIndex(uint8_t index) { + // Zero field is reserved and should not be affected + if(index == 0) + return; + + UsbDeviceAddress uda = thePool[index].address; + // If a hub was switched off all port addresses should be freed + if(uda.bmHub == 1) { + for(uint8_t i = 1; (i = FindChildIndex(uda, i));) + FreeAddressByIndex(i); + + // If the hub had the last allocated address, hubCounter should be decremented + if(hubCounter == uda.bmAddress) + hubCounter--; + } + InitEntry(index); + } + + // Initializes the whole address pool at once + + void InitAllAddresses() { + for(uint8_t i = 1; i < MAX_DEVICES_ALLOWED; i++) + InitEntry(i); + + hubCounter = 0; + }; + +public: + + AddressPoolImpl() : hubCounter(0) { + // Zero address is reserved + InitEntry(0); + + thePool[0].address.devAddress = 0; + thePool[0].epinfo = &dev0ep; + dev0ep.epAddr = 0; + dev0ep.maxPktSize = 8; + dev0ep.epAttribs = 0; //set DATA0/1 toggles to 0 + dev0ep.bmNakPower = USB_NAK_MAX_POWER; + + InitAllAddresses(); + }; + + // Returns a pointer to a specified address entry + + virtual UsbDevice* GetUsbDevicePtr(uint8_t addr) { + if(!addr) + return thePool; + + uint8_t index = FindAddressIndex(addr); + + return (!index) ? NULL : thePool + index; + }; + + // Performs an operation specified by pfunc for each addressed device + + void ForEachUsbDevice(UsbDeviceHandleFunc pfunc) { + if(!pfunc) + return; + + for(uint8_t i = 1; i < MAX_DEVICES_ALLOWED; i++) + if(thePool[i].address.devAddress) + pfunc(thePool + i); + }; + + // Allocates new address + + virtual uint8_t AllocAddress(uint8_t parent, bool is_hub = false, uint8_t port = 0) { + /* if (parent != 0 && port == 0) + USB_HOST_SERIAL.println("PRT:0"); */ + UsbDeviceAddress _parent; + _parent.devAddress = parent; + if(_parent.bmReserved || port > 7) + //if(parent > 127 || port > 7) + return 0; + + if(is_hub && hubCounter == 7) + return 0; + + // finds first empty address entry starting from one + uint8_t index = FindAddressIndex(0); + + if(!index) // if empty entry is not found + return 0; + + if(_parent.devAddress == 0) { + if(is_hub) { + thePool[index].address.devAddress = 0x41; + hubCounter++; + } else + thePool[index].address.devAddress = 1; + + return thePool[index].address.devAddress; + } + + UsbDeviceAddress addr; + addr.devAddress = 0; // Ensure all bits are zero + addr.bmParent = _parent.bmAddress; + if(is_hub) { + addr.bmHub = 1; + addr.bmAddress = ++hubCounter; + } else { + addr.bmHub = 0; + addr.bmAddress = port; + } + thePool[index].address = addr; + /* + USB_HOST_SERIAL.print("Addr:"); + USB_HOST_SERIAL.print(addr.bmHub, HEX); + USB_HOST_SERIAL.print("."); + USB_HOST_SERIAL.print(addr.bmParent, HEX); + USB_HOST_SERIAL.print("."); + USB_HOST_SERIAL.println(addr.bmAddress, HEX); + */ + return thePool[index].address.devAddress; + }; + + // Empties pool entry + + virtual void FreeAddress(uint8_t addr) { + // if the root hub is disconnected all the addresses should be initialized + if(addr == 0x41) { + InitAllAddresses(); + return; + } + uint8_t index = FindAddressIndex(addr); + FreeAddressByIndex(index); + }; + + // Returns number of hubs attached + // It can be rather helpfull to find out if there are hubs attached than getting the exact number of hubs. + //uint8_t GetNumHubs() + //{ + // return hubCounter; + //}; + //uint8_t GetNumDevices() + //{ + // uint8_t counter = 0; + + // for (uint8_t i=1; iRegisterDeviceClass(this); //set devConfig[] entry + } +} + +uint8_t ADK::ConfigureDevice(uint8_t parent, uint8_t port, bool lowspeed) { + return Init(parent, port, lowspeed); // Just call Init. Yes, really! +} + +/* Connection initialization of an Android phone */ +uint8_t ADK::Init(uint8_t parent, uint8_t port, bool lowspeed) { + uint8_t buf[sizeof (USB_DEVICE_DESCRIPTOR)]; + USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast(buf); + uint8_t rcode; + uint8_t num_of_conf; // number of configurations + UsbDevice *p = NULL; + EpInfo *oldep_ptr = NULL; + + // get memory address of USB device address pool + AddressPool &addrPool = pUsb->GetAddressPool(); + + USBTRACE("\r\nADK Init"); + + // check if address has already been assigned to an instance + if(bAddress) { + USBTRACE("\r\nAddress in use"); + return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE; + } + + // Get pointer to pseudo device with address 0 assigned + p = addrPool.GetUsbDevicePtr(0); + + if(!p) { + USBTRACE("\r\nAddress not found"); + return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; + } + + if(!p->epinfo) { + USBTRACE("epinfo is null\r\n"); + return USB_ERROR_EPINFO_IS_NULL; + } + + // Save old pointer to EP_RECORD of address 0 + oldep_ptr = p->epinfo; + + // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence + p->epinfo = epInfo; + + p->lowspeed = lowspeed; + + // Get device descriptor + rcode = pUsb->getDevDescr(0, 0, sizeof (USB_DEVICE_DESCRIPTOR), (uint8_t*)buf); + + // Restore p->epinfo + p->epinfo = oldep_ptr; + + if(rcode) { + goto FailGetDevDescr; + } + + // Allocate new address according to device class + bAddress = addrPool.AllocAddress(parent, false, port); + + // Extract Max Packet Size from device descriptor + epInfo[0].maxPktSize = udd->bMaxPacketSize0; + + // Assign new address to the device + rcode = pUsb->setAddr(0, 0, bAddress); + if(rcode) { + p->lowspeed = false; + addrPool.FreeAddress(bAddress); + bAddress = 0; + //USBTRACE2("setAddr:",rcode); + return rcode; + }//if (rcode... + + //USBTRACE2("\r\nAddr:", bAddress); + // Spec says you should wait at least 200ms. + //delay(300); + + p->lowspeed = false; + + //get pointer to assigned address record + p = addrPool.GetUsbDevicePtr(bAddress); + if(!p) { + return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; + } + + p->lowspeed = lowspeed; + + // Assign epInfo to epinfo pointer - only EP0 is known + rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo); + if(rcode) { + goto FailSetDevTblEntry; + } + + //check if ADK device is already in accessory mode; if yes, configure and exit + if(udd->idVendor == ADK_VID && + (udd->idProduct == ADK_PID || udd->idProduct == ADB_PID)) { + USBTRACE("\r\nAcc.mode device detected"); + /* go through configurations, find first bulk-IN, bulk-OUT EP, fill epInfo and quit */ + num_of_conf = udd->bNumConfigurations; + + //USBTRACE2("\r\nNC:",num_of_conf); + for(uint8_t i = 0; i < num_of_conf; i++) { + ConfigDescParser < 0, 0, 0, 0 > confDescrParser(this); + delay(1); + rcode = pUsb->getConfDescr(bAddress, 0, i, &confDescrParser); +#if defined(XOOM) + //added by Jaylen Scott Vanorden + if(rcode) { + USBTRACE2("\r\nGot 1st bad code for config: ", rcode); + // Try once more + rcode = pUsb->getConfDescr(bAddress, 0, i, &confDescrParser); + } +#endif + if(rcode) { + goto FailGetConfDescr; + } + if(bNumEP > 2) { + break; + } + } // for (uint8_t i=0; isetEpInfoEntry(bAddress, 3, epInfo); + if(rcode) { + goto FailSetDevTblEntry; + } + } + + // Set Configuration Value + rcode = pUsb->setConf(bAddress, 0, bConfNum); + if(rcode) { + goto FailSetConfDescr; + } + /* print endpoint structure */ + /* + USBTRACE("\r\nEndpoint Structure:"); + USBTRACE("\r\nEP0:"); + USBTRACE2("\r\nAddr: ", epInfo[0].epAddr); + USBTRACE2("\r\nMax.pkt.size: ", epInfo[0].maxPktSize); + USBTRACE2("\r\nAttr: ", epInfo[0].epAttribs); + USBTRACE("\r\nEpout:"); + USBTRACE2("\r\nAddr: ", epInfo[epDataOutIndex].epAddr); + USBTRACE2("\r\nMax.pkt.size: ", epInfo[epDataOutIndex].maxPktSize); + USBTRACE2("\r\nAttr: ", epInfo[epDataOutIndex].epAttribs); + USBTRACE("\r\nEpin:"); + USBTRACE2("\r\nAddr: ", epInfo[epDataInIndex].epAddr); + USBTRACE2("\r\nMax.pkt.size: ", epInfo[epDataInIndex].maxPktSize); + USBTRACE2("\r\nAttr: ", epInfo[epDataInIndex].epAttribs); + */ + + USBTRACE("\r\nConfiguration successful"); + ready = true; + return 0; //successful configuration + }//if( buf->idVendor == ADK_VID... + + //probe device - get accessory protocol revision + { + uint16_t adkproto = -1; + delay(1); + rcode = getProto((uint8_t*) & adkproto); +#if defined(XOOM) + //added by Jaylen Scott Vanorden + if(rcode) { + USBTRACE2("\r\nGot 1st bad code for proto: ", rcode); + // Try once more + rcode = getProto((uint8_t*) & adkproto); + } +#endif + if(rcode) { + goto FailGetProto; //init fails + } + USBTRACE2("\r\nADK protocol rev. ", adkproto); + } + + delay(100); + + //sending ID strings + sendStr(ACCESSORY_STRING_MANUFACTURER, manufacturer); + delay(10); + sendStr(ACCESSORY_STRING_MODEL, model); + delay(10); + sendStr(ACCESSORY_STRING_DESCRIPTION, description); + delay(10); + sendStr(ACCESSORY_STRING_VERSION, version); + delay(10); + sendStr(ACCESSORY_STRING_URI, uri); + delay(10); + sendStr(ACCESSORY_STRING_SERIAL, serial); + + delay(100); + + //switch to accessory mode + //the Android phone will reset + rcode = switchAcc(); + if(rcode) { + goto FailSwAcc; //init fails + } + rcode = USB_ERROR_CONFIG_REQUIRES_ADDITIONAL_RESET; + delay(100); // Give Android a chance to do its reset. This is a guess, and possibly could be lower. + goto SwAttempt; //switch to accessory mode attempted + + /* diagnostic messages */ +FailGetDevDescr: +#ifdef DEBUG_USB_HOST + NotifyFailGetDevDescr(rcode); + goto Fail; +#endif + +FailSetDevTblEntry: +#ifdef DEBUG_USB_HOST + NotifyFailSetDevTblEntry(rcode); + goto Fail; +#endif + +FailGetConfDescr: +#ifdef DEBUG_USB_HOST + NotifyFailGetConfDescr(rcode); + goto Fail; +#endif + +FailSetConfDescr: +#ifdef DEBUG_USB_HOST + NotifyFailSetConfDescr(rcode); + goto Fail; +#endif + +FailGetProto: +#ifdef DEBUG_USB_HOST + USBTRACE("\r\ngetProto:"); + goto Fail; +#endif + +FailSwAcc: +#ifdef DEBUG_USB_HOST + USBTRACE("\r\nswAcc:"); + goto Fail; +#endif + + //FailOnInit: + // USBTRACE("OnInit:"); + // goto Fail; + // +SwAttempt: +#ifdef DEBUG_USB_HOST + USBTRACE("\r\nAccessory mode switch attempt"); +Fail: +#endif + //USBTRACE2("\r\nADK Init Failed, error code: ", rcode); + //NotifyFail(rcode); + Release(); + return rcode; +} + +/* Extracts bulk-IN and bulk-OUT endpoint information from config descriptor */ +void ADK::EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *pep) { + //ErrorMessage(PSTR("Conf.Val"), conf); + //ErrorMessage(PSTR("Iface Num"), iface); + //ErrorMessage(PSTR("Alt.Set"), alt); + + //added by Yuuichi Akagawa + if(bNumEP == 3) { + return; + } + + bConfNum = conf; + + if((pep->bmAttributes & 0x02) == 2) { + uint8_t index = ((pep->bEndpointAddress & 0x80) == 0x80) ? epDataInIndex : epDataOutIndex; + // Fill in the endpoint info structure + epInfo[index].epAddr = (pep->bEndpointAddress & 0x0F); + epInfo[index].maxPktSize = (uint8_t)pep->wMaxPacketSize; + + bNumEP++; + + //PrintEndpointDescriptor(pep); + } +} + +/* Performs a cleanup after failed Init() attempt */ +uint8_t ADK::Release() { + pUsb->GetAddressPool().FreeAddress(bAddress); + + bNumEP = 1; //must have to be reset to 1 + + bAddress = 0; + ready = false; + return 0; +} + +uint8_t ADK::RcvData(uint16_t *bytes_rcvd, uint8_t *dataptr) { + //USBTRACE2("\r\nAddr: ", bAddress ); + //USBTRACE2("\r\nEP: ",epInfo[epDataInIndex].epAddr); + return pUsb->inTransfer(bAddress, epInfo[epDataInIndex].epAddr, bytes_rcvd, dataptr); +} + +uint8_t ADK::SndData(uint16_t nbytes, uint8_t *dataptr) { + return pUsb->outTransfer(bAddress, epInfo[epDataOutIndex].epAddr, nbytes, dataptr); +} + +void ADK::PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr) { + Notify(PSTR("Endpoint descriptor:"), 0x80); + Notify(PSTR("\r\nLength:\t\t"), 0x80); + D_PrintHex (ep_ptr->bLength, 0x80); + Notify(PSTR("\r\nType:\t\t"), 0x80); + D_PrintHex (ep_ptr->bDescriptorType, 0x80); + Notify(PSTR("\r\nAddress:\t"), 0x80); + D_PrintHex (ep_ptr->bEndpointAddress, 0x80); + Notify(PSTR("\r\nAttributes:\t"), 0x80); + D_PrintHex (ep_ptr->bmAttributes, 0x80); + Notify(PSTR("\r\nMaxPktSize:\t"), 0x80); + D_PrintHex (ep_ptr->wMaxPacketSize, 0x80); + Notify(PSTR("\r\nPoll Intrv:\t"), 0x80); + D_PrintHex (ep_ptr->bInterval, 0x80); + Notify(PSTR("\r\n"), 0x80); +} diff --git a/libraries/USB_Host_Shield/adk.h b/libraries/USB_Host_Shield/adk.h new file mode 100755 index 0000000..4a2920b --- /dev/null +++ b/libraries/USB_Host_Shield/adk.h @@ -0,0 +1,140 @@ +/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved. + +This software may be distributed and modified under the terms of the GNU +General Public License version 2 (GPL2) as published by the Free Software +Foundation and appearing in the file GPL2.TXT included in the packaging of +this file. Please note that GPL2 Section 2[b] requires that all works based +on this software must also be made publicly available under the terms of +the GPL2 ("Copyleft"). + +Contact information +------------------- + +Circuits At Home, LTD +Web : http://www.circuitsathome.com +e-mail : support@circuitsathome.com + */ + +/* Google ADK interface support header */ + +#if !defined(_ADK_H_) +#define _ADK_H_ + +#include "Usb.h" + +#define ADK_VID 0x18D1 +#define ADK_PID 0x2D00 +#define ADB_PID 0x2D01 + +#define XOOM //enables repeating getProto() and getConf() attempts +//necessary for slow devices such as Motorola XOOM +//defined by default, can be commented out to save memory + +/* requests */ + +#define ADK_GETPROTO 51 //check USB accessory protocol version +#define ADK_SENDSTR 52 //send identifying string +#define ADK_ACCSTART 53 //start device in accessory mode + +#define bmREQ_ADK_GET USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_VENDOR|USB_SETUP_RECIPIENT_DEVICE +#define bmREQ_ADK_SEND USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_VENDOR|USB_SETUP_RECIPIENT_DEVICE + +#define ACCESSORY_STRING_MANUFACTURER 0 +#define ACCESSORY_STRING_MODEL 1 +#define ACCESSORY_STRING_DESCRIPTION 2 +#define ACCESSORY_STRING_VERSION 3 +#define ACCESSORY_STRING_URI 4 +#define ACCESSORY_STRING_SERIAL 5 + +#define ADK_MAX_ENDPOINTS 3 //endpoint 0, bulk_IN, bulk_OUT + +class ADK; + +class ADK : public USBDeviceConfig, public UsbConfigXtracter { +private: + /* ID strings */ + const char* manufacturer; + const char* model; + const char* description; + const char* version; + const char* uri; + const char* serial; + + /* ADK proprietary requests */ + uint8_t getProto(uint8_t* adkproto); + uint8_t sendStr(uint8_t index, const char* str); + uint8_t switchAcc(void); + +protected: + static const uint8_t epDataInIndex; // DataIn endpoint index + static const uint8_t epDataOutIndex; // DataOUT endpoint index + + /* mandatory members */ + USB *pUsb; + uint8_t bAddress; + uint8_t bConfNum; // configuration number + + uint8_t bNumEP; // total number of EP in the configuration + bool ready; + + /* Endpoint data structure */ + EpInfo epInfo[ADK_MAX_ENDPOINTS]; + + void PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr); + +public: + ADK(USB *pUsb, const char* manufacturer, + const char* model, + const char* description, + const char* version, + const char* uri, + const char* serial); + + // Methods for receiving and sending data + uint8_t RcvData(uint16_t *nbytesptr, uint8_t *dataptr); + uint8_t SndData(uint16_t nbytes, uint8_t *dataptr); + + + // USBDeviceConfig implementation + uint8_t ConfigureDevice(uint8_t parent, uint8_t port, bool lowspeed); + uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed); + uint8_t Release(); + + virtual uint8_t Poll() { + return 0; + }; + + virtual uint8_t GetAddress() { + return bAddress; + }; + + virtual bool isReady() { + return ready; + }; + + virtual bool VIDPIDOK(uint16_t vid, uint16_t pid) { + return (vid == ADK_VID && (pid == ADK_PID || pid == ADB_PID)); + }; + + //UsbConfigXtracter implementation + void EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *ep); +}; //class ADK : public USBDeviceConfig ... + +/* get ADK protocol version */ + +/* returns 2 bytes in *adkproto */ +inline uint8_t ADK::getProto(uint8_t* adkproto) { + return ( pUsb->ctrlReq(bAddress, 0, bmREQ_ADK_GET, ADK_GETPROTO, 0, 0, 0, 2, 2, adkproto, NULL)); +} + +/* send ADK string */ +inline uint8_t ADK::sendStr(uint8_t index, const char* str) { + return ( pUsb->ctrlReq(bAddress, 0, bmREQ_ADK_SEND, ADK_SENDSTR, 0, 0, index, strlen(str) + 1, strlen(str) + 1, (uint8_t*)str, NULL)); +} + +/* switch to accessory mode */ +inline uint8_t ADK::switchAcc(void) { + return ( pUsb->ctrlReq(bAddress, 0, bmREQ_ADK_SEND, ADK_ACCSTART, 0, 0, 0, 0, 0, NULL, NULL)); +} + +#endif // _ADK_H_ diff --git a/libraries/USB_Host_Shield/avrpins.h b/libraries/USB_Host_Shield/avrpins.h new file mode 100755 index 0000000..982f2fa --- /dev/null +++ b/libraries/USB_Host_Shield/avrpins.h @@ -0,0 +1,1067 @@ +/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved. + +This software may be distributed and modified under the terms of the GNU +General Public License version 2 (GPL2) as published by the Free Software +Foundation and appearing in the file GPL2.TXT included in the packaging of +this file. Please note that GPL2 Section 2[b] requires that all works based +on this software must also be made publicly available under the terms of +the GPL2 ("Copyleft"). + +Contact information +------------------- + +Circuits At Home, LTD +Web : http://www.circuitsathome.com +e-mail : support@circuitsathome.com + */ + +/* derived from Konstantin Chizhov's AVR port templates */ + +#if !defined(_usb_h_) || defined(_avrpins_h_) +#error "Never include avrpins.h directly; include Usb.h instead" +#else +#define _avrpins_h_ + +#if defined(__AVR__) + +// pointers are 16 bits on AVR +#define pgm_read_pointer(p) pgm_read_word(p) + +// Support for these boards needs to be manually activated in settings.h or in a makefile +#if !defined(BOARD_MEGA_ADK) && defined(__AVR_ATmega2560__) && (USE_UHS_MEGA_ADK || defined(ARDUINO_AVR_ADK)) +#define BOARD_MEGA_ADK +#elif !defined(BOARD_BLACK_WIDDOW) && USE_UHS_BLACK_WIDDOW +#define BOARD_BLACK_WIDDOW +#endif + +#ifdef PORTA +#define USE_PORTA +#endif +#ifdef PORTB +#define USE_PORTB +#endif +#ifdef PORTC +#define USE_PORTC +#endif +#ifdef PORTD +#define USE_PORTD +#endif +#ifdef PORTE +#define USE_PORTE +#endif +#ifdef PORTF +#define USE_PORTF +#endif +#ifdef PORTG +#define USE_PORTG +#endif +#ifdef PORTH +#define USE_PORTH +#endif +#ifdef PORTJ +#define USE_PORTJ +#endif +#ifdef PORTK +#define USE_PORTK +#endif +#ifdef PORTL +#define USE_PORTL +#endif +#ifdef PORTQ +#define USE_PORTQ +#endif +#ifdef PORTR +#define USE_PORTR +#endif + +#ifdef TCCR0A +#define USE_TCCR0A +#endif +#ifdef TCCR1A +#define USE_TCCR1A +#endif +#ifdef TCCR2A +#define USE_TCCR2A +#endif + +//Port definitions for AtTiny, AtMega families. + +#define MAKE_PORT(portName, ddrName, pinName, className, ID) \ + class className{\ + public:\ + typedef uint8_t DataT;\ + public:\ + static void Write(DataT value){portName = value;}\ + static void ClearAndSet(DataT clearMask, DataT value){portName = (portName & ~clearMask) | value;}\ + static DataT Read(){return portName;}\ + static void DirWrite(DataT value){ddrName = value;}\ + static DataT DirRead(){return ddrName;}\ + static void Set(DataT value){portName |= value;}\ + static void Clear(DataT value){portName &= ~value;}\ + static void Toggle(DataT value){portName ^= value;}\ + static void DirSet(DataT value){ddrName |= value;}\ + static void DirClear(DataT value){ddrName &= ~value;}\ + static void DirToggle(DataT value){ddrName ^= value;}\ + static DataT PinRead(){return pinName;}\ + enum{Id = ID};\ + enum{Width=sizeof(DataT)*8};\ + }; + +// TCCR registers to set/clear Arduino PWM +#define MAKE_TCCR(TccrName, className) \ + class className{\ + public:\ + typedef uint8_t DataT;\ + public:\ + static void Write(DataT value){TccrName = value;}\ + static void ClearAndSet(DataT clearMask, DataT value){TccrName = (TccrName & ~clearMask) | value;}\ + static DataT Read(){return TccrName;}\ + static void Set(DataT value){TccrName |= value;}\ + static void Clear(DataT value){TccrName &= ~value;}\ + static void Toggle(DataT value){TccrName ^= value;}\ + enum{Width=sizeof(DataT)*8};\ + }; + +#ifdef USE_PORTA + +MAKE_PORT(PORTA, DDRA, PINA, Porta, 'A') +#endif +#ifdef USE_PORTB +MAKE_PORT(PORTB, DDRB, PINB, Portb, 'B') +#endif +#ifdef USE_PORTC +MAKE_PORT(PORTC, DDRC, PINC, Portc, 'C') +#endif +#ifdef USE_PORTD +MAKE_PORT(PORTD, DDRD, PIND, Portd, 'D') +#endif +#ifdef USE_PORTE +MAKE_PORT(PORTE, DDRE, PINE, Porte, 'E') +#endif +#ifdef USE_PORTF +MAKE_PORT(PORTF, DDRF, PINF, Portf, 'F') +#endif +#ifdef USE_PORTG +MAKE_PORT(PORTG, DDRG, PING, Portg, 'G') +#endif +#ifdef USE_PORTH +MAKE_PORT(PORTH, DDRH, PINH, Porth, 'H') +#endif +#ifdef USE_PORTJ +MAKE_PORT(PORTJ, DDRJ, PINJ, Portj, 'J') +#endif +#ifdef USE_PORTK +MAKE_PORT(PORTK, DDRK, PINK, Portk, 'K') +#endif +#ifdef USE_PORTL +MAKE_PORT(PORTL, DDRL, PINL, Portl, 'L') +#endif +#ifdef USE_PORTQ +MAKE_PORT(PORTQ, DDRQ, PINQ, Portq, 'Q') +#endif +#ifdef USE_PORTR +MAKE_PORT(PORTR, DDRR, PINR, Portr, 'R') +#endif + +#ifdef USE_TCCR0A +MAKE_TCCR(TCCR0A, Tccr0a) +#endif +#ifdef USE_TCCR1A +MAKE_TCCR(TCCR1A, Tccr1a) +#endif +#ifdef USE_TCCR2A +MAKE_TCCR(TCCR2A, Tccr2a) +#endif + +// this class represents one pin in a IO port. +// It is fully static. +template +class TPin { + // BOOST_STATIC_ASSERT(PIN < PORT::Width); +public: + typedef PORT Port; + + enum { + Number = PIN + }; + + static void Set() { + PORT::Set(1 << PIN); + } + + static void Set(uint8_t val) { + if(val) + Set(); + else Clear(); + } + + static void SetDir(uint8_t val) { + if(val) + SetDirWrite(); + else SetDirRead(); + } + + static void Clear() { + PORT::Clear(1 << PIN); + } + + static void Toggle() { + PORT::Toggle(1 << PIN); + } + + static void SetDirRead() { + PORT::DirClear(1 << PIN); + } + + static void SetDirWrite() { + PORT::DirSet(1 << PIN); + } + + static uint8_t IsSet() { + return PORT::PinRead() & (uint8_t)(1 << PIN); + } + + static void WaiteForSet() { + while(IsSet() == 0) { + } + } + + static void WaiteForClear() { + while(IsSet()) { + } + } +}; //class TPin... + +// this class represents one bit in TCCR port. +// used to set/clear TCCRx bits +// It is fully static. + +template +class TCom { + // BOOST_STATIC_ASSERT(PIN < PORT::Width); +public: + typedef TCCR Tccr; + + enum { + Com = COM + }; + + static void Set() { + TCCR::Set(1 << COM); + } + + static void Clear() { + TCCR::Clear(1 << COM); + } + + static void Toggle() { + TCCR::Toggle(1 << COM); + } +}; //class TCom... + +//Short pin definitions +#ifdef USE_PORTA +typedef TPin Pa0; +typedef TPin Pa1; +typedef TPin Pa2; +typedef TPin Pa3; +typedef TPin Pa4; +typedef TPin Pa5; +typedef TPin Pa6; +typedef TPin Pa7; +#endif + +#ifdef USE_PORTB +typedef TPin Pb0; +typedef TPin Pb1; +typedef TPin Pb2; +typedef TPin Pb3; +typedef TPin Pb4; +typedef TPin Pb5; +typedef TPin Pb6; +typedef TPin Pb7; +#endif + +#ifdef USE_PORTC +typedef TPin Pc0; +typedef TPin Pc1; +typedef TPin Pc2; +typedef TPin Pc3; +typedef TPin Pc4; +typedef TPin Pc5; +typedef TPin Pc6; +typedef TPin Pc7; +#endif + +#ifdef USE_PORTD +typedef TPin Pd0; +typedef TPin Pd1; +typedef TPin Pd2; +typedef TPin Pd3; +typedef TPin Pd4; +typedef TPin Pd5; +typedef TPin Pd6; +typedef TPin Pd7; +#endif + +#ifdef USE_PORTE +typedef TPin Pe0; +typedef TPin Pe1; +typedef TPin Pe2; +typedef TPin Pe3; +typedef TPin Pe4; +typedef TPin Pe5; +typedef TPin Pe6; +typedef TPin Pe7; +#endif + +#ifdef USE_PORTF +typedef TPin Pf0; +typedef TPin Pf1; +typedef TPin Pf2; +typedef TPin Pf3; +typedef TPin Pf4; +typedef TPin Pf5; +typedef TPin Pf6; +typedef TPin Pf7; +#endif + +#ifdef USE_PORTG +typedef TPin Pg0; +typedef TPin Pg1; +typedef TPin Pg2; +typedef TPin Pg3; +typedef TPin Pg4; +typedef TPin Pg5; +typedef TPin Pg6; +typedef TPin Pg7; +#endif + +#ifdef USE_PORTH +typedef TPin Ph0; +typedef TPin Ph1; +typedef TPin Ph2; +typedef TPin Ph3; +typedef TPin Ph4; +typedef TPin Ph5; +typedef TPin Ph6; +typedef TPin Ph7; +#endif + +#ifdef USE_PORTJ +typedef TPin Pj0; +typedef TPin Pj1; +typedef TPin Pj2; +typedef TPin Pj3; +typedef TPin Pj4; +typedef TPin Pj5; +typedef TPin Pj6; +typedef TPin Pj7; +#endif + +#ifdef USE_PORTK +typedef TPin Pk0; +typedef TPin Pk1; +typedef TPin Pk2; +typedef TPin Pk3; +typedef TPin Pk4; +typedef TPin Pk5; +typedef TPin Pk6; +typedef TPin Pk7; +#endif + +#ifdef USE_PORTL +typedef TPin Pl0; +typedef TPin Pl1; +typedef TPin Pl2; +typedef TPin Pl3; +typedef TPin Pl4; +typedef TPin Pl5; +typedef TPin Pl6; +typedef TPin Pl7; +#endif + +#ifdef USE_PORTQ +typedef TPin Pq0; +typedef TPin Pq1; +typedef TPin Pq2; +typedef TPin Pq3; +typedef TPin Pq4; +typedef TPin Pq5; +typedef TPin Pq6; +typedef TPin Pq7; +#endif + +#ifdef USE_PORTR +typedef TPin Pr0; +typedef TPin Pr1; +typedef TPin Pr2; +typedef TPin Pr3; +typedef TPin Pr4; +typedef TPin Pr5; +typedef TPin Pr6; +typedef TPin Pr7; +#endif + +#ifdef USE_TCCR0A +typedef TCom Tc0a; //P6 +typedef TCom Tc0b; //P5 +#endif + +#ifdef USE_TCCR1A +typedef TCom Tc1a; //P9 +typedef TCom Tc1b; //P10 +#endif + +#ifdef USE_TCCR2A +typedef TCom Tc2a; //P11 +typedef TCom Tc2b; //P3 +#endif + +template +class Tp_Tc { +public: + + static void SetDir(uint8_t val) { + if(val) + SetDirWrite(); + else SetDirRead(); + } + + static void SetDirRead() { + Tp_pin::SetDirRead(); //set pin direction + Tc_bit::Clear(); //disconnect pin from PWM + } + + static void SetDirWrite() { + Tp_pin::SetDirWrite(); + Tc_bit::Clear(); + } +}; + +/* pin definitions for cases where it's necessary to clear compare output mode bits */ + +//typedef Tp_Tc P3; //Arduino pin 3 +//typedef Tp_Tc P5; //Arduino pin 5 +//typedef Tp_Tc P6; //Arduino pin 6 +//typedef Tp_Tc P9; //Arduino pin 9 +//typedef Tp_Tc P10; //Arduino pin 10 +//typedef Tp_Tc P11; //Arduino pin 11 + +/* Arduino pin definitions */ +#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) +// "Mega" Arduino pin numbers + +#define P0 Pe0 +#define P1 Pe1 +#define P2 Pe4 +#define P3 Pe5 +#define P4 Pg5 +#define P5 Pe3 +#define P6 Ph3 +#define P7 Ph4 + +#define P8 Ph5 +#define P9 Ph6 +#define P10 Pb4 +#define P11 Pb5 +#define P12 Pb6 +#define P13 Pb7 + +#define P14 Pj1 +#define P15 Pj0 +#define P16 Ph1 +#define P17 Ph0 +#define P18 Pd3 +#define P19 Pd2 +#define P20 Pd1 +#define P21 Pd0 + +#define P22 Pa0 +#define P23 Pa1 +#define P24 Pa2 +#define P25 Pa3 +#define P26 Pa4 +#define P27 Pa5 +#define P28 Pa6 +#define P29 Pa7 +#define P30 Pc7 +#define P31 Pc6 +#define P32 Pc5 +#define P33 Pc4 +#define P34 Pc3 +#define P35 Pc2 +#define P36 Pc1 +#define P37 Pc0 + +#define P38 Pd7 +#define P39 Pg2 +#define P40 Pg1 +#define P41 Pg0 +#define P42 Pl7 +#define P43 Pl6 +#define P44 Pl5 +#define P45 Pl4 +#define P46 Pl3 +#define P47 Pl2 +#define P48 Pl1 +#define P49 Pl0 +#define P50 Pb3 +#define P51 Pb2 +#define P52 Pb1 +#define P53 Pb0 + +#ifdef BOARD_MEGA_ADK // These pins are not broken out on the Arduino ADK +#define P54 Pe6 // INT on Arduino ADK +#define P55 Pj2 // MAX_RESET on Arduino ADK +#endif + +// "Mega" pin numbers + +#elif defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__) +// "Classic" Arduino pin numbers + +#define P0 Pd0 +#define P1 Pd1 +#define P2 Pd2 +#define P3 Pd3 +#define P4 Pd4 +#define P5 Pd5 +#define P6 Pd6 +#define P7 Pd7 + +#define P8 Pb0 +#define P9 Pb1 +#define P10 Pb2 +#define P11 Pb3 +#define P12 Pb4 +#define P13 Pb5 + +#define P14 Pc0 +#define P15 Pc1 +#define P16 Pc2 +#define P17 Pc3 +#define P18 Pc4 +#define P19 Pc5 + +// "Classic" Arduino pin numbers + +#elif defined(CORE_TEENSY) && defined(__AVR_ATmega32U4__) +// Teensy 2.0 pin numbers +// http://www.pjrc.com/teensy/pinout.html +#define P0 Pb0 +#define P1 Pb1 +#define P2 Pb2 +#define P3 Pb3 +#define P4 Pb7 +#define P5 Pd0 +#define P6 Pd1 +#define P7 Pd2 +#define P8 Pd3 +#define P9 Pc6 +#define P10 Pc7 +#define P11 Pd6 +#define P12 Pd7 +#define P13 Pb4 +#define P14 Pb5 +#define P15 Pb6 +#define P16 Pf7 +#define P17 Pf6 +#define P18 Pf5 +#define P19 Pf4 +#define P20 Pf1 +#define P21 Pf0 +#define P22 Pd4 +#define P23 Pd5 +#define P24 Pe6 +// Teensy 2.0 + +#elif defined(__AVR_ATmega32U4__) +// Arduino Leonardo pin numbers + +#define P0 Pd2 // D0 - PD2 +#define P1 Pd3 // D1 - PD3 +#define P2 Pd1 // D2 - PD1 +#define P3 Pd0 // D3 - PD0 +#define P4 Pd4 // D4 - PD4 +#define P5 Pc6 // D5 - PC6 +#define P6 Pd7 // D6 - PD7 +#define P7 Pe6 // D7 - PE6 + +#define P8 Pb4 // D8 - PB4 +#define P9 Pb5 // D9 - PB5 +#define P10 Pb6 // D10 - PB6 +#define P11 Pb7 // D11 - PB7 +#define P12 Pd6 // D12 - PD6 +#define P13 Pc7 // D13 - PC7 + +#define P14 Pb3 // D14 - MISO - PB3 +#define P15 Pb1 // D15 - SCK - PB1 +#define P16 Pb2 // D16 - MOSI - PB2 +#define P17 Pb0 // D17 - SS - PB0 + +#define P18 Pf7 // D18 - A0 - PF7 +#define P19 Pf6 // D19 - A1 - PF6 +#define P20 Pf5 // D20 - A2 - PF5 +#define P21 Pf4 // D21 - A3 - PF4 +#define P22 Pf1 // D22 - A4 - PF1 +#define P23 Pf0 // D23 - A5 - PF0 + +#define P24 Pd4 // D24 / D4 - A6 - PD4 +#define P25 Pd7 // D25 / D6 - A7 - PD7 +#define P26 Pb4 // D26 / D8 - A8 - PB4 +#define P27 Pb5 // D27 / D9 - A9 - PB5 +#define P28 Pb6 // D28 / D10 - A10 - PB6 +#define P29 Pd6 // D29 / D12 - A11 - PD6 + +// Arduino Leonardo pin numbers + +#elif defined(CORE_TEENSY) && (defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__)) +// Teensy++ 1.0 and 2.0 pin numbers +// http://www.pjrc.com/teensy/pinout.html +#define P0 Pd0 +#define P1 Pd1 +#define P2 Pd2 +#define P3 Pd3 +#define P4 Pd4 +#define P5 Pd5 +#define P6 Pd6 +#define P7 Pd7 +#define P8 Pe0 +#define P9 Pe1 +#define P10 Pc0 +#define P11 Pc1 +#define P12 Pc2 +#define P13 Pc3 +#define P14 Pc4 +#define P15 Pc5 +#define P16 Pc6 +#define P17 Pc7 +#define P18 Pe6 +#define P19 Pe7 +#define P20 Pb0 +#define P21 Pb1 +#define P22 Pb2 +#define P23 Pb3 +#define P24 Pb4 +#define P25 Pb5 +#define P26 Pb6 +#define P27 Pb7 +#define P28 Pa0 +#define P29 Pa1 +#define P30 Pa2 +#define P31 Pa3 +#define P32 Pa4 +#define P33 Pa5 +#define P34 Pa6 +#define P35 Pa7 +#define P36 Pe4 +#define P37 Pe5 +#define P38 Pf0 +#define P39 Pf1 +#define P40 Pf2 +#define P41 Pf3 +#define P42 Pf4 +#define P43 Pf5 +#define P44 Pf6 +#define P45 Pf7 +// Teensy++ 1.0 and 2.0 + +#elif defined(ARDUINO_AVR_BALANDUINO) && (defined(__AVR_ATmega644__) || defined(__AVR_ATmega1284P__)) +// Balanduino pin numbers +// http://balanduino.net/ +#define P0 Pd0 /* 0 - PD0 */ +#define P1 Pd1 /* 1 - PD1 */ + +#if BALANDUINO_REVISION < 13 + #define P2 Pb2 /* 2 - PB2 */ + #define P3 Pd6 /* 3 - PD6 */ + #define P4 Pd7 /* 4 - PD7 */ + #define P5 Pb3 /* 5 - PB3 */ +#else + #define P2 Pd2 /* 2 - PD2 */ + #define P3 Pd3 /* 3 - PD3 */ + #define P4 Pd6 /* 4 - PD6 */ + #define P5 Pd7 /* 5 - PD7 */ +#endif + +#define P6 Pb4 /* 6 - PB4 */ +#define P7 Pa0 /* 7 - PA0 */ +#define P8 Pa1 /* 8 - PA1 */ +#define P9 Pa2 /* 9 - PA2 */ +#define P10 Pa3 /* 10 - PA3 */ +#define P11 Pa4 /* 11 - PA4 */ +#define P12 Pa5 /* 12 - PA5 */ +#define P13 Pc1 /* 13 - PC1 */ +#define P14 Pc0 /* 14 - PC0 */ + +#if BALANDUINO_REVISION < 13 + #define P15 Pd2 /* 15 - PD2 */ + #define P16 Pd3 /* 16 - PD3 */ +#else + #define P15 Pb2 /* 15 - PB2 */ + #define P16 Pb3 /* 16 - PB2 */ +#endif + +#define P17 Pd4 /* 17 - PD4 */ +#define P18 Pd5 /* 18 - PD5 */ +#define P19 Pc2 /* 19 - PC2 */ +#define P20 Pc3 /* 20 - PC3 */ +#define P21 Pc4 /* 21 - PC4 */ +#define P22 Pc5 /* 22 - PC5 */ +#define P23 Pc6 /* 23 - PC6 */ +#define P24 Pc7 /* 24 - PC7 */ +#define P25 Pb0 /* 25 - PB0 */ +#define P26 Pb1 /* 26 - PB1 */ +#define P27 Pb5 /* 27 - PB5 */ +#define P28 Pb6 /* 28 - PB6 */ +#define P29 Pb7 /* 29 - PB7 */ +#define P30 Pa6 /* 30 - PA6 */ +#define P31 Pa7 /* 31 - PA7 */ +// Balanduino + +#elif defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) +// Sanguino pin numbers +// Homepage: http://sanguino.cc/hardware +// Hardware add-on: https://github.com/Lauszus/Sanguino +#define P0 Pb0 +#define P1 Pb1 +#define P2 Pb2 +#define P3 Pb3 +#define P4 Pb4 +#define P5 Pb5 +#define P6 Pb6 +#define P7 Pb7 +#define P8 Pd0 +#define P9 Pd1 +#define P10 Pd2 +#define P11 Pd3 +#define P12 Pd4 +#define P13 Pd5 +#define P14 Pd6 +#define P15 Pd7 +#define P16 Pc0 +#define P17 Pc1 +#define P18 Pc2 +#define P19 Pc3 +#define P20 Pc4 +#define P21 Pc5 +#define P22 Pc6 +#define P23 Pc7 +#define P24 Pa0 +#define P25 Pa1 +#define P26 Pa2 +#define P27 Pa3 +#define P28 Pa4 +#define P29 Pa5 +#define P30 Pa6 +#define P31 Pa7 +// Sanguino + +#else +#error "Please define board in avrpins.h" + +#endif // Arduino pin definitions + +#endif // __AVR__ + +#if defined(__arm__) + +// pointers are 32 bits on ARM +#define pgm_read_pointer(p) pgm_read_dword(p) + +#if defined(CORE_TEENSY) && (defined(__MK20DX128__) || defined(__MK20DX256__)) + +#include "core_pins.h" +#include "avr_emulation.h" + +#define GPIO_BITBAND_ADDR(reg, bit) (((uint32_t)&(reg) - 0x40000000) * 32 + (bit) * 4 + 0x42000000) +#define GPIO_BITBAND_PTR(reg, bit) ((uint8_t *)GPIO_BITBAND_ADDR((reg), (bit))) + +#define MAKE_PIN(className, baseReg, pinNum, configReg) \ +class className { \ +public: \ + static void Set() { \ + *GPIO_BITBAND_PTR(baseReg, pinNum) = 1; \ + } \ + static void Clear() { \ + *GPIO_BITBAND_PTR(baseReg, pinNum) = 0; \ + } \ + static void SetDirRead() { \ + configReg = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); \ + *(GPIO_BITBAND_PTR(baseReg, pinNum) + 640) = 0; \ + } \ + static void SetDirWrite() { \ + configReg = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); \ + *(GPIO_BITBAND_PTR(baseReg, pinNum) + 640) = 1; \ + } \ + static uint8_t IsSet() { \ + return *(GPIO_BITBAND_PTR(baseReg, pinNum) + 512); \ + } \ +}; + +MAKE_PIN(P0, CORE_PIN0_PORTREG, CORE_PIN0_BIT, CORE_PIN0_CONFIG); +MAKE_PIN(P1, CORE_PIN1_PORTREG, CORE_PIN1_BIT, CORE_PIN1_CONFIG); +MAKE_PIN(P2, CORE_PIN2_PORTREG, CORE_PIN2_BIT, CORE_PIN2_CONFIG); +MAKE_PIN(P3, CORE_PIN3_PORTREG, CORE_PIN3_BIT, CORE_PIN3_CONFIG); +MAKE_PIN(P4, CORE_PIN4_PORTREG, CORE_PIN4_BIT, CORE_PIN4_CONFIG); +MAKE_PIN(P5, CORE_PIN5_PORTREG, CORE_PIN5_BIT, CORE_PIN5_CONFIG); +MAKE_PIN(P6, CORE_PIN6_PORTREG, CORE_PIN6_BIT, CORE_PIN6_CONFIG); +MAKE_PIN(P7, CORE_PIN7_PORTREG, CORE_PIN7_BIT, CORE_PIN7_CONFIG); +MAKE_PIN(P8, CORE_PIN8_PORTREG, CORE_PIN8_BIT, CORE_PIN8_CONFIG); +MAKE_PIN(P9, CORE_PIN9_PORTREG, CORE_PIN9_BIT, CORE_PIN9_CONFIG); +MAKE_PIN(P10, CORE_PIN10_PORTREG, CORE_PIN10_BIT, CORE_PIN10_CONFIG); +MAKE_PIN(P11, CORE_PIN11_PORTREG, CORE_PIN11_BIT, CORE_PIN11_CONFIG); +MAKE_PIN(P12, CORE_PIN12_PORTREG, CORE_PIN12_BIT, CORE_PIN12_CONFIG); +MAKE_PIN(P13, CORE_PIN13_PORTREG, CORE_PIN13_BIT, CORE_PIN13_CONFIG); +MAKE_PIN(P14, CORE_PIN14_PORTREG, CORE_PIN14_BIT, CORE_PIN14_CONFIG); +MAKE_PIN(P15, CORE_PIN15_PORTREG, CORE_PIN15_BIT, CORE_PIN15_CONFIG); +MAKE_PIN(P16, CORE_PIN16_PORTREG, CORE_PIN16_BIT, CORE_PIN16_CONFIG); +MAKE_PIN(P17, CORE_PIN17_PORTREG, CORE_PIN17_BIT, CORE_PIN17_CONFIG); +MAKE_PIN(P18, CORE_PIN18_PORTREG, CORE_PIN18_BIT, CORE_PIN18_CONFIG); +MAKE_PIN(P19, CORE_PIN19_PORTREG, CORE_PIN19_BIT, CORE_PIN19_CONFIG); +MAKE_PIN(P20, CORE_PIN20_PORTREG, CORE_PIN20_BIT, CORE_PIN20_CONFIG); +MAKE_PIN(P21, CORE_PIN21_PORTREG, CORE_PIN21_BIT, CORE_PIN21_CONFIG); +MAKE_PIN(P22, CORE_PIN22_PORTREG, CORE_PIN22_BIT, CORE_PIN22_CONFIG); +MAKE_PIN(P23, CORE_PIN23_PORTREG, CORE_PIN23_BIT, CORE_PIN23_CONFIG); +MAKE_PIN(P24, CORE_PIN24_PORTREG, CORE_PIN24_BIT, CORE_PIN24_CONFIG); +MAKE_PIN(P25, CORE_PIN25_PORTREG, CORE_PIN25_BIT, CORE_PIN25_CONFIG); +MAKE_PIN(P26, CORE_PIN26_PORTREG, CORE_PIN26_BIT, CORE_PIN26_CONFIG); +MAKE_PIN(P27, CORE_PIN27_PORTREG, CORE_PIN27_BIT, CORE_PIN27_CONFIG); +MAKE_PIN(P28, CORE_PIN28_PORTREG, CORE_PIN28_BIT, CORE_PIN28_CONFIG); +MAKE_PIN(P29, CORE_PIN29_PORTREG, CORE_PIN29_BIT, CORE_PIN29_CONFIG); +MAKE_PIN(P30, CORE_PIN30_PORTREG, CORE_PIN30_BIT, CORE_PIN30_CONFIG); +MAKE_PIN(P31, CORE_PIN31_PORTREG, CORE_PIN31_BIT, CORE_PIN31_CONFIG); +MAKE_PIN(P32, CORE_PIN32_PORTREG, CORE_PIN32_BIT, CORE_PIN32_CONFIG); +MAKE_PIN(P33, CORE_PIN33_PORTREG, CORE_PIN33_BIT, CORE_PIN33_CONFIG); + +#undef MAKE_PIN + +#elif defined(ARDUINO_SAM_DUE) && defined(__SAM3X8E__) + +// SetDirRead: +// Disable interrupts +// Disable the pull up resistor +// Set to INPUT +// Enable PIO + +// SetDirWrite: +// Disable interrupts +// Disable the pull up resistor +// Set to OUTPUT +// Enable PIO + +#define MAKE_PIN(className, pio, pinMask) \ +class className { \ +public: \ + static void Set() { \ + pio->PIO_SODR = pinMask; \ + } \ + static void Clear() { \ + pio->PIO_CODR = pinMask; \ + } \ + static void SetDirRead() { \ + pio->PIO_IDR = pinMask ; \ + pio->PIO_PUDR = pinMask; \ + pio->PIO_ODR = pinMask; \ + pio->PIO_PER = pinMask; \ + } \ + static void SetDirWrite() { \ + pio->PIO_IDR = pinMask ; \ + pio->PIO_PUDR = pinMask; \ + pio->PIO_OER = pinMask; \ + pio->PIO_PER = pinMask; \ + } \ + static uint8_t IsSet() { \ + return pio->PIO_PDSR & pinMask; \ + } \ +}; + +// See: http://arduino.cc/en/Hacking/PinMappingSAM3X and variant.cpp + +MAKE_PIN(P0, PIOA, PIO_PA8); +MAKE_PIN(P1, PIOA, PIO_PA9); +MAKE_PIN(P2, PIOB, PIO_PB25); +MAKE_PIN(P3, PIOC, PIO_PC28); +MAKE_PIN(P4, PIOC, PIO_PC26); +MAKE_PIN(P5, PIOC, PIO_PC25); +MAKE_PIN(P6, PIOC, PIO_PC24); +MAKE_PIN(P7, PIOC, PIO_PC23); +MAKE_PIN(P8, PIOC, PIO_PC22); +MAKE_PIN(P9, PIOC, PIO_PC21); +MAKE_PIN(P10, PIOC, PIO_PC29); +MAKE_PIN(P11, PIOD, PIO_PD7); +MAKE_PIN(P12, PIOD, PIO_PD8); +MAKE_PIN(P13, PIOB, PIO_PB27); +MAKE_PIN(P14, PIOD, PIO_PD4); +MAKE_PIN(P15, PIOD, PIO_PD5); +MAKE_PIN(P16, PIOA, PIO_PA13); +MAKE_PIN(P17, PIOA, PIO_PA12); +MAKE_PIN(P18, PIOA, PIO_PA11); +MAKE_PIN(P19, PIOA, PIO_PA10); +MAKE_PIN(P20, PIOB, PIO_PB12); +MAKE_PIN(P21, PIOB, PIO_PB13); +MAKE_PIN(P22, PIOB, PIO_PB26); +MAKE_PIN(P23, PIOA, PIO_PA14); +MAKE_PIN(P24, PIOA, PIO_PA15); +MAKE_PIN(P25, PIOD, PIO_PD0); +MAKE_PIN(P26, PIOD, PIO_PD1); +MAKE_PIN(P27, PIOD, PIO_PD2); +MAKE_PIN(P28, PIOD, PIO_PD3); +MAKE_PIN(P29, PIOD, PIO_PD6); +MAKE_PIN(P30, PIOD, PIO_PD9); +MAKE_PIN(P31, PIOA, PIO_PA7); +MAKE_PIN(P32, PIOD, PIO_PD10); +MAKE_PIN(P33, PIOC, PIO_PC1); +MAKE_PIN(P34, PIOC, PIO_PC2); +MAKE_PIN(P35, PIOC, PIO_PC3); +MAKE_PIN(P36, PIOC, PIO_PC4); +MAKE_PIN(P37, PIOC, PIO_PC5); +MAKE_PIN(P38, PIOC, PIO_PC6); +MAKE_PIN(P39, PIOC, PIO_PC7); +MAKE_PIN(P40, PIOC, PIO_PC8); +MAKE_PIN(P41, PIOC, PIO_PC9); +MAKE_PIN(P42, PIOA, PIO_PA19); +MAKE_PIN(P43, PIOA, PIO_PA20); +MAKE_PIN(P44, PIOC, PIO_PC19); +MAKE_PIN(P45, PIOC, PIO_PC18); +MAKE_PIN(P46, PIOC, PIO_PC17); +MAKE_PIN(P47, PIOC, PIO_PC16); +MAKE_PIN(P48, PIOC, PIO_PC15); +MAKE_PIN(P49, PIOC, PIO_PC14); +MAKE_PIN(P50, PIOC, PIO_PC13); +MAKE_PIN(P51, PIOC, PIO_PC12); +MAKE_PIN(P52, PIOB, PIO_PB21); +MAKE_PIN(P53, PIOB, PIO_PB14); +MAKE_PIN(P54, PIOA, PIO_PA16); +MAKE_PIN(P55, PIOA, PIO_PA24); +MAKE_PIN(P56, PIOA, PIO_PA23); +MAKE_PIN(P57, PIOA, PIO_PA22); +MAKE_PIN(P58, PIOA, PIO_PA6); +MAKE_PIN(P59, PIOA, PIO_PA4); +MAKE_PIN(P60, PIOA, PIO_PA3); +MAKE_PIN(P61, PIOA, PIO_PA2); +MAKE_PIN(P62, PIOB, PIO_PB17); +MAKE_PIN(P63, PIOB, PIO_PB18); +MAKE_PIN(P64, PIOB, PIO_PB19); +MAKE_PIN(P65, PIOB, PIO_PB20); +MAKE_PIN(P66, PIOB, PIO_PB15); +MAKE_PIN(P67, PIOB, PIO_PB16); +MAKE_PIN(P68, PIOA, PIO_PA1); +MAKE_PIN(P69, PIOA, PIO_PA0); +MAKE_PIN(P70, PIOA, PIO_PA17); +MAKE_PIN(P71, PIOA, PIO_PA18); +MAKE_PIN(P72, PIOC, PIO_PC30); +MAKE_PIN(P73, PIOA, PIO_PA21); +MAKE_PIN(P74, PIOA, PIO_PA25); // MISO +MAKE_PIN(P75, PIOA, PIO_PA26); // MOSI +MAKE_PIN(P76, PIOA, PIO_PA27); // CLK +MAKE_PIN(P77, PIOA, PIO_PA28); +MAKE_PIN(P78, PIOB, PIO_PB23); // Unconnected + +#undef MAKE_PIN + +#elif defined(RBL_NRF51822) + +#define MAKE_PIN(className, pin) \ +class className { \ +public: \ + static void Set() { \ + nrf_gpio_pin_set(pin); \ + } \ + static void Clear() { \ + nrf_gpio_pin_clear(pin); \ + } \ + static void SetDirRead() { \ + nrf_gpio_cfg_input(pin, NRF_GPIO_PIN_NOPULL); \ + } \ + static void SetDirWrite() { \ + nrf_gpio_cfg_output(pin); \ + } \ + static uint8_t IsSet() { \ + return (uint8_t)nrf_gpio_pin_read(pin); \ + } \ +}; + +// See: pin_transform.c in RBL nRF51822 SDK +MAKE_PIN(P0, Pin_nRF51822_to_Arduino(D0)); +MAKE_PIN(P1, Pin_nRF51822_to_Arduino(D1)); +MAKE_PIN(P2, Pin_nRF51822_to_Arduino(D2)); +MAKE_PIN(P3, Pin_nRF51822_to_Arduino(D3)); +MAKE_PIN(P4, Pin_nRF51822_to_Arduino(D4)); +MAKE_PIN(P5, Pin_nRF51822_to_Arduino(D5)); +MAKE_PIN(P6, Pin_nRF51822_to_Arduino(D6)); +MAKE_PIN(P7, Pin_nRF51822_to_Arduino(D7)); +MAKE_PIN(P8, Pin_nRF51822_to_Arduino(D8)); +MAKE_PIN(P9, Pin_nRF51822_to_Arduino(D9)); // INT +MAKE_PIN(P10, Pin_nRF51822_to_Arduino(D10)); // SS +MAKE_PIN(P11, Pin_nRF51822_to_Arduino(D11)); +MAKE_PIN(P12, Pin_nRF51822_to_Arduino(D12)); +MAKE_PIN(P13, Pin_nRF51822_to_Arduino(D13)); +MAKE_PIN(P14, Pin_nRF51822_to_Arduino(D14)); +MAKE_PIN(P15, Pin_nRF51822_to_Arduino(D15)); +MAKE_PIN(P17, Pin_nRF51822_to_Arduino(D17)); // MISO +MAKE_PIN(P18, Pin_nRF51822_to_Arduino(D18)); // MOSI +MAKE_PIN(P16, Pin_nRF51822_to_Arduino(D16)); // CLK +MAKE_PIN(P19, Pin_nRF51822_to_Arduino(D19)); +MAKE_PIN(P20, Pin_nRF51822_to_Arduino(D20)); +MAKE_PIN(P21, Pin_nRF51822_to_Arduino(D21)); +MAKE_PIN(P22, Pin_nRF51822_to_Arduino(D22)); +MAKE_PIN(P23, Pin_nRF51822_to_Arduino(D23)); +MAKE_PIN(P24, Pin_nRF51822_to_Arduino(D24)); + +#undef MAKE_PIN + +#else +#error "Please define board in avrpins.h" + +#endif + +#endif // __arm__ + +#if defined(__MIPSEL__) +// MIPSEL (MIPS architecture using a little endian byte order) + +// MIPS size_t = 4 +#define pgm_read_pointer(p) pgm_read_dword(p) + +#define MAKE_PIN(className, pin) \ +class className { \ +public: \ + static void Set() { \ + digitalWrite(pin, HIGH);\ + } \ + static void Clear() { \ + digitalWrite(pin, LOW); \ + } \ + static void SetDirRead() { \ + pinMode(pin, INPUT); \ + } \ + static void SetDirWrite() { \ + pinMode(pin, OUTPUT); \ + } \ + static uint8_t IsSet() { \ + return digitalRead(pin); \ + } \ +}; + +// 0 .. 13 - Digital pins +MAKE_PIN(P0, 0); // RX +MAKE_PIN(P1, 1); // TX +MAKE_PIN(P2, 2); // +MAKE_PIN(P3, 3); // +MAKE_PIN(P4, 4); // +MAKE_PIN(P5, 5); // +MAKE_PIN(P6, 6); // +MAKE_PIN(P7, 7); // +MAKE_PIN(P8, 8); // +MAKE_PIN(P9, 9); // +MAKE_PIN(P10, 10); // +MAKE_PIN(P11, 11); // +MAKE_PIN(P12, 12); // +MAKE_PIN(P13, 13); // + +#undef MAKE_PIN +#endif + +#endif //_avrpins_h_ diff --git a/libraries/USB_Host_Shield/cdc_XR21B1411.cpp b/libraries/USB_Host_Shield/cdc_XR21B1411.cpp new file mode 100755 index 0000000..74df8c3 --- /dev/null +++ b/libraries/USB_Host_Shield/cdc_XR21B1411.cpp @@ -0,0 +1,211 @@ +/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved. + +This software may be distributed and modified under the terms of the GNU +General Public License version 2 (GPL2) as published by the Free Software +Foundation and appearing in the file GPL2.TXT included in the packaging of +this file. Please note that GPL2 Section 2[b] requires that all works based +on this software must also be made publicly available under the terms of +the GPL2 ("Copyleft"). + +Contact information +------------------- + +Circuits At Home, LTD +Web : http://www.circuitsathome.com +e-mail : support@circuitsathome.com + */ +#include "cdc_XR21B1411.h" + +XR21B1411::XR21B1411(USB *p, CDCAsyncOper *pasync) : +ACM(p, pasync) { + // Is this needed?? + _enhanced_status = enhanced_features(); // Set up features +} + +uint8_t XR21B1411::Init(uint8_t parent, uint8_t port, bool lowspeed) { + const uint8_t constBufSize = sizeof (USB_DEVICE_DESCRIPTOR); + + uint8_t buf[constBufSize]; + USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast(buf); + + uint8_t rcode; + UsbDevice *p = NULL; + EpInfo *oldep_ptr = NULL; + uint8_t num_of_conf; // number of configurations + + AddressPool &addrPool = pUsb->GetAddressPool(); + + USBTRACE("XR Init\r\n"); + + if(bAddress) + return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE; + + // Get pointer to pseudo device with address 0 assigned + p = addrPool.GetUsbDevicePtr(0); + + if(!p) + return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; + + if(!p->epinfo) { + USBTRACE("epinfo\r\n"); + return USB_ERROR_EPINFO_IS_NULL; + } + + // Save old pointer to EP_RECORD of address 0 + oldep_ptr = p->epinfo; + + // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence + p->epinfo = epInfo; + + p->lowspeed = lowspeed; + + // Get device descriptor + rcode = pUsb->getDevDescr(0, 0, constBufSize, (uint8_t*)buf); + + // Restore p->epinfo + p->epinfo = oldep_ptr; + + if(rcode) + goto FailGetDevDescr; + + // Allocate new address according to device class + bAddress = addrPool.AllocAddress(parent, false, port); + + if(!bAddress) + return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL; + + // Extract Max Packet Size from the device descriptor + epInfo[0].maxPktSize = udd->bMaxPacketSize0; + + // Assign new address to the device + rcode = pUsb->setAddr(0, 0, bAddress); + + if(rcode) { + p->lowspeed = false; + addrPool.FreeAddress(bAddress); + bAddress = 0; + USBTRACE2("setAddr:", rcode); + return rcode; + } + + USBTRACE2("Addr:", bAddress); + + p->lowspeed = false; + + p = addrPool.GetUsbDevicePtr(bAddress); + + if(!p) + return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; + + p->lowspeed = lowspeed; + + num_of_conf = udd->bNumConfigurations; + + if((((udd->idVendor != 0x2890U) || (udd->idProduct != 0x0201U)) && ((udd->idVendor != 0x04e2U) || (udd->idProduct != 0x1411U)))) + return USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED; + + // Assign epInfo to epinfo pointer + rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo); + + if(rcode) + goto FailSetDevTblEntry; + + USBTRACE2("NC:", num_of_conf); + + for(uint8_t i = 0; i < num_of_conf; i++) { + ConfigDescParser< USB_CLASS_COM_AND_CDC_CTRL, + CDC_SUBCLASS_ACM, + CDC_PROTOCOL_ITU_T_V_250, + CP_MASK_COMPARE_CLASS | + CP_MASK_COMPARE_SUBCLASS | + CP_MASK_COMPARE_PROTOCOL > CdcControlParser(this); + + ConfigDescParser CdcDataParser(this); + + rcode = pUsb->getConfDescr(bAddress, 0, i, &CdcControlParser); + + if(rcode) + goto FailGetConfDescr; + + rcode = pUsb->getConfDescr(bAddress, 0, i, &CdcDataParser); + + if(rcode) + goto FailGetConfDescr; + + if(bNumEP > 1) + break; + } // for + + if(bNumEP < 4) + return USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED; + + // Assign epInfo to epinfo pointer + rcode = pUsb->setEpInfoEntry(bAddress, bNumEP, epInfo); + + USBTRACE2("Conf:", bConfNum); + + // Set Configuration Value + rcode = pUsb->setConf(bAddress, 0, bConfNum); + + if(rcode) + goto FailSetConfDescr; + + // Set up features status + _enhanced_status = enhanced_features(); + half_duplex(false); + autoflowRTS(false); + autoflowDSR(false); + autoflowXON(false); + wide(false); // Always false, because this is only available in custom mode. + + rcode = pAsync->OnInit(this); + + if(rcode) + goto FailOnInit; + + USBTRACE("XR configured\r\n"); + + ready = true; + + //bPollEnable = true; + + //USBTRACE("Poll enabled\r\n"); + return 0; + +FailGetDevDescr: +#ifdef DEBUG_USB_HOST + NotifyFailGetDevDescr(); + goto Fail; +#endif + +FailSetDevTblEntry: +#ifdef DEBUG_USB_HOST + NotifyFailSetDevTblEntry(); + goto Fail; +#endif + +FailGetConfDescr: +#ifdef DEBUG_USB_HOST + NotifyFailGetConfDescr(); + goto Fail; +#endif + +FailSetConfDescr: +#ifdef DEBUG_USB_HOST + NotifyFailSetConfDescr(); + goto Fail; +#endif + +FailOnInit: +#ifdef DEBUG_USB_HOST + USBTRACE("OnInit:"); +#endif + +#ifdef DEBUG_USB_HOST +Fail: + NotifyFail(rcode); +#endif + Release(); + return rcode; +} diff --git a/libraries/USB_Host_Shield/cdc_XR21B1411.h b/libraries/USB_Host_Shield/cdc_XR21B1411.h new file mode 100755 index 0000000..347f0df --- /dev/null +++ b/libraries/USB_Host_Shield/cdc_XR21B1411.h @@ -0,0 +1,272 @@ +/* Copyright (C) 2015 Andrew J. Kroll + and + Circuits At Home, LTD. All rights reserved. + +This software may be distributed and modified under the terms of the GNU +General Public License version 2 (GPL2) as published by the Free Software +Foundation and appearing in the file GPL2.TXT included in the packaging of +this file. Please note that GPL2 Section 2[b] requires that all works based +on this software must also be made publicly available under the terms of +the GPL2 ("Copyleft"). + +Contact information +------------------- + +Circuits At Home, LTD +Web : http://www.circuitsathome.com +e-mail : support@circuitsathome.com + */ +#if !defined(__CDC_XR21B1411_H__) +#define __CDC_XR21B1411_H__ + +#include "cdcacm.h" + +#define XR_REG_CUSTOM_DRIVER (0x020DU) // DRIVER SELECT +#define XR_REG_CUSTOM_DRIVER_ACTIVE (0x0001U) // 0: CDC 1: CUSTOM + +#define XR_REG_ACM_FLOW_CTL (0x0216U) // FLOW CONTROL REGISTER CDCACM MODE +#define XR_REG_FLOW_CTL (0x0C06U) // FLOW CONTROL REGISTER CUSTOM MODE +#define XR_REG_FLOW_CTL_HALF_DPLX (0x0008U) // 0:FULL DUPLEX 1:HALF DUPLEX +#define XR_REG_FLOW_CTL_MODE_MASK (0x0007U) // MODE BITMASK +#define XR_REG_FLOW_CTL_NONE (0x0000U) // NO FLOW CONTROL +#define XR_REG_FLOW_CTL_HW (0x0001U) // HARDWARE FLOW CONTROL +#define XR_REG_FLOW_CTL_SW (0x0002U) // SOFTWARE FLOW CONTROL +#define XR_REG_FLOW_CTL_MMMRX (0x0003U) // MULTIDROP RX UPON ADDRESS MATCH +#define XR_REG_FLOW_CTL_MMMRXTX (0x0004U) // MULTIDROP RX/TX UPON ADDRESS MATCH + +#define XR_REG_ACM_GPIO_MODE (0x0217U) // GPIO MODE REGISTER IN CDCACM MODE +#define XR_REG_GPIO_MODE (0x0C0CU) // GPIO MODE REGISTER IN CUSTOM MODE +#define XR_REG_GPIO_MODE_GPIO (0x0000U) // ALL GPIO PINS ACM PROGRAMMABLE +#define XR_REG_GPIO_MODE_FC_RTSCTS (0x0001U) // AUTO RTSCTS HW FC (GPIO 4/5) +#define XR_REG_GPIO_MODE_FC_DTRDSR (0x0002U) // AUTO DTRDSR HW FC (GPIO 2/3) +#define XR_REG_GPIO_MODE_ATE (0x0003U) // AUTO TRANSCEIVER ENABLE DURING TX (GPIO 5) +#define XR_REG_GPIO_MODE_ATE_ADDRESS (0x0004U) // AUTO TRANSCEIVER ENABLE ON ADDRESS MATCH (GPIO 5) + +#define XR_REG_ACM_GPIO_DIR (0x0218U) // GPIO DIRECTION REGISTER CDCACM MODE, 0:IN 1:OUT +#define XR_REG_GPIO_DIR (0x0C0DU) // GPIO DIRECTION REGISTER CUSTOM MODE, 0:IN 1:OUT + +#define XR_REG_ACM_GPIO_INT (0x0219U) // GPIO PIN CHANGE INTERRUPT ENABLE CDCACM MODE, 0: ENABLED 1: DISABLED +#define XR_REG_GPIO_INT (0x0C11U) // GPIO PIN CHANGE INTERRUPT ENABLE CUSTOM MODE, 0: ENABLED 1: DISABLED +#define XR_REG_GPIO_MASK (0x001FU) // GPIO REGISTERS BITMASK + +#define XR_REG_UART_ENABLE (0x0C00U) // UART I/O ENABLE REGISTER +#define XR_REG_UART_ENABLE_RX (0x0002U) // 0:DISABLED 1:ENABLED +#define XR_REG_UART_ENABLE_TX (0x0001U) // 0:DISABLED 1:ENABLED + +#define XR_REG_ERROR_STATUS (0x0C09U) // ERROR STATUS REGISTER +#define XR_REG_ERROR_STATUS_MASK (0x00F8U) // ERROR STATUS BITMASK +#define XR_REG_ERROR_STATUS_ERROR (0x0070U) // ERROR STATUS ERROR BITMASK +#define XR_REG_ERROR_STATUS_BREAK (0x0008U) // BREAK HAS BEEN DETECTED +#define XR_REG_ERROR_STATUS_OVERRUN (0x0010U) // RX OVERRUN ERROR +#define XR_REG_ERROR_STATUS_PARITY (0x0020U) // PARITY ERROR +#define XR_REG_ERROR_STATUS_FRAME (0x0040U) // FRAMING ERROR +#define XR_REG_ERROR_STATUS_BREAK (0x0080U) // BREAK IS BEING DETECTED + +#define XR_REG_TX_BREAK (0x0C0AU) // TRANSMIT BREAK. 0X0001-0XFFE TIME IN MS, 0X0000 STOP, 0X0FFF BREAK ON + +#define XR_REG_XCVR_EN_DELAY (0x0C0BU) // TURN-ARROUND DELAY IN BIT-TIMES 0X0000-0X000F + +#define XR_REG_GPIO_SET (0x0C0EU) // 1:SET GPIO PIN + +#define XR_REG_GPIO_CLR (0x0C0FU) // 1:CLEAR GPIO PIN + +#define XR_REG_GPIO_STATUS (0x0C10U) // READ GPIO PINS + +#define XR_REG_CUSTOMISED_INT (0x0C12U) // 0:STANDARD 1:CUSTOM SEE DATA SHEET + +#define XR_REG_PIN_PULLUP_ENABLE (0x0C14U) // 0:DISABLE 1:ENABLE, BITS 0-5:GPIO, 6:RX 7:TX + +#define XR_REG_PIN_PULLDOWN_ENABLE (0x0C15U) // 0:DISABLE 1:ENABLE, BITS 0-5:GPIO, 6:RX 7:TX + +#define XR_REG_LOOPBACK (0x0C16U) // 0:DISABLE 1:ENABLE, SEE DATA SHEET + +#define XR_REG_RX_FIFO_LATENCY (0x0CC2U) // FIFO LATENCY REGISTER +#define XR_REG_RX_FIFO_LATENCY_ENABLE (0x0001U) // + +#define XR_REG_WIDE_MODE (0x0D02U) +#define XR_REG_WIDE_MODE_ENABLE (0x0001U) + +#define XR_REG_XON_CHAR (0x0C07U) +#define XR_REG_XOFF_CHAR (0x0C08U) + +#define XR_REG_TX_FIFO_RESET (0x0C80U) // 1: RESET, SELF-CLEARING +#define XR_REG_TX_FIFO_COUNT (0x0C81U) // READ-ONLY +#define XR_REG_RX_FIFO_RESET (0x0CC0U) // 1: RESET, SELF-CLEARING +#define XR_REG_RX_FIFO_COUNT (0x0CC1U) // READ-ONLY + +#define XR_WRITE_REQUEST_TYPE (0x40U) + +#define XR_READ_REQUEST_TYPE (0xC0U) + +#define XR_MAX_ENDPOINTS 4 + +class XR21B1411 : public ACM { +protected: + +public: + XR21B1411(USB *pusb, CDCAsyncOper *pasync); + + /** + * Used by the USB core to check what this driver support. + * @param vid The device's VID. + * @param pid The device's PID. + * @return Returns true if the device's VID and PID matches this driver. + */ + virtual bool VIDPIDOK(uint16_t vid, uint16_t pid) { + return (((vid == 0x2890U) && (pid == 0x0201U)) || ((vid == 0x04e2U) && (pid == 0x1411U))); + }; + + uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed); + + virtual tty_features enhanced_features(void) { + tty_features rv; + rv.enhanced = true; + rv.autoflow_RTS = true; + rv.autoflow_DSR = true; + rv.autoflow_XON = true; + rv.half_duplex = true; + rv.wide = true; + return rv; + }; + + uint8_t read_register(uint16_t reg, uint16_t *val) { + return (pUsb->ctrlReq(bAddress, 0, XR_READ_REQUEST_TYPE, 1, 0, 0, reg, 2, 2, (uint8_t *)val, NULL)); + } + + uint8_t write_register(uint16_t reg, uint16_t val) { + return (pUsb->ctrlReq(bAddress, 0, XR_WRITE_REQUEST_TYPE, 0, BGRAB0(val), BGRAB1(val), reg, 0, 0, NULL, NULL)); + } + + + //////////////////////////////////////////////////////////////////////// + // The following methods set the CDC-ACM defaults. + //////////////////////////////////////////////////////////////////////// + + virtual void autoflowRTS(bool s) { + uint16_t val; + uint8_t rval; + rval = read_register(XR_REG_ACM_FLOW_CTL, &val); + if(!rval) { + if(s) { + val &= XR_REG_FLOW_CTL_HALF_DPLX; + val |= XR_REG_FLOW_CTL_HW; + } else { + val &= XR_REG_FLOW_CTL_HALF_DPLX; + } + rval = write_register(XR_REG_ACM_FLOW_CTL, val); + if(!rval) { + rval = write_register(XR_REG_ACM_GPIO_MODE, XR_REG_GPIO_MODE_GPIO); + if(!rval) { + // ACM commands apply the new settings. + LINE_CODING LCT; + rval = GetLineCoding(&LCT); + if(!rval) { + rval = SetLineCoding(&LCT); + if(!rval) { + _enhanced_status.autoflow_XON = false; + _enhanced_status.autoflow_DSR = false; + _enhanced_status.autoflow_RTS = s; + } + } + } + } + } + }; + + virtual void autoflowDSR(bool s) { + uint16_t val; + uint8_t rval; + rval = read_register(XR_REG_ACM_FLOW_CTL, &val); + if(!rval) { + if(s) { + val &= XR_REG_FLOW_CTL_HALF_DPLX; + val |= XR_REG_FLOW_CTL_HW; + } else { + val &= XR_REG_FLOW_CTL_HALF_DPLX; + } + rval = write_register(XR_REG_ACM_FLOW_CTL, val); + if(!rval) { + if(s) { + rval = write_register(XR_REG_ACM_GPIO_MODE, XR_REG_GPIO_MODE_FC_DTRDSR); + } else { + rval = write_register(XR_REG_ACM_GPIO_MODE, XR_REG_GPIO_MODE_GPIO); + } + if(!rval) { + // ACM commands apply the new settings. + LINE_CODING LCT; + rval = GetLineCoding(&LCT); + if(!rval) { + rval = SetLineCoding(&LCT); + if(!rval) { + _enhanced_status.autoflow_XON = false; + _enhanced_status.autoflow_RTS = false; + _enhanced_status.autoflow_DSR = s; + } + } + } + } + } + }; + + virtual void autoflowXON(bool s) { + // NOTE: hardware defaults to the normal XON/XOFF + uint16_t val; + uint8_t rval; + rval = read_register(XR_REG_ACM_FLOW_CTL, &val); + if(!rval) { + if(s) { + val &= XR_REG_FLOW_CTL_HALF_DPLX; + val |= XR_REG_FLOW_CTL_SW; + } else { + val &= XR_REG_FLOW_CTL_HALF_DPLX; + } + rval = write_register(XR_REG_ACM_FLOW_CTL, val); + if(!rval) { + rval = write_register(XR_REG_ACM_GPIO_MODE, XR_REG_GPIO_MODE_GPIO); + if(!rval) { + // ACM commands apply the new settings. + LINE_CODING LCT; + rval = GetLineCoding(&LCT); + if(!rval) { + rval = SetLineCoding(&LCT); + if(!rval) { + _enhanced_status.autoflow_RTS = false; + _enhanced_status.autoflow_DSR = false; + _enhanced_status.autoflow_XON = s; + } + } + } + } + } + }; + + virtual void half_duplex(bool s) { + uint16_t val; + uint8_t rval; + rval = read_register(XR_REG_ACM_FLOW_CTL, &val); + if(!rval) { + if(s) { + val |= XR_REG_FLOW_CTL_HALF_DPLX; + } else { + val &= XR_REG_FLOW_CTL_MODE_MASK; + } + rval = write_register(XR_REG_ACM_FLOW_CTL, val); + if(!rval) { + // ACM commands apply the new settings. + LINE_CODING LCT; + rval = GetLineCoding(&LCT); + if(!rval) { + rval = SetLineCoding(&LCT); + if(!rval) { + _enhanced_status.half_duplex = s; + } + } + } + } + }; + + + +}; + +#endif // __CDCPROLIFIC_H__ diff --git a/libraries/USB_Host_Shield/cdcacm.cpp b/libraries/USB_Host_Shield/cdcacm.cpp new file mode 100755 index 0000000..2cd2c9a --- /dev/null +++ b/libraries/USB_Host_Shield/cdcacm.cpp @@ -0,0 +1,331 @@ +/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved. + +This software may be distributed and modified under the terms of the GNU +General Public License version 2 (GPL2) as published by the Free Software +Foundation and appearing in the file GPL2.TXT included in the packaging of +this file. Please note that GPL2 Section 2[b] requires that all works based +on this software must also be made publicly available under the terms of +the GPL2 ("Copyleft"). + +Contact information +------------------- + +Circuits At Home, LTD +Web : http://www.circuitsathome.com +e-mail : support@circuitsathome.com + */ +#include "cdcacm.h" + +const uint8_t ACM::epDataInIndex = 1; +const uint8_t ACM::epDataOutIndex = 2; +const uint8_t ACM::epInterruptInIndex = 3; + +ACM::ACM(USB *p, CDCAsyncOper *pasync) : +pUsb(p), +pAsync(pasync), +bAddress(0), +bControlIface(0), +bDataIface(0), +bNumEP(1), +qNextPollTime(0), +bPollEnable(false), +ready(false) { + _enhanced_status = enhanced_features(); // Set up features + for(uint8_t i = 0; i < ACM_MAX_ENDPOINTS; i++) { + epInfo[i].epAddr = 0; + epInfo[i].maxPktSize = (i) ? 0 : 8; + epInfo[i].epAttribs = 0; + epInfo[i].bmNakPower = (i == epDataInIndex) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER; + + } + if(pUsb) + pUsb->RegisterDeviceClass(this); +} + +uint8_t ACM::Init(uint8_t parent, uint8_t port, bool lowspeed) { + + const uint8_t constBufSize = sizeof (USB_DEVICE_DESCRIPTOR); + + uint8_t buf[constBufSize]; + USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast(buf); + + uint8_t rcode; + UsbDevice *p = NULL; + EpInfo *oldep_ptr = NULL; + uint8_t num_of_conf; // number of configurations + + AddressPool &addrPool = pUsb->GetAddressPool(); + + USBTRACE("ACM Init\r\n"); + + if(bAddress) + return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE; + + // Get pointer to pseudo device with address 0 assigned + p = addrPool.GetUsbDevicePtr(0); + + if(!p) + return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; + + if(!p->epinfo) { + USBTRACE("epinfo\r\n"); + return USB_ERROR_EPINFO_IS_NULL; + } + + // Save old pointer to EP_RECORD of address 0 + oldep_ptr = p->epinfo; + + // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence + p->epinfo = epInfo; + + p->lowspeed = lowspeed; + + // Get device descriptor + rcode = pUsb->getDevDescr(0, 0, constBufSize, (uint8_t*)buf); + + // Restore p->epinfo + p->epinfo = oldep_ptr; + + if(rcode) + goto FailGetDevDescr; + + // Allocate new address according to device class + bAddress = addrPool.AllocAddress(parent, false, port); + + if(!bAddress) + return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL; + + // Extract Max Packet Size from the device descriptor + epInfo[0].maxPktSize = udd->bMaxPacketSize0; + + // Assign new address to the device + rcode = pUsb->setAddr(0, 0, bAddress); + + if(rcode) { + p->lowspeed = false; + addrPool.FreeAddress(bAddress); + bAddress = 0; + USBTRACE2("setAddr:", rcode); + return rcode; + } + + USBTRACE2("Addr:", bAddress); + + p->lowspeed = false; + + p = addrPool.GetUsbDevicePtr(bAddress); + + if(!p) + return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; + + p->lowspeed = lowspeed; + + num_of_conf = udd->bNumConfigurations; + + // Assign epInfo to epinfo pointer + rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo); + + if(rcode) + goto FailSetDevTblEntry; + + USBTRACE2("NC:", num_of_conf); + + for(uint8_t i = 0; i < num_of_conf; i++) { + ConfigDescParser< USB_CLASS_COM_AND_CDC_CTRL, + CDC_SUBCLASS_ACM, + CDC_PROTOCOL_ITU_T_V_250, + CP_MASK_COMPARE_CLASS | + CP_MASK_COMPARE_SUBCLASS | + CP_MASK_COMPARE_PROTOCOL > CdcControlParser(this); + + ConfigDescParser CdcDataParser(this); + + rcode = pUsb->getConfDescr(bAddress, 0, i, &CdcControlParser); + + if(rcode) + goto FailGetConfDescr; + + rcode = pUsb->getConfDescr(bAddress, 0, i, &CdcDataParser); + + if(rcode) + goto FailGetConfDescr; + + if(bNumEP > 1) + break; + } // for + + if(bNumEP < 4) + return USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED; + + // Assign epInfo to epinfo pointer + rcode = pUsb->setEpInfoEntry(bAddress, bNumEP, epInfo); + + USBTRACE2("Conf:", bConfNum); + + // Set Configuration Value + rcode = pUsb->setConf(bAddress, 0, bConfNum); + + if(rcode) + goto FailSetConfDescr; + + // Set up features status + _enhanced_status = enhanced_features(); + half_duplex(false); + autoflowRTS(false); + autoflowDSR(false); + autoflowXON(false); + wide(false); // Always false, because this is only available in custom mode. + rcode = pAsync->OnInit(this); + + if(rcode) + goto FailOnInit; + + USBTRACE("ACM configured\r\n"); + + ready = true; + + //bPollEnable = true; + + //USBTRACE("Poll enabled\r\n"); + return 0; + +FailGetDevDescr: +#ifdef DEBUG_USB_HOST + NotifyFailGetDevDescr(); + goto Fail; +#endif + +FailSetDevTblEntry: +#ifdef DEBUG_USB_HOST + NotifyFailSetDevTblEntry(); + goto Fail; +#endif + +FailGetConfDescr: +#ifdef DEBUG_USB_HOST + NotifyFailGetConfDescr(); + goto Fail; +#endif + +FailSetConfDescr: +#ifdef DEBUG_USB_HOST + NotifyFailSetConfDescr(); + goto Fail; +#endif + +FailOnInit: +#ifdef DEBUG_USB_HOST + USBTRACE("OnInit:"); +#endif + +#ifdef DEBUG_USB_HOST +Fail: + NotifyFail(rcode); +#endif + Release(); + return rcode; +} + +void ACM::EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *pep) { + //ErrorMessage (PSTR("Conf.Val"), conf); + //ErrorMessage (PSTR("Iface Num"), iface); + //ErrorMessage (PSTR("Alt.Set"), alt); + + bConfNum = conf; + + uint8_t index; + + if((pep->bmAttributes & 0x03) == 3 && (pep->bEndpointAddress & 0x80) == 0x80) + index = epInterruptInIndex; + else + if((pep->bmAttributes & 0x02) == 2) + index = ((pep->bEndpointAddress & 0x80) == 0x80) ? epDataInIndex : epDataOutIndex; + else + return; + + // Fill in the endpoint info structure + epInfo[index].epAddr = (pep->bEndpointAddress & 0x0F); + epInfo[index].maxPktSize = (uint8_t)pep->wMaxPacketSize; + epInfo[index].epAttribs = 0; + + bNumEP++; + + PrintEndpointDescriptor(pep); +} + +uint8_t ACM::Release() { + ready = false; + pUsb->GetAddressPool().FreeAddress(bAddress); + + bControlIface = 0; + bDataIface = 0; + bNumEP = 1; + + bAddress = 0; + qNextPollTime = 0; + bPollEnable = false; + return 0; +} + +uint8_t ACM::Poll() { + uint8_t rcode = 0; + + if(!bPollEnable) + return 0; + + return rcode; +} + +uint8_t ACM::RcvData(uint16_t *bytes_rcvd, uint8_t *dataptr) { + return pUsb->inTransfer(bAddress, epInfo[epDataInIndex].epAddr, bytes_rcvd, dataptr); +} + +uint8_t ACM::SndData(uint16_t nbytes, uint8_t *dataptr) { + return pUsb->outTransfer(bAddress, epInfo[epDataOutIndex].epAddr, nbytes, dataptr); +} + +uint8_t ACM::SetCommFeature(uint16_t fid, uint8_t nbytes, uint8_t *dataptr) { + return ( pUsb->ctrlReq(bAddress, 0, bmREQ_CDCOUT, CDC_SET_COMM_FEATURE, (fid & 0xff), (fid >> 8), bControlIface, nbytes, nbytes, dataptr, NULL)); +} + +uint8_t ACM::GetCommFeature(uint16_t fid, uint8_t nbytes, uint8_t *dataptr) { + return ( pUsb->ctrlReq(bAddress, 0, bmREQ_CDCIN, CDC_GET_COMM_FEATURE, (fid & 0xff), (fid >> 8), bControlIface, nbytes, nbytes, dataptr, NULL)); +} + +uint8_t ACM::ClearCommFeature(uint16_t fid) { + return ( pUsb->ctrlReq(bAddress, 0, bmREQ_CDCOUT, CDC_CLEAR_COMM_FEATURE, (fid & 0xff), (fid >> 8), bControlIface, 0, 0, NULL, NULL)); +} + +uint8_t ACM::SetLineCoding(const LINE_CODING *dataptr) { + return ( pUsb->ctrlReq(bAddress, 0, bmREQ_CDCOUT, CDC_SET_LINE_CODING, 0x00, 0x00, bControlIface, sizeof (LINE_CODING), sizeof (LINE_CODING), (uint8_t*)dataptr, NULL)); +} + +uint8_t ACM::GetLineCoding(LINE_CODING *dataptr) { + return ( pUsb->ctrlReq(bAddress, 0, bmREQ_CDCIN, CDC_GET_LINE_CODING, 0x00, 0x00, bControlIface, sizeof (LINE_CODING), sizeof (LINE_CODING), (uint8_t*)dataptr, NULL)); +} + +uint8_t ACM::SetControlLineState(uint8_t state) { + return ( pUsb->ctrlReq(bAddress, 0, bmREQ_CDCOUT, CDC_SET_CONTROL_LINE_STATE, state, 0, bControlIface, 0, 0, NULL, NULL)); +} + +uint8_t ACM::SendBreak(uint16_t duration) { + return ( pUsb->ctrlReq(bAddress, 0, bmREQ_CDCOUT, CDC_SEND_BREAK, (duration & 0xff), (duration >> 8), bControlIface, 0, 0, NULL, NULL)); +} + +void ACM::PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr) { + Notify(PSTR("Endpoint descriptor:"), 0x80); + Notify(PSTR("\r\nLength:\t\t"), 0x80); + D_PrintHex (ep_ptr->bLength, 0x80); + Notify(PSTR("\r\nType:\t\t"), 0x80); + D_PrintHex (ep_ptr->bDescriptorType, 0x80); + Notify(PSTR("\r\nAddress:\t"), 0x80); + D_PrintHex (ep_ptr->bEndpointAddress, 0x80); + Notify(PSTR("\r\nAttributes:\t"), 0x80); + D_PrintHex (ep_ptr->bmAttributes, 0x80); + Notify(PSTR("\r\nMaxPktSize:\t"), 0x80); + D_PrintHex (ep_ptr->wMaxPacketSize, 0x80); + Notify(PSTR("\r\nPoll Intrv:\t"), 0x80); + D_PrintHex (ep_ptr->bInterval, 0x80); + Notify(PSTR("\r\n"), 0x80); +} diff --git a/libraries/USB_Host_Shield/cdcacm.h b/libraries/USB_Host_Shield/cdcacm.h new file mode 100755 index 0000000..8372f56 --- /dev/null +++ b/libraries/USB_Host_Shield/cdcacm.h @@ -0,0 +1,252 @@ +/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved. + +This software may be distributed and modified under the terms of the GNU +General Public License version 2 (GPL2) as published by the Free Software +Foundation and appearing in the file GPL2.TXT included in the packaging of +this file. Please note that GPL2 Section 2[b] requires that all works based +on this software must also be made publicly available under the terms of +the GPL2 ("Copyleft"). + +Contact information +------------------- + +Circuits At Home, LTD +Web : http://www.circuitsathome.com +e-mail : support@circuitsathome.com + */ +#if !defined(__CDCACM_H__) +#define __CDCACM_H__ + +#include "Usb.h" + +#define bmREQ_CDCOUT USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_INTERFACE +#define bmREQ_CDCIN USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_INTERFACE + +// CDC Subclass Constants +#define CDC_SUBCLASS_DLCM 0x01 // Direct Line Control Model +#define CDC_SUBCLASS_ACM 0x02 // Abstract Control Model +#define CDC_SUBCLASS_TCM 0x03 // Telephone Control Model +#define CDC_SUBCLASS_MCCM 0x04 // Multi Channel Control Model +#define CDC_SUBCLASS_CAPI 0x05 // CAPI Control Model +#define CDC_SUBCLASS_ETHERNET 0x06 // Ethernet Network Control Model +#define CDC_SUBCLASS_ATM 0x07 // ATM Network Control Model +#define CDC_SUBCLASS_WIRELESS_HANDSET 0x08 // Wireless Handset Control Model +#define CDC_SUBCLASS_DEVICE_MANAGEMENT 0x09 // Device Management +#define CDC_SUBCLASS_MOBILE_DIRECT_LINE 0x0A // Mobile Direct Line Model +#define CDC_SUBCLASS_OBEX 0x0B // OBEX +#define CDC_SUBCLASS_ETHERNET_EMU 0x0C // Ethernet Emulation Model + +// Communication Interface Class Control Protocol Codes +#define CDC_PROTOCOL_ITU_T_V_250 0x01 // AT Commands defined by ITU-T V.250 +#define CDC_PROTOCOL_PCCA_101 0x02 // AT Commands defined by PCCA-101 +#define CDC_PROTOCOL_PCCA_101_O 0x03 // AT Commands defined by PCCA-101 & Annex O +#define CDC_PROTOCOL_GSM_7_07 0x04 // AT Commands defined by GSM 7.07 +#define CDC_PROTOCOL_3GPP_27_07 0x05 // AT Commands defined by 3GPP 27.007 +#define CDC_PROTOCOL_C_S0017_0 0x06 // AT Commands defined by TIA for CDMA +#define CDC_PROTOCOL_USB_EEM 0x07 // Ethernet Emulation Model + +// CDC Commands defined by CDC 1.2 +#define CDC_SEND_ENCAPSULATED_COMMAND 0x00 +#define CDC_GET_ENCAPSULATED_RESPONSE 0x01 + +// CDC Commands defined by PSTN 1.2 +#define CDC_SET_COMM_FEATURE 0x02 +#define CDC_GET_COMM_FEATURE 0x03 +#define CDC_CLEAR_COMM_FEATURE 0x04 +#define CDC_SET_AUX_LINE_STATE 0x10 +#define CDC_SET_HOOK_STATE 0x11 +#define CDC_PULSE_SETUP 0x12 +#define CDC_SEND_PULSE 0x13 +#define CDC_SET_PULSE_TIME 0x14 +#define CDC_RING_AUX_JACK 0x15 +#define CDC_SET_LINE_CODING 0x20 +#define CDC_GET_LINE_CODING 0x21 +#define CDC_SET_CONTROL_LINE_STATE 0x22 +#define CDC_SEND_BREAK 0x23 +#define CDC_SET_RINGER_PARMS 0x30 +#define CDC_GET_RINGER_PARMS 0x31 +#define CDC_SET_OPERATION_PARMS 0x32 +#define CDC_GET_OPERATION_PARMS 0x33 +#define CDC_SET_LINE_PARMS 0x34 +#define CDC_GET_LINE_PARMS 0x35 +#define CDC_DIAL_DIGITS 0x36 + +//Class-Specific Notification Codes +#define NETWORK_CONNECTION 0x00 +#define RESPONSE_AVAILABLE 0x01 +#define AUX_JACK_HOOK_STATE 0x08 +#define RING_DETECT 0x09 +#define SERIAL_STATE 0x20 +#define CALL_STATE_CHANGE 0x28 +#define LINE_STATE_CHANGE 0x29 +#define CONNECTION_SPEED_CHANGE 0x2a + +// CDC Functional Descriptor Structures + +typedef struct { + uint8_t bFunctionLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubtype; + uint8_t bmCapabilities; + uint8_t bDataInterface; +} CALL_MGMNT_FUNC_DESCR; + +typedef struct { + uint8_t bFunctionLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubtype; + uint8_t bmCapabilities; +} ACM_FUNC_DESCR, DLM_FUNC_DESCR, TEL_OPER_MODES_FUNC_DESCR, +TEL_CALL_STATE_REP_CPBL_FUNC_DESCR; + +typedef struct { + uint8_t bFunctionLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubtype; + uint8_t bRingerVolSteps; + uint8_t bNumRingerPatterns; +} TEL_RINGER_FUNC_DESCR; + +typedef struct { + uint32_t dwDTERate; // Data Terminal Rate in bits per second + uint8_t bCharFormat; // 0 - 1 stop bit, 1 - 1.5 stop bits, 2 - 2 stop bits + uint8_t bParityType; // 0 - None, 1 - Odd, 2 - Even, 3 - Mark, 4 - Space + uint8_t bDataBits; // Data bits (5, 6, 7, 8 or 16) +} LINE_CODING; + +typedef struct { + uint8_t bmRequestType; // 0xa1 for class-specific notifications + uint8_t bNotification; + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; + uint16_t bmState; //UART state bitmap for SERIAL_STATE, other notifications variable length +} CLASS_NOTIFICATION; + +class ACM; + +class CDCAsyncOper { +public: + + virtual uint8_t OnInit(ACM *pacm) { + return 0; + }; + //virtual void OnDataRcvd(ACM *pacm, uint8_t nbytes, uint8_t *dataptr) = 0; + //virtual void OnDisconnected(ACM *pacm) = 0; +}; + +/** + * This structure is used to report the extended capabilities of the connected device. + * It is also used to report the current status. + * Regular CDC-ACM reports all as false. + */ +typedef struct { + + union { + uint8_t tty; + + struct { + bool enhanced : 1; // Do we have the ability to set/clear any features? + // Status and 8th bit in data stream. + // Presence only indicates feature is available, but this isn't used for CDC-ACM. + bool wide : 1; + bool autoflow_RTS : 1; // Has autoflow on RTS/CTS + bool autoflow_DSR : 1; // Has autoflow on DTR/DSR + bool autoflow_XON : 1; // Has autoflow XON/XOFF + bool half_duplex : 1; // Has half-duplex capability. + } __attribute__((packed)); + }; +} tty_features; + +#define ACM_MAX_ENDPOINTS 4 + +class ACM : public USBDeviceConfig, public UsbConfigXtracter { +protected: + static const uint8_t epDataInIndex; // DataIn endpoint index + static const uint8_t epDataOutIndex; // DataOUT endpoint index + static const uint8_t epInterruptInIndex; // InterruptIN endpoint index + + USB *pUsb; + CDCAsyncOper *pAsync; + uint8_t bAddress; + uint8_t bConfNum; // configuration number + uint8_t bControlIface; // Control interface value + uint8_t bDataIface; // Data interface value + uint8_t bNumEP; // total number of EP in the configuration + uint32_t qNextPollTime; // next poll time + volatile bool bPollEnable; // poll enable flag + volatile bool ready; //device ready indicator + tty_features _enhanced_status; // current status + + EpInfo epInfo[ACM_MAX_ENDPOINTS]; + + void PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr); + +public: + ACM(USB *pusb, CDCAsyncOper *pasync); + + uint8_t SetCommFeature(uint16_t fid, uint8_t nbytes, uint8_t *dataptr); + uint8_t GetCommFeature(uint16_t fid, uint8_t nbytes, uint8_t *dataptr); + uint8_t ClearCommFeature(uint16_t fid); + uint8_t SetLineCoding(const LINE_CODING *dataptr); + uint8_t GetLineCoding(LINE_CODING *dataptr); + uint8_t SetControlLineState(uint8_t state); + uint8_t SendBreak(uint16_t duration); + uint8_t GetNotif(uint16_t *bytes_rcvd, uint8_t *dataptr); + + // Methods for receiving and sending data + uint8_t RcvData(uint16_t *nbytesptr, uint8_t *dataptr); + uint8_t SndData(uint16_t nbytes, uint8_t *dataptr); + + // USBDeviceConfig implementation + uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed); + uint8_t Release(); + uint8_t Poll(); + + bool available(void) { + + }; + + virtual uint8_t GetAddress() { + return bAddress; + }; + + virtual bool isReady() { + return ready; + }; + + virtual tty_features enhanced_status(void) { + return _enhanced_status; + }; + + virtual tty_features enhanced_features(void) { + tty_features rv; + rv.enhanced = false; + rv.autoflow_RTS = false; + rv.autoflow_DSR = false; + rv.autoflow_XON = false; + rv.half_duplex = false; + rv.wide = false; + return rv; + }; + + virtual void autoflowRTS(bool s) { + }; + + virtual void autoflowDSR(bool s) { + }; + + virtual void autoflowXON(bool s) { + }; + + virtual void half_duplex(bool s) { + }; + + virtual void wide(bool s) { + }; + + // UsbConfigXtracter implementation + void EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *ep); +}; + +#endif // __CDCACM_H__ diff --git a/libraries/USB_Host_Shield/cdcftdi.cpp b/libraries/USB_Host_Shield/cdcftdi.cpp new file mode 100755 index 0000000..80d21d1 --- /dev/null +++ b/libraries/USB_Host_Shield/cdcftdi.cpp @@ -0,0 +1,334 @@ +/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved. + +This software may be distributed and modified under the terms of the GNU +General Public License version 2 (GPL2) as published by the Free Software +Foundation and appearing in the file GPL2.TXT included in the packaging of +this file. Please note that GPL2 Section 2[b] requires that all works based +on this software must also be made publicly available under the terms of +the GPL2 ("Copyleft"). + +Contact information +------------------- + +Circuits At Home, LTD +Web : http://www.circuitsathome.com +e-mail : support@circuitsathome.com + */ +#include "cdcftdi.h" + +const uint8_t FTDI::epDataInIndex = 1; +const uint8_t FTDI::epDataOutIndex = 2; +const uint8_t FTDI::epInterruptInIndex = 3; + +FTDI::FTDI(USB *p, FTDIAsyncOper *pasync) : +pAsync(pasync), +pUsb(p), +bAddress(0), +bNumEP(1), +wFTDIType(0) { + for(uint8_t i = 0; i < FTDI_MAX_ENDPOINTS; i++) { + epInfo[i].epAddr = 0; + epInfo[i].maxPktSize = (i) ? 0 : 8; + epInfo[i].epAttribs = 0; + epInfo[i].bmNakPower = (i==epDataInIndex) ? USB_NAK_NOWAIT: USB_NAK_MAX_POWER; + } + if(pUsb) + pUsb->RegisterDeviceClass(this); +} + +uint8_t FTDI::Init(uint8_t parent, uint8_t port, bool lowspeed) { + const uint8_t constBufSize = sizeof (USB_DEVICE_DESCRIPTOR); + + uint8_t buf[constBufSize]; + USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast(buf); + uint8_t rcode; + UsbDevice *p = NULL; + EpInfo *oldep_ptr = NULL; + + uint8_t num_of_conf; // number of configurations + + AddressPool &addrPool = pUsb->GetAddressPool(); + + USBTRACE("FTDI Init\r\n"); + + if(bAddress) + return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE; + + // Get pointer to pseudo device with address 0 assigned + p = addrPool.GetUsbDevicePtr(0); + + if(!p) + return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; + + if(!p->epinfo) { + USBTRACE("epinfo\r\n"); + return USB_ERROR_EPINFO_IS_NULL; + } + + // Save old pointer to EP_RECORD of address 0 + oldep_ptr = p->epinfo; + + // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence + p->epinfo = epInfo; + + p->lowspeed = lowspeed; + + // Get device descriptor + rcode = pUsb->getDevDescr(0, 0, sizeof (USB_DEVICE_DESCRIPTOR), buf); + + // Restore p->epinfo + p->epinfo = oldep_ptr; + + if(rcode) + goto FailGetDevDescr; + if(udd->idVendor != FTDI_VID || udd->idProduct != FTDI_PID) + return USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED; + + // Save type of FTDI chip + wFTDIType = udd->bcdDevice; + + // Allocate new address according to device class + bAddress = addrPool.AllocAddress(parent, false, port); + + if(!bAddress) + return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL; + + // Extract Max Packet Size from the device descriptor + epInfo[0].maxPktSize = udd->bMaxPacketSize0; + + // Assign new address to the device + rcode = pUsb->setAddr(0, 0, bAddress); + + if(rcode) { + p->lowspeed = false; + addrPool.FreeAddress(bAddress); + bAddress = 0; + USBTRACE2("setAddr:", rcode); + return rcode; + } + + USBTRACE2("Addr:", bAddress); + + p->lowspeed = false; + + p = addrPool.GetUsbDevicePtr(bAddress); + + if(!p) + return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; + + p->lowspeed = lowspeed; + + num_of_conf = udd->bNumConfigurations; + + // Assign epInfo to epinfo pointer + rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo); + + if(rcode) + goto FailSetDevTblEntry; + + USBTRACE2("NC:", num_of_conf); + + for(uint8_t i = 0; i < num_of_conf; i++) { + HexDumper HexDump; + ConfigDescParser < 0xFF, 0xFF, 0xFF, CP_MASK_COMPARE_ALL> confDescrParser(this); + + rcode = pUsb->getConfDescr(bAddress, 0, i, &HexDump); + + if(rcode) + goto FailGetConfDescr; + + rcode = pUsb->getConfDescr(bAddress, 0, i, &confDescrParser); + + if(rcode) + goto FailGetConfDescr; + + if(bNumEP > 1) + break; + } // for + + if(bNumEP < 2) + return USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED; + + USBTRACE2("NumEP:", bNumEP); + + // Assign epInfo to epinfo pointer + rcode = pUsb->setEpInfoEntry(bAddress, bNumEP, epInfo); + + USBTRACE2("Conf:", bConfNum); + + // Set Configuration Value + rcode = pUsb->setConf(bAddress, 0, bConfNum); + + if(rcode) + goto FailSetConfDescr; + + rcode = pAsync->OnInit(this); + + if(rcode) + goto FailOnInit; + + USBTRACE("FTDI configured\r\n"); + + bPollEnable = true; + return 0; + +FailGetDevDescr: +#ifdef DEBUG_USB_HOST + NotifyFailGetDevDescr(); + goto Fail; +#endif + +FailSetDevTblEntry: +#ifdef DEBUG_USB_HOST + NotifyFailSetDevTblEntry(); + goto Fail; +#endif + +FailGetConfDescr: +#ifdef DEBUG_USB_HOST + NotifyFailGetConfDescr(); + goto Fail; +#endif + +FailSetConfDescr: +#ifdef DEBUG_USB_HOST + NotifyFailSetConfDescr(); + goto Fail; +#endif + +FailOnInit: +#ifdef DEBUG_USB_HOST + USBTRACE("OnInit:"); + +Fail: + NotifyFail(rcode); +#endif + Release(); + return rcode; +} + +void FTDI::EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *pep) { + ErrorMessage (PSTR("Conf.Val"), conf); + ErrorMessage (PSTR("Iface Num"), iface); + ErrorMessage (PSTR("Alt.Set"), alt); + + bConfNum = conf; + + uint8_t index; + + if((pep->bmAttributes & 0x03) == 3 && (pep->bEndpointAddress & 0x80) == 0x80) + index = epInterruptInIndex; + else + if((pep->bmAttributes & 0x02) == 2) + index = ((pep->bEndpointAddress & 0x80) == 0x80) ? epDataInIndex : epDataOutIndex; + else + return; + + // Fill in the endpoint info structure + epInfo[index].epAddr = (pep->bEndpointAddress & 0x0F); + epInfo[index].maxPktSize = (uint8_t)pep->wMaxPacketSize; + epInfo[index].epAttribs = 0; + + bNumEP++; + + PrintEndpointDescriptor(pep); +} + +uint8_t FTDI::Release() { + pUsb->GetAddressPool().FreeAddress(bAddress); + + bAddress = 0; + bNumEP = 1; + qNextPollTime = 0; + bPollEnable = false; + return pAsync->OnRelease(this); +} + +uint8_t FTDI::Poll() { + uint8_t rcode = 0; + + //if (!bPollEnable) + // return 0; + + //if (qNextPollTime <= millis()) + //{ + // USB_HOST_SERIAL.println(bAddress, HEX); + + // qNextPollTime = millis() + 100; + //} + return rcode; +} + +uint8_t FTDI::SetBaudRate(uint32_t baud) { + uint16_t baud_value, baud_index = 0; + uint32_t divisor3; + + divisor3 = 48000000 / 2 / baud; // divisor shifted 3 bits to the left + + if(wFTDIType == FT232AM) { + if((divisor3 & 0x7) == 7) + divisor3++; // round x.7/8 up to x+1 + + baud_value = divisor3 >> 3; + divisor3 &= 0x7; + + if(divisor3 == 1) baud_value |= 0xc000; + else // 0.125 + if(divisor3 >= 4) baud_value |= 0x4000; + else // 0.5 + if(divisor3 != 0) baud_value |= 0x8000; // 0.25 + if(baud_value == 1) baud_value = 0; /* special case for maximum baud rate */ + } else { + static const unsigned char divfrac [8] = {0, 3, 2, 0, 1, 1, 2, 3}; + static const unsigned char divindex[8] = {0, 0, 0, 1, 0, 1, 1, 1}; + + baud_value = divisor3 >> 3; + baud_value |= divfrac [divisor3 & 0x7] << 14; + baud_index = divindex[divisor3 & 0x7]; + + /* Deal with special cases for highest baud rates. */ + if(baud_value == 1) baud_value = 0; + else // 1.0 + if(baud_value == 0x4001) baud_value = 1; // 1.5 + } + USBTRACE2("baud_value:", baud_value); + USBTRACE2("baud_index:", baud_index); + return pUsb->ctrlReq(bAddress, 0, bmREQ_FTDI_OUT, FTDI_SIO_SET_BAUD_RATE, baud_value & 0xff, baud_value >> 8, baud_index, 0, 0, NULL, NULL); +} + +uint8_t FTDI::SetModemControl(uint16_t signal) { + return pUsb->ctrlReq(bAddress, 0, bmREQ_FTDI_OUT, FTDI_SIO_MODEM_CTRL, signal & 0xff, signal >> 8, 0, 0, 0, NULL, NULL); +} + +uint8_t FTDI::SetFlowControl(uint8_t protocol, uint8_t xon, uint8_t xoff) { + return pUsb->ctrlReq(bAddress, 0, bmREQ_FTDI_OUT, FTDI_SIO_SET_FLOW_CTRL, xon, xoff, protocol << 8, 0, 0, NULL, NULL); +} + +uint8_t FTDI::SetData(uint16_t databm) { + return pUsb->ctrlReq(bAddress, 0, bmREQ_FTDI_OUT, FTDI_SIO_SET_DATA, databm & 0xff, databm >> 8, 0, 0, 0, NULL, NULL); +} + +uint8_t FTDI::RcvData(uint16_t *bytes_rcvd, uint8_t *dataptr) { + return pUsb->inTransfer(bAddress, epInfo[epDataInIndex].epAddr, bytes_rcvd, dataptr); +} + +uint8_t FTDI::SndData(uint16_t nbytes, uint8_t *dataptr) { + return pUsb->outTransfer(bAddress, epInfo[epDataOutIndex].epAddr, nbytes, dataptr); +} + +void FTDI::PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr) { + Notify(PSTR("Endpoint descriptor:"), 0x80); + Notify(PSTR("\r\nLength:\t\t"), 0x80); + D_PrintHex (ep_ptr->bLength, 0x80); + Notify(PSTR("\r\nType:\t\t"), 0x80); + D_PrintHex (ep_ptr->bDescriptorType, 0x80); + Notify(PSTR("\r\nAddress:\t"), 0x80); + D_PrintHex (ep_ptr->bEndpointAddress, 0x80); + Notify(PSTR("\r\nAttributes:\t"), 0x80); + D_PrintHex (ep_ptr->bmAttributes, 0x80); + Notify(PSTR("\r\nMaxPktSize:\t"), 0x80); + D_PrintHex (ep_ptr->wMaxPacketSize, 0x80); + Notify(PSTR("\r\nPoll Intrv:\t"), 0x80); + D_PrintHex (ep_ptr->bInterval, 0x80); + Notify(PSTR("\r\n"), 0x80); +} diff --git a/libraries/USB_Host_Shield/cdcftdi.h b/libraries/USB_Host_Shield/cdcftdi.h new file mode 100755 index 0000000..5a13324 --- /dev/null +++ b/libraries/USB_Host_Shield/cdcftdi.h @@ -0,0 +1,143 @@ +/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved. + +This software may be distributed and modified under the terms of the GNU +General Public License version 2 (GPL2) as published by the Free Software +Foundation and appearing in the file GPL2.TXT included in the packaging of +this file. Please note that GPL2 Section 2[b] requires that all works based +on this software must also be made publicly available under the terms of +the GPL2 ("Copyleft"). + +Contact information +------------------- + +Circuits At Home, LTD +Web : http://www.circuitsathome.com +e-mail : support@circuitsathome.com + */ +#if !defined(__CDCFTDI_H__) +#define __CDCFTDI_H__ + +#include "Usb.h" + +#define bmREQ_FTDI_OUT 0x40 +#define bmREQ_FTDI_IN 0xc0 + +//#define bmREQ_FTDI_OUT USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_INTERFACE +//#define bmREQ_FTDI_IN USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_INTERFACE + +#define FTDI_VID 0x0403 // FTDI VID +#define FTDI_PID 0x6001 // FTDI PID + +#define FT232AM 0x0200 +#define FT232BM 0x0400 +#define FT2232 0x0500 +#define FT232R 0x0600 + +// Commands +#define FTDI_SIO_RESET 0 /* Reset the port */ +#define FTDI_SIO_MODEM_CTRL 1 /* Set the modem control register */ +#define FTDI_SIO_SET_FLOW_CTRL 2 /* Set flow control register */ +#define FTDI_SIO_SET_BAUD_RATE 3 /* Set baud rate */ +#define FTDI_SIO_SET_DATA 4 /* Set the data characteristics of the port */ +#define FTDI_SIO_GET_MODEM_STATUS 5 /* Retrieve current value of modem status register */ +#define FTDI_SIO_SET_EVENT_CHAR 6 /* Set the event character */ +#define FTDI_SIO_SET_ERROR_CHAR 7 /* Set the error character */ + +#define FTDI_SIO_RESET_SIO 0 +#define FTDI_SIO_RESET_PURGE_RX 1 +#define FTDI_SIO_RESET_PURGE_TX 2 + +#define FTDI_SIO_SET_DATA_PARITY_NONE (0x0 << 8 ) +#define FTDI_SIO_SET_DATA_PARITY_ODD (0x1 << 8 ) +#define FTDI_SIO_SET_DATA_PARITY_EVEN (0x2 << 8 ) +#define FTDI_SIO_SET_DATA_PARITY_MARK (0x3 << 8 ) +#define FTDI_SIO_SET_DATA_PARITY_SPACE (0x4 << 8 ) +#define FTDI_SIO_SET_DATA_STOP_BITS_1 (0x0 << 11) +#define FTDI_SIO_SET_DATA_STOP_BITS_15 (0x1 << 11) +#define FTDI_SIO_SET_DATA_STOP_BITS_2 (0x2 << 11) +#define FTDI_SIO_SET_BREAK (0x1 << 14) + +#define FTDI_SIO_SET_DTR_MASK 0x1 +#define FTDI_SIO_SET_DTR_HIGH ( 1 | ( FTDI_SIO_SET_DTR_MASK << 8)) +#define FTDI_SIO_SET_DTR_LOW ( 0 | ( FTDI_SIO_SET_DTR_MASK << 8)) +#define FTDI_SIO_SET_RTS_MASK 0x2 +#define FTDI_SIO_SET_RTS_HIGH ( 2 | ( FTDI_SIO_SET_RTS_MASK << 8 )) +#define FTDI_SIO_SET_RTS_LOW ( 0 | ( FTDI_SIO_SET_RTS_MASK << 8 )) + +#define FTDI_SIO_DISABLE_FLOW_CTRL 0x0 +#define FTDI_SIO_RTS_CTS_HS (0x1 << 8) +#define FTDI_SIO_DTR_DSR_HS (0x2 << 8) +#define FTDI_SIO_XON_XOFF_HS (0x4 << 8) + +#define FTDI_SIO_CTS_MASK 0x10 +#define FTDI_SIO_DSR_MASK 0x20 +#define FTDI_SIO_RI_MASK 0x40 +#define FTDI_SIO_RLSD_MASK 0x80 + +class FTDI; + +class FTDIAsyncOper { +public: + + virtual uint8_t OnInit(FTDI *pftdi) { + }; + + virtual uint8_t OnRelease(FTDI *pftdi) { + }; +}; + + +// Only single port chips are currently supported by the library, +// so only three endpoints are allocated. +#define FTDI_MAX_ENDPOINTS 3 + +class FTDI : public USBDeviceConfig, public UsbConfigXtracter { + static const uint8_t epDataInIndex; // DataIn endpoint index + static const uint8_t epDataOutIndex; // DataOUT endpoint index + static const uint8_t epInterruptInIndex; // InterruptIN endpoint index + + FTDIAsyncOper *pAsync; + USB *pUsb; + uint8_t bAddress; + uint8_t bConfNum; // configuration number + uint8_t bNumIface; // number of interfaces in the configuration + uint8_t bNumEP; // total number of EP in the configuration + uint32_t qNextPollTime; // next poll time + bool bPollEnable; // poll enable flag + uint16_t wFTDIType; // Type of FTDI chip + + EpInfo epInfo[FTDI_MAX_ENDPOINTS]; + + void PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr); + +public: + FTDI(USB *pusb, FTDIAsyncOper *pasync); + + uint8_t SetBaudRate(uint32_t baud); + uint8_t SetModemControl(uint16_t control); + uint8_t SetFlowControl(uint8_t protocol, uint8_t xon = 0x11, uint8_t xoff = 0x13); + uint8_t SetData(uint16_t databm); + + // Methods for recieving and sending data + uint8_t RcvData(uint16_t *bytes_rcvd, uint8_t *dataptr); + uint8_t SndData(uint16_t nbytes, uint8_t *dataptr); + + // USBDeviceConfig implementation + uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed); + uint8_t Release(); + uint8_t Poll(); + + virtual uint8_t GetAddress() { + return bAddress; + }; + + // UsbConfigXtracter implementation + void EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *ep); + + virtual bool VIDPIDOK(uint16_t vid, uint16_t pid) { + return (vid == FTDI_VID && pid == FTDI_PID); + } + +}; + +#endif // __CDCFTDI_H__ diff --git a/libraries/USB_Host_Shield/cdcprolific.cpp b/libraries/USB_Host_Shield/cdcprolific.cpp new file mode 100755 index 0000000..6490c40 --- /dev/null +++ b/libraries/USB_Host_Shield/cdcprolific.cpp @@ -0,0 +1,210 @@ +/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved. + +This software may be distributed and modified under the terms of the GNU +General Public License version 2 (GPL2) as published by the Free Software +Foundation and appearing in the file GPL2.TXT included in the packaging of +this file. Please note that GPL2 Section 2[b] requires that all works based +on this software must also be made publicly available under the terms of +the GPL2 ("Copyleft"). + +Contact information +------------------- + +Circuits At Home, LTD +Web : http://www.circuitsathome.com +e-mail : support@circuitsathome.com + */ +#include "cdcprolific.h" + +PL2303::PL2303(USB *p, CDCAsyncOper *pasync) : +ACM(p, pasync), +wPLType(0) { +} + +uint8_t PL2303::Init(uint8_t parent, uint8_t port, bool lowspeed) { + const uint8_t constBufSize = sizeof (USB_DEVICE_DESCRIPTOR); + + uint8_t buf[constBufSize]; + USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast(buf); + uint8_t rcode; + UsbDevice *p = NULL; + EpInfo *oldep_ptr = NULL; + uint8_t num_of_conf; // number of configurations + + AddressPool &addrPool = pUsb->GetAddressPool(); + + USBTRACE("PL Init\r\n"); + + if(bAddress) + return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE; + + // Get pointer to pseudo device with address 0 assigned + p = addrPool.GetUsbDevicePtr(0); + + if(!p) + return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; + + if(!p->epinfo) { + USBTRACE("epinfo\r\n"); + return USB_ERROR_EPINFO_IS_NULL; + } + + // Save old pointer to EP_RECORD of address 0 + oldep_ptr = p->epinfo; + + // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence + p->epinfo = epInfo; + + p->lowspeed = lowspeed; + + // Get device descriptor + rcode = pUsb->getDevDescr(0, 0, sizeof (USB_DEVICE_DESCRIPTOR), (uint8_t*)buf); + + // Restore p->epinfo + p->epinfo = oldep_ptr; + + if(rcode) + goto FailGetDevDescr; + + if(udd->idVendor != PL_VID && udd->idProduct != PL_PID) + return USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED; + + // Save type of PL chip + wPLType = udd->bcdDevice; + + // Allocate new address according to device class + bAddress = addrPool.AllocAddress(parent, false, port); + + if(!bAddress) + return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL; + + // Extract Max Packet Size from the device descriptor + epInfo[0].maxPktSize = udd->bMaxPacketSize0; + + // Assign new address to the device + rcode = pUsb->setAddr(0, 0, bAddress); + + if(rcode) { + p->lowspeed = false; + addrPool.FreeAddress(bAddress); + bAddress = 0; + USBTRACE2("setAddr:", rcode); + return rcode; + } + + USBTRACE2("Addr:", bAddress); + + p->lowspeed = false; + + p = addrPool.GetUsbDevicePtr(bAddress); + + if(!p) + return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; + + p->lowspeed = lowspeed; + + num_of_conf = udd->bNumConfigurations; + + // Assign epInfo to epinfo pointer + rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo); + + if(rcode) + goto FailSetDevTblEntry; + + USBTRACE2("NC:", num_of_conf); + + for(uint8_t i = 0; i < num_of_conf; i++) { + HexDumper HexDump; + ConfigDescParser < 0xFF, 0, 0, CP_MASK_COMPARE_CLASS> confDescrParser(this); + + rcode = pUsb->getConfDescr(bAddress, 0, i, &HexDump); + + if(rcode) + goto FailGetConfDescr; + + rcode = pUsb->getConfDescr(bAddress, 0, i, &confDescrParser); + + if(rcode) + goto FailGetConfDescr; + + if(bNumEP > 1) + break; + } // for + + if(bNumEP < 2) + return USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED; + + // Assign epInfo to epinfo pointer + rcode = pUsb->setEpInfoEntry(bAddress, bNumEP, epInfo); + + USBTRACE2("Conf:", bConfNum); + + // Set Configuration Value + rcode = pUsb->setConf(bAddress, 0, bConfNum); + + if(rcode) + goto FailSetConfDescr; + + rcode = pAsync->OnInit(this); + + if(rcode) + goto FailOnInit; + + USBTRACE("PL configured\r\n"); + + //bPollEnable = true; + ready = true; + return 0; + +FailGetDevDescr: +#ifdef DEBUG_USB_HOST + NotifyFailGetDevDescr(); + goto Fail; +#endif + +FailSetDevTblEntry: +#ifdef DEBUG_USB_HOST + NotifyFailSetDevTblEntry(); + goto Fail; +#endif + +FailGetConfDescr: +#ifdef DEBUG_USB_HOST + NotifyFailGetConfDescr(); + goto Fail; +#endif + +FailSetConfDescr: +#ifdef DEBUG_USB_HOST + NotifyFailSetConfDescr(); + goto Fail; +#endif + +FailOnInit: +#ifdef DEBUG_USB_HOST + USBTRACE("OnInit:"); +#endif + +#ifdef DEBUG_USB_HOST +Fail: + NotifyFail(rcode); +#endif + Release(); + return rcode; +} + +//uint8_t PL::Poll() +//{ +// uint8_t rcode = 0; +// +// //if (!bPollEnable) +// // return 0; +// +// //if (qNextPollTime <= millis()) +// //{ +// // USB_HOST_SERIAL.println(bAddress, HEX); +// +// // qNextPollTime = millis() + 100; +// //} +// return rcode; +//} diff --git a/libraries/USB_Host_Shield/cdcprolific.h b/libraries/USB_Host_Shield/cdcprolific.h new file mode 100755 index 0000000..3256658 --- /dev/null +++ b/libraries/USB_Host_Shield/cdcprolific.h @@ -0,0 +1,135 @@ +/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved. + +This software may be distributed and modified under the terms of the GNU +General Public License version 2 (GPL2) as published by the Free Software +Foundation and appearing in the file GPL2.TXT included in the packaging of +this file. Please note that GPL2 Section 2[b] requires that all works based +on this software must also be made publicly available under the terms of +the GPL2 ("Copyleft"). + +Contact information +------------------- + +Circuits At Home, LTD +Web : http://www.circuitsathome.com +e-mail : support@circuitsathome.com + */ +#if !defined(__CDCPROLIFIC_H__) +#define __CDCPROLIFIC_H__ + +#include "cdcacm.h" + +#define PL_VID 0x067B +#define PL_PID ( 0x2303 || 0x0609 ) + +//#define PL_PID 0x0609 + +#define PROLIFIC_REV_H 0x0202 +#define PROLIFIC_REV_X 0x0300 +#define PROLIFIC_REV_HX_CHIP_D 0x0400 +#define PROLIFIC_REV_1 0x0001 + +#define kXOnChar '\x11' +#define kXOffChar '\x13' + +#define SPECIAL_SHIFT (5) +#define SPECIAL_MASK ((1< +class ConfigDescParser : public USBReadParser { + UsbConfigXtracter *theXtractor; + MultiValueBuffer theBuffer; + MultiByteValueParser valParser; + ByteSkipper theSkipper; + uint8_t varBuffer[16 /*sizeof(USB_CONFIGURATION_DESCRIPTOR)*/]; + + uint8_t stateParseDescr; // ParseDescriptor state + + uint8_t dscrLen; // Descriptor length + uint8_t dscrType; // Descriptor type + + bool isGoodInterface; // Apropriate interface flag + uint8_t confValue; // Configuration value + uint8_t protoValue; // Protocol value + uint8_t ifaceNumber; // Interface number + uint8_t ifaceAltSet; // Interface alternate settings + + bool UseOr; + bool ParseDescriptor(uint8_t **pp, uint16_t *pcntdn); + void PrintHidDescriptor(const USB_HID_DESCRIPTOR *pDesc); + +public: + + void SetOR(void) { + UseOr = true; + } + ConfigDescParser(UsbConfigXtracter *xtractor); + void Parse(const uint16_t len, const uint8_t *pbuf, const uint16_t &offset); +}; + +template +ConfigDescParser::ConfigDescParser(UsbConfigXtracter *xtractor) : +theXtractor(xtractor), +stateParseDescr(0), +dscrLen(0), +dscrType(0), +UseOr(false) { + theBuffer.pValue = varBuffer; + valParser.Initialize(&theBuffer); + theSkipper.Initialize(&theBuffer); +}; + +template +void ConfigDescParser::Parse(const uint16_t len, const uint8_t *pbuf, const uint16_t &offset) { + uint16_t cntdn = (uint16_t)len; + uint8_t *p = (uint8_t*)pbuf; + + while(cntdn) + if(!ParseDescriptor(&p, &cntdn)) + return; +} + +/* Parser for the configuration descriptor. Takes values for class, subclass, protocol fields in interface descriptor and + compare masks for them. When the match is found, calls EndpointXtract passing buffer containing endpoint descriptor */ +template +bool ConfigDescParser::ParseDescriptor(uint8_t **pp, uint16_t *pcntdn) { + USB_CONFIGURATION_DESCRIPTOR* ucd = reinterpret_cast(varBuffer); + USB_INTERFACE_DESCRIPTOR* uid = reinterpret_cast(varBuffer); + switch(stateParseDescr) { + case 0: + theBuffer.valueSize = 2; + valParser.Initialize(&theBuffer); + stateParseDescr = 1; + case 1: + if(!valParser.Parse(pp, pcntdn)) + return false; + dscrLen = *((uint8_t*)theBuffer.pValue); + dscrType = *((uint8_t*)theBuffer.pValue + 1); + stateParseDescr = 2; + case 2: + // This is a sort of hack. Assuming that two bytes are all ready in the buffer + // the pointer is positioned two bytes ahead in order for the rest of descriptor + // to be read right after the size and the type fields. + // This should be used carefully. varBuffer should be used directly to handle data + // in the buffer. + theBuffer.pValue = varBuffer + 2; + stateParseDescr = 3; + case 3: + switch(dscrType) { + case USB_DESCRIPTOR_INTERFACE: + isGoodInterface = false; + case USB_DESCRIPTOR_CONFIGURATION: + theBuffer.valueSize = sizeof (USB_CONFIGURATION_DESCRIPTOR) - 2; + break; + case USB_DESCRIPTOR_ENDPOINT: + theBuffer.valueSize = sizeof (USB_ENDPOINT_DESCRIPTOR) - 2; + break; + case HID_DESCRIPTOR_HID: + theBuffer.valueSize = dscrLen - 2; + break; + } + valParser.Initialize(&theBuffer); + stateParseDescr = 4; + case 4: + switch(dscrType) { + case USB_DESCRIPTOR_CONFIGURATION: + if(!valParser.Parse(pp, pcntdn)) + return false; + confValue = ucd->bConfigurationValue; + break; + case USB_DESCRIPTOR_INTERFACE: + if(!valParser.Parse(pp, pcntdn)) + return false; + if((MASK & CP_MASK_COMPARE_CLASS) && uid->bInterfaceClass != CLASS_ID) + break; + if((MASK & CP_MASK_COMPARE_SUBCLASS) && uid->bInterfaceSubClass != SUBCLASS_ID) + break; + if(UseOr) { + if((!((MASK & CP_MASK_COMPARE_PROTOCOL) && uid->bInterfaceProtocol))) + break; + } else { + if((MASK & CP_MASK_COMPARE_PROTOCOL) && uid->bInterfaceProtocol != PROTOCOL_ID) + break; + } + isGoodInterface = true; + ifaceNumber = uid->bInterfaceNumber; + ifaceAltSet = uid->bAlternateSetting; + protoValue = uid->bInterfaceProtocol; + break; + case USB_DESCRIPTOR_ENDPOINT: + if(!valParser.Parse(pp, pcntdn)) + return false; + if(isGoodInterface) + if(theXtractor) + theXtractor->EndpointXtract(confValue, ifaceNumber, ifaceAltSet, protoValue, (USB_ENDPOINT_DESCRIPTOR*)varBuffer); + break; + //case HID_DESCRIPTOR_HID: + // if (!valParser.Parse(pp, pcntdn)) + // return false; + // PrintHidDescriptor((const USB_HID_DESCRIPTOR*)varBuffer); + // break; + default: + if(!theSkipper.Skip(pp, pcntdn, dscrLen - 2)) + return false; + } + theBuffer.pValue = varBuffer; + stateParseDescr = 0; + } + return true; +} + +template +void ConfigDescParser::PrintHidDescriptor(const USB_HID_DESCRIPTOR *pDesc) { + Notify(PSTR("\r\n\r\nHID Descriptor:\r\n"), 0x80); + Notify(PSTR("bDescLength:\t\t"), 0x80); + PrintHex (pDesc->bLength, 0x80); + + Notify(PSTR("\r\nbDescriptorType:\t"), 0x80); + PrintHex (pDesc->bDescriptorType, 0x80); + + Notify(PSTR("\r\nbcdHID:\t\t\t"), 0x80); + PrintHex (pDesc->bcdHID, 0x80); + + Notify(PSTR("\r\nbCountryCode:\t\t"), 0x80); + PrintHex (pDesc->bCountryCode, 0x80); + + Notify(PSTR("\r\nbNumDescriptors:\t"), 0x80); + PrintHex (pDesc->bNumDescriptors, 0x80); + + for(uint8_t i = 0; i < pDesc->bNumDescriptors; i++) { + HID_CLASS_DESCRIPTOR_LEN_AND_TYPE *pLT = (HID_CLASS_DESCRIPTOR_LEN_AND_TYPE*)&(pDesc->bDescrType); + + Notify(PSTR("\r\nbDescrType:\t\t"), 0x80); + PrintHex (pLT[i].bDescrType, 0x80); + + Notify(PSTR("\r\nwDescriptorLength:\t"), 0x80); + PrintHex (pLT[i].wDescriptorLength, 0x80); + } + Notify(PSTR("\r\n"), 0x80); +} + + +#endif // __CONFDESCPARSER_H__ diff --git a/libraries/USB_Host_Shield/controllerEnums.h b/libraries/USB_Host_Shield/controllerEnums.h new file mode 100755 index 0000000..0169c76 --- /dev/null +++ b/libraries/USB_Host_Shield/controllerEnums.h @@ -0,0 +1,204 @@ +/* Copyright (C) 2013 Kristian Lauszus, TKJ Electronics. All rights reserved. + + This software may be distributed and modified under the terms of the GNU + General Public License version 2 (GPL2) as published by the Free Software + Foundation and appearing in the file GPL2.TXT included in the packaging of + this file. Please note that GPL2 Section 2[b] requires that all works based + on this software must also be made publicly available under the terms of + the GPL2 ("Copyleft"). + + Contact information + ------------------- + + Kristian Lauszus, TKJ Electronics + Web : http://www.tkjelectronics.com + e-mail : kristianl@tkjelectronics.com + */ + +#ifndef _controllerenums_h +#define _controllerenums_h + +/** + * This header file is used to store different enums for the controllers, + * This is necessary so all the different libraries can be used at once. + */ + +/** Enum used to turn on the LEDs on the different controllers. */ +enum LEDEnum { + OFF = 0, + LED1 = 1, + LED2 = 2, + LED3 = 3, + LED4 = 4, + + LED5 = 5, + LED6 = 6, + LED7 = 7, + LED8 = 8, + LED9 = 9, + LED10 = 10, + /** Used to blink all LEDs on the Xbox controller */ + ALL = 5, +}; + +/** Used to set the colors of the Move and PS4 controller. */ +enum ColorsEnum { + /** r = 255, g = 0, b = 0 */ + Red = 0xFF0000, + /** r = 0, g = 255, b = 0 */ + Green = 0xFF00, + /** r = 0, g = 0, b = 255 */ + Blue = 0xFF, + + /** r = 255, g = 235, b = 4 */ + Yellow = 0xFFEB04, + /** r = 0, g = 255, b = 255 */ + Lightblue = 0xFFFF, + /** r = 255, g = 0, b = 255 */ + Purble = 0xFF00FF, + + /** r = 255, g = 255, b = 255 */ + White = 0xFFFFFF, + /** r = 0, g = 0, b = 0 */ + Off = 0x00, +}; + +enum RumbleEnum { + RumbleHigh = 0x10, + RumbleLow = 0x20, +}; + +/** This enum is used to read all the different buttons on the different controllers */ +enum ButtonEnum { + /**@{*/ + /** These buttons are available on all the the controllers */ + UP = 0, + RIGHT = 1, + DOWN = 2, + LEFT = 3, + /**@}*/ + + /**@{*/ + /** Wii buttons */ + PLUS = 5, + TWO = 6, + ONE = 7, + MINUS = 8, + HOME = 9, + Z = 10, + C = 11, + B = 12, + A = 13, + /**@}*/ + + /**@{*/ + /** These are only available on the Wii U Pro Controller */ + L = 16, + R = 17, + ZL = 18, + ZR = 19, + /**@}*/ + + /**@{*/ + /** PS3 controllers buttons */ + SELECT = 4, + START = 5, + L3 = 6, + R3 = 7, + + L2 = 8, + R2 = 9, + L1 = 10, + R1 = 11, + TRIANGLE = 12, + CIRCLE = 13, + CROSS = 14, + SQUARE = 15, + + PS = 16, + + MOVE = 17, // Covers 12 bits - we only need to read the top 8 + T = 18, // Covers 12 bits - we only need to read the top 8 + /**@}*/ + + /** PS4 controllers buttons - SHARE and OPTIONS are present instead of SELECT and START */ + SHARE = 4, + OPTIONS = 5, + TOUCHPAD = 17, + /**@}*/ + + /**@{*/ + /** Xbox buttons */ + BACK = 4, + X = 14, + Y = 15, + XBOX = 16, + SYNC = 17, + BLACK = 8, // Available on the original Xbox controller + WHITE = 9, // Available on the original Xbox controller + /**@}*/ + + /** PS Buzz controllers */ + RED = 0, + YELLOW = 1, + GREEN = 2, + ORANGE = 3, + BLUE = 4, + /**@}*/ +}; + +/** Joysticks on the PS3 and Xbox controllers. */ +enum AnalogHatEnum { + /** Left joystick x-axis */ + LeftHatX = 0, + /** Left joystick y-axis */ + LeftHatY = 1, + /** Right joystick x-axis */ + RightHatX = 2, + /** Right joystick y-axis */ + RightHatY = 3, +}; + +/** + * Sensors inside the Sixaxis Dualshock 3, Move controller and PS4 controller. + * Note: that the location is shifted 9 when it's connected via USB on the PS3 controller. + */ +enum SensorEnum { + /** Accelerometer values */ + aX = 50, aY = 52, aZ = 54, + /** Gyro z-axis */ + gZ = 56, + gX, gY, // These are not available on the PS3 controller + + /** Accelerometer x-axis */ + aXmove = 28, + /** Accelerometer z-axis */ + aZmove = 30, + /** Accelerometer y-axis */ + aYmove = 32, + + /** Gyro x-axis */ + gXmove = 40, + /** Gyro z-axis */ + gZmove = 42, + /** Gyro y-axis */ + gYmove = 44, + + /** Temperature sensor */ + tempMove = 46, + + /** Magnetometer x-axis */ + mXmove = 47, + /** Magnetometer z-axis */ + mZmove = 49, + /** Magnetometer y-axis */ + mYmove = 50, +}; + +/** Used to get the angle calculated using the PS3 controller and PS4 controller. */ +enum AngleEnum { + Pitch = 0x01, + Roll = 0x02, +}; + +#endif diff --git a/libraries/USB_Host_Shield/examples/Bluetooth/BTHID/BTHID.ino b/libraries/USB_Host_Shield/examples/Bluetooth/BTHID/BTHID.ino new file mode 100755 index 0000000..7f08332 --- /dev/null +++ b/libraries/USB_Host_Shield/examples/Bluetooth/BTHID/BTHID.ino @@ -0,0 +1,54 @@ +/* + Example sketch for the HID Bluetooth library - developed by Kristian Lauszus + For more information visit my blog: http://blog.tkjelectronics.dk/ or + send me an e-mail: kristianl@tkjelectronics.com + */ + +#include +#include +#include "KeyboardParser.h" +#include "MouseParser.h" +// Satisfy IDE, which only needs to see the include statment in the ino. +#ifdef dobogusinclude +#include +#include +#endif + +USB Usb; +//USBHub Hub1(&Usb); // Some dongles have a hub inside +BTD Btd(&Usb); // You have to create the Bluetooth Dongle instance like so + +/* You can create the instance of the class in two ways */ +// This will start an inquiry and then pair with your device - you only have to do this once +// If you are using a Bluetooth keyboard, then you should type in the password on the keypad and then press enter +BTHID bthid(&Btd, PAIR, "0000"); + +// After that you can simply create the instance like so and then press any button on the device +//BTHID hid(&Btd); + +KbdRptParser keyboardPrs; +MouseRptParser mousePrs; + +void setup() { + Serial.begin(115200); +#if !defined(__MIPSEL__) + while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection +#endif + if (Usb.Init() == -1) { + Serial.print(F("\r\nOSC did not start")); + while (1); // Halt + } + + bthid.SetReportParser(KEYBOARD_PARSER_ID, (HIDReportParser*)&keyboardPrs); + bthid.SetReportParser(MOUSE_PARSER_ID, (HIDReportParser*)&mousePrs); + + // If "Boot Protocol Mode" does not work, then try "Report Protocol Mode" + // If that does not work either, then uncomment PRINTREPORT in BTHID.cpp to see the raw report + bthid.setProtocolMode(HID_BOOT_PROTOCOL); // Boot Protocol Mode + //bthid.setProtocolMode(HID_RPT_PROTOCOL); // Report Protocol Mode + + Serial.print(F("\r\nHID Bluetooth Library Started")); +} +void loop() { + Usb.Task(); +} diff --git a/libraries/USB_Host_Shield/examples/Bluetooth/BTHID/KeyboardParser.h b/libraries/USB_Host_Shield/examples/Bluetooth/BTHID/KeyboardParser.h new file mode 100755 index 0000000..c539433 --- /dev/null +++ b/libraries/USB_Host_Shield/examples/Bluetooth/BTHID/KeyboardParser.h @@ -0,0 +1,105 @@ +#ifndef __kbdrptparser_h_ +#define __kbdrptparser_h_ + +class KbdRptParser : public KeyboardReportParser { + protected: + virtual uint8_t HandleLockingKeys(HID *hid, uint8_t key); + virtual void OnControlKeysChanged(uint8_t before, uint8_t after); + virtual void OnKeyDown(uint8_t mod, uint8_t key); + virtual void OnKeyUp(uint8_t mod, uint8_t key); + virtual void OnKeyPressed(uint8_t key); + + private: + void PrintKey(uint8_t mod, uint8_t key); +}; + +uint8_t KbdRptParser::HandleLockingKeys(HID *hid, uint8_t key) { + uint8_t old_keys = kbdLockingKeys.bLeds; + + switch (key) { + case UHS_HID_BOOT_KEY_NUM_LOCK: + Serial.println(F("Num lock")); + kbdLockingKeys.kbdLeds.bmNumLock = ~kbdLockingKeys.kbdLeds.bmNumLock; + break; + case UHS_HID_BOOT_KEY_CAPS_LOCK: + Serial.println(F("Caps lock")); + kbdLockingKeys.kbdLeds.bmCapsLock = ~kbdLockingKeys.kbdLeds.bmCapsLock; + break; + case UHS_HID_BOOT_KEY_SCROLL_LOCK: + Serial.println(F("Scroll lock")); + kbdLockingKeys.kbdLeds.bmScrollLock = ~kbdLockingKeys.kbdLeds.bmScrollLock; + break; + } + + if (old_keys != kbdLockingKeys.bLeds && hid) { + BTHID *pBTHID = reinterpret_cast (hid); // A cast the other way around is done in BTHID.cpp + pBTHID->setLeds(kbdLockingKeys.bLeds); // Update the LEDs on the keyboard + } + + return 0; +}; + +void KbdRptParser::PrintKey(uint8_t m, uint8_t key) { + MODIFIERKEYS mod; + *((uint8_t*)&mod) = m; + Serial.print((mod.bmLeftCtrl == 1) ? F("C") : F(" ")); + Serial.print((mod.bmLeftShift == 1) ? F("S") : F(" ")); + Serial.print((mod.bmLeftAlt == 1) ? F("A") : F(" ")); + Serial.print((mod.bmLeftGUI == 1) ? F("G") : F(" ")); + + Serial.print(F(" >")); + PrintHex(key, 0x80); + Serial.print(F("< ")); + + Serial.print((mod.bmRightCtrl == 1) ? F("C") : F(" ")); + Serial.print((mod.bmRightShift == 1) ? F("S") : F(" ")); + Serial.print((mod.bmRightAlt == 1) ? F("A") : F(" ")); + Serial.println((mod.bmRightGUI == 1) ? F("G") : F(" ")); +}; + +void KbdRptParser::OnKeyDown(uint8_t mod, uint8_t key) { + Serial.print(F("DN ")); + PrintKey(mod, key); + uint8_t c = OemToAscii(mod, key); + + if (c) + OnKeyPressed(c); +}; + +void KbdRptParser::OnControlKeysChanged(uint8_t before, uint8_t after) { + MODIFIERKEYS beforeMod; + *((uint8_t*)&beforeMod) = before; + + MODIFIERKEYS afterMod; + *((uint8_t*)&afterMod) = after; + + if (beforeMod.bmLeftCtrl != afterMod.bmLeftCtrl) + Serial.println(F("LeftCtrl changed")); + if (beforeMod.bmLeftShift != afterMod.bmLeftShift) + Serial.println(F("LeftShift changed")); + if (beforeMod.bmLeftAlt != afterMod.bmLeftAlt) + Serial.println(F("LeftAlt changed")); + if (beforeMod.bmLeftGUI != afterMod.bmLeftGUI) + Serial.println(F("LeftGUI changed")); + + if (beforeMod.bmRightCtrl != afterMod.bmRightCtrl) + Serial.println(F("RightCtrl changed")); + if (beforeMod.bmRightShift != afterMod.bmRightShift) + Serial.println(F("RightShift changed")); + if (beforeMod.bmRightAlt != afterMod.bmRightAlt) + Serial.println(F("RightAlt changed")); + if (beforeMod.bmRightGUI != afterMod.bmRightGUI) + Serial.println(F("RightGUI changed")); +}; + +void KbdRptParser::OnKeyUp(uint8_t mod, uint8_t key) { + Serial.print(F("UP ")); + PrintKey(mod, key); +}; + +void KbdRptParser::OnKeyPressed(uint8_t key) { + Serial.print(F("ASCII: ")); + Serial.println((char)key); +}; + +#endif diff --git a/libraries/USB_Host_Shield/examples/Bluetooth/BTHID/MouseParser.h b/libraries/USB_Host_Shield/examples/Bluetooth/BTHID/MouseParser.h new file mode 100755 index 0000000..a9245de --- /dev/null +++ b/libraries/USB_Host_Shield/examples/Bluetooth/BTHID/MouseParser.h @@ -0,0 +1,46 @@ +#ifndef __mouserptparser_h__ +#define __mouserptparser_h__ + +class MouseRptParser : public MouseReportParser { + protected: + virtual void OnMouseMove(MOUSEINFO *mi); + virtual void OnLeftButtonUp(MOUSEINFO *mi); + virtual void OnLeftButtonDown(MOUSEINFO *mi); + virtual void OnRightButtonUp(MOUSEINFO *mi); + virtual void OnRightButtonDown(MOUSEINFO *mi); + virtual void OnMiddleButtonUp(MOUSEINFO *mi); + virtual void OnMiddleButtonDown(MOUSEINFO *mi); +}; + +void MouseRptParser::OnMouseMove(MOUSEINFO *mi) { + Serial.print(F("dx=")); + Serial.print(mi->dX, DEC); + Serial.print(F(" dy=")); + Serial.println(mi->dY, DEC); +}; + +void MouseRptParser::OnLeftButtonUp(MOUSEINFO *mi) { + Serial.println(F("L Butt Up")); +}; + +void MouseRptParser::OnLeftButtonDown(MOUSEINFO *mi) { + Serial.println(F("L Butt Dn")); +}; + +void MouseRptParser::OnRightButtonUp(MOUSEINFO *mi) { + Serial.println(F("R Butt Up")); +}; + +void MouseRptParser::OnRightButtonDown(MOUSEINFO *mi) { + Serial.println(F("R Butt Dn")); +}; + +void MouseRptParser::OnMiddleButtonUp(MOUSEINFO *mi) { + Serial.println(F("M Butt Up")); +}; + +void MouseRptParser::OnMiddleButtonDown(MOUSEINFO *mi) { + Serial.println(F("M Butt Dn")); +}; + +#endif diff --git a/libraries/USB_Host_Shield/examples/Bluetooth/PS3BT/PS3BT.ino b/libraries/USB_Host_Shield/examples/Bluetooth/PS3BT/PS3BT.ino new file mode 100755 index 0000000..3d3ebcd --- /dev/null +++ b/libraries/USB_Host_Shield/examples/Bluetooth/PS3BT/PS3BT.ino @@ -0,0 +1,187 @@ +/* + Example sketch for the PS3 Bluetooth library - developed by Kristian Lauszus + For more information visit my blog: http://blog.tkjelectronics.dk/ or + send me an e-mail: kristianl@tkjelectronics.com + */ + +#include +#include +// Satisfy IDE, which only needs to see the include statment in the ino. +#ifdef dobogusinclude +#include +#include +#endif + +USB Usb; +//USBHub Hub1(&Usb); // Some dongles have a hub inside + +BTD Btd(&Usb); // You have to create the Bluetooth Dongle instance like so +/* You can create the instance of the class in two ways */ +PS3BT PS3(&Btd); // This will just create the instance +//PS3BT PS3(&Btd, 0x00, 0x15, 0x83, 0x3D, 0x0A, 0x57); // This will also store the bluetooth address - this can be obtained from the dongle when running the sketch + +bool printTemperature; +bool printAngle; + +void setup() { + Serial.begin(115200); +#if !defined(__MIPSEL__) + while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection +#endif + if (Usb.Init() == -1) { + Serial.print(F("\r\nOSC did not start")); + while (1); //halt + } + Serial.print(F("\r\nPS3 Bluetooth Library Started")); +} +void loop() { + Usb.Task(); + + if (PS3.PS3Connected || PS3.PS3NavigationConnected) { + if (PS3.getAnalogHat(LeftHatX) > 137 || PS3.getAnalogHat(LeftHatX) < 117 || PS3.getAnalogHat(LeftHatY) > 137 || PS3.getAnalogHat(LeftHatY) < 117 || PS3.getAnalogHat(RightHatX) > 137 || PS3.getAnalogHat(RightHatX) < 117 || PS3.getAnalogHat(RightHatY) > 137 || PS3.getAnalogHat(RightHatY) < 117) { + Serial.print(F("\r\nLeftHatX: ")); + Serial.print(PS3.getAnalogHat(LeftHatX)); + Serial.print(F("\tLeftHatY: ")); + Serial.print(PS3.getAnalogHat(LeftHatY)); + if (PS3.PS3Connected) { // The Navigation controller only have one joystick + Serial.print(F("\tRightHatX: ")); + Serial.print(PS3.getAnalogHat(RightHatX)); + Serial.print(F("\tRightHatY: ")); + Serial.print(PS3.getAnalogHat(RightHatY)); + } + } + + // Analog button values can be read from almost all buttons + if (PS3.getAnalogButton(L2) || PS3.getAnalogButton(R2)) { + Serial.print(F("\r\nL2: ")); + Serial.print(PS3.getAnalogButton(L2)); + if (PS3.PS3Connected) { + Serial.print(F("\tR2: ")); + Serial.print(PS3.getAnalogButton(R2)); + } + } + if (PS3.getButtonClick(PS)) { + Serial.print(F("\r\nPS")); + PS3.disconnect(); + } + else { + if (PS3.getButtonClick(TRIANGLE)) + Serial.print(F("\r\nTraingle")); + if (PS3.getButtonClick(CIRCLE)) + Serial.print(F("\r\nCircle")); + if (PS3.getButtonClick(CROSS)) + Serial.print(F("\r\nCross")); + if (PS3.getButtonClick(SQUARE)) + Serial.print(F("\r\nSquare")); + + if (PS3.getButtonClick(UP)) { + Serial.print(F("\r\nUp")); + if (PS3.PS3Connected) { + PS3.setLedOff(); + PS3.setLedOn(LED4); + } + } + if (PS3.getButtonClick(RIGHT)) { + Serial.print(F("\r\nRight")); + if (PS3.PS3Connected) { + PS3.setLedOff(); + PS3.setLedOn(LED1); + } + } + if (PS3.getButtonClick(DOWN)) { + Serial.print(F("\r\nDown")); + if (PS3.PS3Connected) { + PS3.setLedOff(); + PS3.setLedOn(LED2); + } + } + if (PS3.getButtonClick(LEFT)) { + Serial.print(F("\r\nLeft")); + if (PS3.PS3Connected) { + PS3.setLedOff(); + PS3.setLedOn(LED3); + } + } + + if (PS3.getButtonClick(L1)) + Serial.print(F("\r\nL1")); + if (PS3.getButtonClick(L3)) + Serial.print(F("\r\nL3")); + if (PS3.getButtonClick(R1)) + Serial.print(F("\r\nR1")); + if (PS3.getButtonClick(R3)) + Serial.print(F("\r\nR3")); + + if (PS3.getButtonClick(SELECT)) { + Serial.print(F("\r\nSelect - ")); + PS3.printStatusString(); + } + if (PS3.getButtonClick(START)) { + Serial.print(F("\r\nStart")); + printAngle = !printAngle; + } + } +#if 0 // Set this to 1 in order to see the angle of the controller + if (printAngle) { + Serial.print(F("\r\nPitch: ")); + Serial.print(PS3.getAngle(Pitch)); + Serial.print(F("\tRoll: ")); + Serial.print(PS3.getAngle(Roll)); + } +#endif + } +#if 0 // Set this to 1 in order to enable support for the Playstation Move controller + else if (PS3.PS3MoveConnected) { + if (PS3.getAnalogButton(T)) { + Serial.print(F("\r\nT: ")); + Serial.print(PS3.getAnalogButton(T)); + } + if (PS3.getButtonClick(PS)) { + Serial.print(F("\r\nPS")); + PS3.disconnect(); + } + else { + if (PS3.getButtonClick(SELECT)) { + Serial.print(F("\r\nSelect")); + printTemperature = !printTemperature; + } + if (PS3.getButtonClick(START)) { + Serial.print(F("\r\nStart")); + printAngle = !printAngle; + } + if (PS3.getButtonClick(TRIANGLE)) { + Serial.print(F("\r\nTriangle")); + PS3.moveSetBulb(Red); + } + if (PS3.getButtonClick(CIRCLE)) { + Serial.print(F("\r\nCircle")); + PS3.moveSetBulb(Green); + } + if (PS3.getButtonClick(SQUARE)) { + Serial.print(F("\r\nSquare")); + PS3.moveSetBulb(Blue); + } + if (PS3.getButtonClick(CROSS)) { + Serial.print(F("\r\nCross")); + PS3.moveSetBulb(Yellow); + } + if (PS3.getButtonClick(MOVE)) { + PS3.moveSetBulb(Off); + Serial.print(F("\r\nMove")); + Serial.print(F(" - ")); + PS3.printStatusString(); + } + } + if (printAngle) { + Serial.print(F("\r\nPitch: ")); + Serial.print(PS3.getAngle(Pitch)); + Serial.print(F("\tRoll: ")); + Serial.print(PS3.getAngle(Roll)); + } + else if (printTemperature) { + Serial.print(F("\r\nTemperature: ")); + Serial.print(PS3.getTemperature()); + } + } +#endif +} diff --git a/libraries/USB_Host_Shield/examples/Bluetooth/PS3Multi/PS3Multi.ino b/libraries/USB_Host_Shield/examples/Bluetooth/PS3Multi/PS3Multi.ino new file mode 100755 index 0000000..2d35b05 --- /dev/null +++ b/libraries/USB_Host_Shield/examples/Bluetooth/PS3Multi/PS3Multi.ino @@ -0,0 +1,148 @@ +/* + Example sketch for the PS3 Bluetooth library - developed by Kristian Lauszus + This example show how one can use multiple controllers with the library + For more information visit my blog: http://blog.tkjelectronics.dk/ or + send me an e-mail: kristianl@tkjelectronics.com + */ + +#include +#include +// Satisfy IDE, which only needs to see the include statment in the ino. +#ifdef dobogusinclude +#include +#include +#endif + +USB Usb; +//USBHub Hub1(&Usb); // Some dongles have a hub inside + +BTD Btd(&Usb); // You have to create the Bluetooth Dongle instance like so +PS3BT *PS3[2]; // We will use this pointer to store the two instance, you can easily make it larger if you like, but it will use a lot of RAM! +const uint8_t length = sizeof(PS3) / sizeof(PS3[0]); // Get the lenght of the array +bool printAngle[length]; +bool oldControllerState[length]; + +void setup() { + for (uint8_t i = 0; i < length; i++) { + PS3[i] = new PS3BT(&Btd); // Create the instances + PS3[i]->attachOnInit(onInit); // onInit() is called upon a new connection - you can call the function whatever you like + } + + Serial.begin(115200); +#if !defined(__MIPSEL__) + while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection +#endif + if (Usb.Init() == -1) { + Serial.print(F("\r\nOSC did not start")); + while (1); //halt + } + Serial.print(F("\r\nPS3 Bluetooth Library Started")); +} +void loop() { + Usb.Task(); + + for (uint8_t i = 0; i < length; i++) { + if (PS3[i]->PS3Connected || PS3[i]->PS3NavigationConnected) { + if (PS3[i]->getAnalogHat(LeftHatX) > 137 || PS3[i]->getAnalogHat(LeftHatX) < 117 || PS3[i]->getAnalogHat(LeftHatY) > 137 || PS3[i]->getAnalogHat(LeftHatY) < 117 || PS3[i]->getAnalogHat(RightHatX) > 137 || PS3[i]->getAnalogHat(RightHatX) < 117 || PS3[i]->getAnalogHat(RightHatY) > 137 || PS3[i]->getAnalogHat(RightHatY) < 117) { + Serial.print(F("\r\nLeftHatX: ")); + Serial.print(PS3[i]->getAnalogHat(LeftHatX)); + Serial.print(F("\tLeftHatY: ")); + Serial.print(PS3[i]->getAnalogHat(LeftHatY)); + if (PS3[i]->PS3Connected) { // The Navigation controller only have one joystick + Serial.print(F("\tRightHatX: ")); + Serial.print(PS3[i]->getAnalogHat(RightHatX)); + Serial.print(F("\tRightHatY: ")); + Serial.print(PS3[i]->getAnalogHat(RightHatY)); + } + } + //Analog button values can be read from almost all buttons + if (PS3[i]->getAnalogButton(L2) || PS3[i]->getAnalogButton(R2)) { + Serial.print(F("\r\nL2: ")); + Serial.print(PS3[i]->getAnalogButton(L2)); + if (PS3[i]->PS3Connected) { + Serial.print(F("\tR2: ")); + Serial.print(PS3[i]->getAnalogButton(R2)); + } + } + if (PS3[i]->getButtonClick(PS)) { + Serial.print(F("\r\nPS")); + PS3[i]->disconnect(); + oldControllerState[i] = false; // Reset value + } + else { + if (PS3[i]->getButtonClick(TRIANGLE)) + Serial.print(F("\r\nTraingle")); + if (PS3[i]->getButtonClick(CIRCLE)) + Serial.print(F("\r\nCircle")); + if (PS3[i]->getButtonClick(CROSS)) + Serial.print(F("\r\nCross")); + if (PS3[i]->getButtonClick(SQUARE)) + Serial.print(F("\r\nSquare")); + + if (PS3[i]->getButtonClick(UP)) { + Serial.print(F("\r\nUp")); + if (PS3[i]->PS3Connected) { + PS3[i]->setLedOff(); + PS3[i]->setLedOn(LED4); + } + } + if (PS3[i]->getButtonClick(RIGHT)) { + Serial.print(F("\r\nRight")); + if (PS3[i]->PS3Connected) { + PS3[i]->setLedOff(); + PS3[i]->setLedOn(LED1); + } + } + if (PS3[i]->getButtonClick(DOWN)) { + Serial.print(F("\r\nDown")); + if (PS3[i]->PS3Connected) { + PS3[i]->setLedOff(); + PS3[i]->setLedOn(LED2); + } + } + if (PS3[i]->getButtonClick(LEFT)) { + Serial.print(F("\r\nLeft")); + if (PS3[i]->PS3Connected) { + PS3[i]->setLedOff(); + PS3[i]->setLedOn(LED3); + } + } + + if (PS3[i]->getButtonClick(L1)) + Serial.print(F("\r\nL1")); + if (PS3[i]->getButtonClick(L3)) + Serial.print(F("\r\nL3")); + if (PS3[i]->getButtonClick(R1)) + Serial.print(F("\r\nR1")); + if (PS3[i]->getButtonClick(R3)) + Serial.print(F("\r\nR3")); + + if (PS3[i]->getButtonClick(SELECT)) { + Serial.print(F("\r\nSelect - ")); + PS3[i]->printStatusString(); + } + if (PS3[i]->getButtonClick(START)) { + Serial.print(F("\r\nStart")); + printAngle[i] = !printAngle[i]; + } + } + if (printAngle[i]) { + Serial.print(F("\r\nPitch: ")); + Serial.print(PS3[i]->getAngle(Pitch)); + Serial.print(F("\tRoll: ")); + Serial.print(PS3[i]->getAngle(Roll)); + } + } + /* I have removed the PS3 Move code as an Uno will run out of RAM if it's included */ + //else if(PS3[i]->PS3MoveConnected) { + } +} + +void onInit() { + for (uint8_t i = 0; i < length; i++) { + if ((PS3[i]->PS3Connected || PS3[i]->PS3NavigationConnected) && !oldControllerState[i]) { + oldControllerState[i] = true; // Used to check which is the new controller + PS3[i]->setLedOn((LEDEnum)(i + 1)); // Cast directly to LEDEnum - see: "controllerEnums.h" + } + } +} diff --git a/libraries/USB_Host_Shield/examples/Bluetooth/PS3SPP/PS3SPP.ino b/libraries/USB_Host_Shield/examples/Bluetooth/PS3SPP/PS3SPP.ino new file mode 100755 index 0000000..a9d7876 --- /dev/null +++ b/libraries/USB_Host_Shield/examples/Bluetooth/PS3SPP/PS3SPP.ino @@ -0,0 +1,161 @@ +/* + Example sketch for the Bluetooth library - developed by Kristian Lauszus + For more information visit my blog: http://blog.tkjelectronics.dk/ or + send me an e-mail: kristianl@tkjelectronics.com + + This example show how one can combine all the difference Bluetooth services in one single code. + Note: + You will need a Arduino Mega 1280/2560 to run this sketch, + as a normal Arduino (Uno, Duemilanove etc.) doesn't have enough SRAM and FLASH + */ + +#include +#include +#include +// Satisfy IDE, which only needs to see the include statment in the ino. +#ifdef dobogusinclude +#include +#include +#endif + +USB Usb; +//USBHub Hub1(&Usb); // Some dongles have a hub inside + +BTD Btd(&Usb); // You have to create the Bluetooth Dongle instance like so + +/* You can create the instances of the bluetooth services in two ways */ +SPP SerialBT(&Btd); // This will set the name to the defaults: "Arduino" and the pin to "0000" +//SPP SerialBTBT(&Btd,"Lauszus's Arduino","0000"); // You can also set the name and pin like so +PS3BT PS3(&Btd); // This will just create the instance +//PS3BT PS3(&Btd, 0x00, 0x15, 0x83, 0x3D, 0x0A, 0x57); // This will also store the bluetooth address - this can be obtained from the dongle when running the sketch + +bool firstMessage = true; +String output = ""; // We will store the data in this string + +void setup() { + Serial.begin(115200); // This wil lprint the debugging from the libraries +#if !defined(__MIPSEL__) + while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection +#endif + if (Usb.Init() == -1) { + Serial.print(F("\r\nOSC did not start")); + while (1); //halt + } + Serial.print(F("\r\nBluetooth Library Started")); + output.reserve(200); // Reserve 200 bytes for the output string +} +void loop() { + Usb.Task(); // The SPP data is actually not send until this is called, one could call SerialBT.send() directly as well + + if (SerialBT.connected) { + if (firstMessage) { + firstMessage = false; + SerialBT.println(F("Hello from Arduino")); // Send welcome message + } + if (Serial.available()) + SerialBT.write(Serial.read()); + if (SerialBT.available()) + Serial.write(SerialBT.read()); + } + else + firstMessage = true; + + if (PS3.PS3Connected || PS3.PS3NavigationConnected) { + output = ""; // Reset output string + if (PS3.getAnalogHat(LeftHatX) > 137 || PS3.getAnalogHat(LeftHatX) < 117 || PS3.getAnalogHat(LeftHatY) > 137 || PS3.getAnalogHat(LeftHatY) < 117 || PS3.getAnalogHat(RightHatX) > 137 || PS3.getAnalogHat(RightHatX) < 117 || PS3.getAnalogHat(RightHatY) > 137 || PS3.getAnalogHat(RightHatY) < 117) { + output += "LeftHatX: "; + output += PS3.getAnalogHat(LeftHatX); + output += "\tLeftHatY: "; + output += PS3.getAnalogHat(LeftHatY); + if (PS3.PS3Connected) { // The Navigation controller only have one joystick + output += "\tRightHatX: "; + output += PS3.getAnalogHat(RightHatX); + output += "\tRightHatY: "; + output += PS3.getAnalogHat(RightHatY); + } + } + //Analog button values can be read from almost all buttons + if (PS3.getAnalogButton(L2) || PS3.getAnalogButton(R2)) { + if (output != "") + output += "\r\n"; + output += "L2: "; + output += PS3.getAnalogButton(L2); + if (PS3.PS3Connected) { + output += "\tR2: "; + output += PS3.getAnalogButton(R2); + } + } + if (output != "") { + Serial.println(output); + if (SerialBT.connected) + SerialBT.println(output); + output = ""; // Reset output string + } + if (PS3.getButtonClick(PS)) { + output += " - PS"; + PS3.disconnect(); + } + else { + if (PS3.getButtonClick(TRIANGLE)) + output += " - Traingle"; + if (PS3.getButtonClick(CIRCLE)) + output += " - Circle"; + if (PS3.getButtonClick(CROSS)) + output += " - Cross"; + if (PS3.getButtonClick(SQUARE)) + output += " - Square"; + + if (PS3.getButtonClick(UP)) { + output += " - Up"; + if (PS3.PS3Connected) { + PS3.setLedOff(); + PS3.setLedOn(LED4); + } + } + if (PS3.getButtonClick(RIGHT)) { + output += " - Right"; + if (PS3.PS3Connected) { + PS3.setLedOff(); + PS3.setLedOn(LED1); + } + } + if (PS3.getButtonClick(DOWN)) { + output += " - Down"; + if (PS3.PS3Connected) { + PS3.setLedOff(); + PS3.setLedOn(LED2); + } + } + if (PS3.getButtonClick(LEFT)) { + output += " - Left"; + if (PS3.PS3Connected) { + PS3.setLedOff(); + PS3.setLedOn(LED3); + } + } + + if (PS3.getButtonClick(L1)) + output += " - L1"; + if (PS3.getButtonClick(L3)) + output += " - L3"; + if (PS3.getButtonClick(R1)) + output += " - R1"; + if (PS3.getButtonClick(R3)) + output += " - R3"; + + if (PS3.getButtonClick(SELECT)) { + output += " - Select"; + } + if (PS3.getButtonClick(START)) + output += " - Start"; + + if (output != "") { + String string = "PS3 Controller" + output; + Serial.println(string); + if (SerialBT.connected) + SerialBT.println(string); + } + } + delay(10); + } +} diff --git a/libraries/USB_Host_Shield/examples/Bluetooth/PS4BT/PS4BT.ino b/libraries/USB_Host_Shield/examples/Bluetooth/PS4BT/PS4BT.ino new file mode 100755 index 0000000..8547e0f --- /dev/null +++ b/libraries/USB_Host_Shield/examples/Bluetooth/PS4BT/PS4BT.ino @@ -0,0 +1,146 @@ +/* + Example sketch for the PS4 Bluetooth library - developed by Kristian Lauszus + For more information visit my blog: http://blog.tkjelectronics.dk/ or + send me an e-mail: kristianl@tkjelectronics.com + */ + +#include +#include + +// Satisfy IDE, which only needs to see the include statment in the ino. +#ifdef dobogusinclude +#include +#include +#endif + +USB Usb; +//USBHub Hub1(&Usb); // Some dongles have a hub inside +BTD Btd(&Usb); // You have to create the Bluetooth Dongle instance like so + +/* You can create the instance of the PS4BT class in two ways */ +// This will start an inquiry and then pair with the PS4 controller - you only have to do this once +// You will need to hold down the PS and Share button at the same time, the PS4 controller will then start to blink rapidly indicating that it is in paring mode +PS4BT PS4(&Btd, PAIR); + +// After that you can simply create the instance like so and then press the PS button on the device +//PS4BT PS4(&Btd); + +bool printAngle, printTouch; +uint8_t oldL2Value, oldR2Value; + +void setup() { + Serial.begin(115200); +#if !defined(__MIPSEL__) + while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection +#endif + if (Usb.Init() == -1) { + Serial.print(F("\r\nOSC did not start")); + while (1); // Halt + } + Serial.print(F("\r\nPS4 Bluetooth Library Started")); +} +void loop() { + Usb.Task(); + + if (PS4.connected()) { + if (PS4.getAnalogHat(LeftHatX) > 137 || PS4.getAnalogHat(LeftHatX) < 117 || PS4.getAnalogHat(LeftHatY) > 137 || PS4.getAnalogHat(LeftHatY) < 117 || PS4.getAnalogHat(RightHatX) > 137 || PS4.getAnalogHat(RightHatX) < 117 || PS4.getAnalogHat(RightHatY) > 137 || PS4.getAnalogHat(RightHatY) < 117) { + Serial.print(F("\r\nLeftHatX: ")); + Serial.print(PS4.getAnalogHat(LeftHatX)); + Serial.print(F("\tLeftHatY: ")); + Serial.print(PS4.getAnalogHat(LeftHatY)); + Serial.print(F("\tRightHatX: ")); + Serial.print(PS4.getAnalogHat(RightHatX)); + Serial.print(F("\tRightHatY: ")); + Serial.print(PS4.getAnalogHat(RightHatY)); + } + + if (PS4.getAnalogButton(L2) || PS4.getAnalogButton(R2)) { // These are the only analog buttons on the PS4 controller + Serial.print(F("\r\nL2: ")); + Serial.print(PS4.getAnalogButton(L2)); + Serial.print(F("\tR2: ")); + Serial.print(PS4.getAnalogButton(R2)); + } + if (PS4.getAnalogButton(L2) != oldL2Value || PS4.getAnalogButton(R2) != oldR2Value) // Only write value if it's different + PS4.setRumbleOn(PS4.getAnalogButton(L2), PS4.getAnalogButton(R2)); + oldL2Value = PS4.getAnalogButton(L2); + oldR2Value = PS4.getAnalogButton(R2); + + if (PS4.getButtonClick(PS)) { + Serial.print(F("\r\nPS")); + PS4.disconnect(); + } + else { + if (PS4.getButtonClick(TRIANGLE)) { + Serial.print(F("\r\nTraingle")); + PS4.setRumbleOn(RumbleLow); + } + if (PS4.getButtonClick(CIRCLE)) { + Serial.print(F("\r\nCircle")); + PS4.setRumbleOn(RumbleHigh); + } + if (PS4.getButtonClick(CROSS)) { + Serial.print(F("\r\nCross")); + PS4.setLedFlash(10, 10); // Set it to blink rapidly + } + if (PS4.getButtonClick(SQUARE)) { + Serial.print(F("\r\nSquare")); + PS4.setLedFlash(0, 0); // Turn off blinking + } + + if (PS4.getButtonClick(UP)) { + Serial.print(F("\r\nUp")); + PS4.setLed(Red); + } if (PS4.getButtonClick(RIGHT)) { + Serial.print(F("\r\nRight")); + PS4.setLed(Blue); + } if (PS4.getButtonClick(DOWN)) { + Serial.print(F("\r\nDown")); + PS4.setLed(Yellow); + } if (PS4.getButtonClick(LEFT)) { + Serial.print(F("\r\nLeft")); + PS4.setLed(Green); + } + + if (PS4.getButtonClick(L1)) + Serial.print(F("\r\nL1")); + if (PS4.getButtonClick(L3)) + Serial.print(F("\r\nL3")); + if (PS4.getButtonClick(R1)) + Serial.print(F("\r\nR1")); + if (PS4.getButtonClick(R3)) + Serial.print(F("\r\nR3")); + + if (PS4.getButtonClick(SHARE)) + Serial.print(F("\r\nShare")); + if (PS4.getButtonClick(OPTIONS)) { + Serial.print(F("\r\nOptions")); + printAngle = !printAngle; + } + if (PS4.getButtonClick(TOUCHPAD)) { + Serial.print(F("\r\nTouchpad")); + printTouch = !printTouch; + } + + if (printAngle) { // Print angle calculated using the accelerometer only + Serial.print(F("\r\nPitch: ")); + Serial.print(PS4.getAngle(Pitch)); + Serial.print(F("\tRoll: ")); + Serial.print(PS4.getAngle(Roll)); + } + + if (printTouch) { // Print the x, y coordinates of the touchpad + if (PS4.isTouching(0) || PS4.isTouching(1)) // Print newline and carriage return if any of the fingers are touching the touchpad + Serial.print(F("\r\n")); + for (uint8_t i = 0; i < 2; i++) { // The touchpad track two fingers + if (PS4.isTouching(i)) { // Print the position of the finger if it is touching the touchpad + Serial.print(F("X")); Serial.print(i + 1); Serial.print(F(": ")); + Serial.print(PS4.getX(i)); + Serial.print(F("\tY")); Serial.print(i + 1); Serial.print(F(": ")); + Serial.print(PS4.getY(i)); + Serial.print(F("\t")); + } + } + } + } + } +} diff --git a/libraries/USB_Host_Shield/examples/Bluetooth/SPP/SPP.ino b/libraries/USB_Host_Shield/examples/Bluetooth/SPP/SPP.ino new file mode 100755 index 0000000..fb0dda7 --- /dev/null +++ b/libraries/USB_Host_Shield/examples/Bluetooth/SPP/SPP.ino @@ -0,0 +1,51 @@ +/* + Example sketch for the RFCOMM/SPP Bluetooth library - developed by Kristian Lauszus + For more information visit my blog: http://blog.tkjelectronics.dk/ or + send me an e-mail: kristianl@tkjelectronics.com + */ + +#include +#include +// Satisfy IDE, which only needs to see the include statment in the ino. +#ifdef dobogusinclude +#include +#include +#endif + +USB Usb; +//USBHub Hub1(&Usb); // Some dongles have a hub inside + +BTD Btd(&Usb); // You have to create the Bluetooth Dongle instance like so +/* You can create the instance of the class in two ways */ +SPP SerialBT(&Btd); // This will set the name to the defaults: "Arduino" and the pin to "0000" +//SPP SerialBT(&Btd, "Lauszus's Arduino", "1234"); // You can also set the name and pin like so + +bool firstMessage = true; + +void setup() { + Serial.begin(115200); +#if !defined(__MIPSEL__) + while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection +#endif + if (Usb.Init() == -1) { + Serial.print(F("\r\nOSC did not start")); + while (1); //halt + } + Serial.print(F("\r\nSPP Bluetooth Library Started")); +} +void loop() { + Usb.Task(); // The SPP data is actually not send until this is called, one could call SerialBT.send() directly as well + + if (SerialBT.connected) { + if (firstMessage) { + firstMessage = false; + SerialBT.println(F("Hello from Arduino")); // Send welcome message + } + if (Serial.available()) + SerialBT.write(Serial.read()); + if (SerialBT.available()) + Serial.write(SerialBT.read()); + } + else + firstMessage = true; +} diff --git a/libraries/USB_Host_Shield/examples/Bluetooth/SPPMulti/SPPMulti.ino b/libraries/USB_Host_Shield/examples/Bluetooth/SPPMulti/SPPMulti.ino new file mode 100755 index 0000000..41090fe --- /dev/null +++ b/libraries/USB_Host_Shield/examples/Bluetooth/SPPMulti/SPPMulti.ino @@ -0,0 +1,66 @@ +/* + Example sketch for the RFCOMM/SPP Bluetooth library - developed by Kristian Lauszus + For more information visit my blog: http://blog.tkjelectronics.dk/ or + send me an e-mail: kristianl@tkjelectronics.com + */ + +#include +#include +// Satisfy IDE, which only needs to see the include statment in the ino. +#ifdef dobogusinclude +#include +#include +#endif + +USB Usb; +//USBHub Hub1(&Usb); // Some dongles have a hub inside + +BTD Btd(&Usb); // You have to create the Bluetooth Dongle instance like so + +const uint8_t length = 2; // Set the number of instances here +SPP *SerialBT[length]; // We will use this pointer to store the instances, you can easily make it larger if you like, but it will use a lot of RAM! + +bool firstMessage[length] = { true }; // Set all to true + +void setup() { + for (uint8_t i = 0; i < length; i++) + SerialBT[i] = new SPP(&Btd); // This will set the name to the default: "Arduino" and the pin to "0000" for all connections + + Serial.begin(115200); +#if !defined(__MIPSEL__) + while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection +#endif + if (Usb.Init() == -1) { + Serial.print(F("\r\nOSC did not start")); + while (1); // Halt + } + Serial.print(F("\r\nSPP Bluetooth Library Started")); +} + +void loop() { + Usb.Task(); // The SPP data is actually not send until this is called, one could call SerialBT.send() directly as well + + for (uint8_t i = 0; i < length; i++) { + if (SerialBT[i]->connected) { + if (firstMessage[i]) { + firstMessage[i] = false; + SerialBT[i]->println(F("Hello from Arduino")); // Send welcome message + } + if (SerialBT[i]->available()) + Serial.write(SerialBT[i]->read()); + } + else + firstMessage[i] = true; + } + + // Set the connection you want to send to using the first character + // For instance "0Hello World" would send "Hello World" to connection 0 + if (Serial.available()) { + delay(10); // Wait for the rest of the data to arrive + uint8_t id = Serial.read() - '0'; // Convert from ASCII + if (id < length && SerialBT[id]->connected) { // Make sure that the id is valid and make sure that a device is actually connected + while (Serial.available()) // Check if data is available + SerialBT[id]->write(Serial.read()); // Send the data + } + } +} diff --git a/libraries/USB_Host_Shield/examples/Bluetooth/Wii/Wii.ino b/libraries/USB_Host_Shield/examples/Bluetooth/Wii/Wii.ino new file mode 100755 index 0000000..fb28148 --- /dev/null +++ b/libraries/USB_Host_Shield/examples/Bluetooth/Wii/Wii.ino @@ -0,0 +1,117 @@ +/* + Example sketch for the Wiimote Bluetooth library - developed by Kristian Lauszus + For more information visit my blog: http://blog.tkjelectronics.dk/ or + send me an e-mail: kristianl@tkjelectronics.com + */ + +#include +#include +// Satisfy IDE, which only needs to see the include statment in the ino. +#ifdef dobogusinclude +#include +#include +#endif + +USB Usb; +//USBHub Hub1(&Usb); // Some dongles have a hub inside + +BTD Btd(&Usb); // You have to create the Bluetooth Dongle instance like so +/* You can create the instance of the class in two ways */ +WII Wii(&Btd, PAIR); // This will start an inquiry and then pair with your Wiimote - you only have to do this once +//WII Wii(&Btd); // After that you can simply create the instance like so and then press any button on the Wiimote + +bool printAngle; + +void setup() { + Serial.begin(115200); +#if !defined(__MIPSEL__) + while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection +#endif + if (Usb.Init() == -1) { + Serial.print(F("\r\nOSC did not start")); + while (1); //halt + } + Serial.print(F("\r\nWiimote Bluetooth Library Started")); +} +void loop() { + Usb.Task(); + if (Wii.wiimoteConnected) { + if (Wii.getButtonClick(HOME)) { // You can use getButtonPress to see if the button is held down + Serial.print(F("\r\nHOME")); + Wii.disconnect(); + } + else { + if (Wii.getButtonClick(LEFT)) { + Wii.setLedOff(); + Wii.setLedOn(LED1); + Serial.print(F("\r\nLeft")); + } + if (Wii.getButtonClick(RIGHT)) { + Wii.setLedOff(); + Wii.setLedOn(LED3); + Serial.print(F("\r\nRight")); + } + if (Wii.getButtonClick(DOWN)) { + Wii.setLedOff(); + Wii.setLedOn(LED4); + Serial.print(F("\r\nDown")); + } + if (Wii.getButtonClick(UP)) { + Wii.setLedOff(); + Wii.setLedOn(LED2); + Serial.print(F("\r\nUp")); + } + + if (Wii.getButtonClick(PLUS)) + Serial.print(F("\r\nPlus")); + if (Wii.getButtonClick(MINUS)) + Serial.print(F("\r\nMinus")); + + if (Wii.getButtonClick(ONE)) + Serial.print(F("\r\nOne")); + if (Wii.getButtonClick(TWO)) + Serial.print(F("\r\nTwo")); + + if (Wii.getButtonClick(A)) { + printAngle = !printAngle; + Serial.print(F("\r\nA")); + } + if (Wii.getButtonClick(B)) { + Wii.setRumbleToggle(); + Serial.print(F("\r\nB")); + } + } +#if 0 // Set this to 1 in order to see the angle of the controllers + if (printAngle) { + Serial.print(F("\r\nPitch: ")); + Serial.print(Wii.getPitch()); + Serial.print(F("\tRoll: ")); + Serial.print(Wii.getRoll()); + if (Wii.motionPlusConnected) { + Serial.print(F("\tYaw: ")); + Serial.print(Wii.getYaw()); + } + if (Wii.nunchuckConnected) { + Serial.print(F("\tNunchuck Pitch: ")); + Serial.print(Wii.getNunchuckPitch()); + Serial.print(F("\tNunchuck Roll: ")); + Serial.print(Wii.getNunchuckRoll()); + } + } +#endif + } +#if 0 // Set this to 1 if you are using a Nunchuck controller + if (Wii.nunchuckConnected) { + if (Wii.getButtonClick(Z)) + Serial.print(F("\r\nZ")); + if (Wii.getButtonClick(C)) + Serial.print(F("\r\nC")); + if (Wii.getAnalogHat(HatX) > 137 || Wii.getAnalogHat(HatX) < 117 || Wii.getAnalogHat(HatY) > 137 || Wii.getAnalogHat(HatY) < 117) { + Serial.print(F("\r\nHatX: ")); + Serial.print(Wii.getAnalogHat(HatX)); + Serial.print(F("\tHatY: ")); + Serial.print(Wii.getAnalogHat(HatY)); + } + } +#endif +} diff --git a/libraries/USB_Host_Shield/examples/Bluetooth/WiiIRCamera/WiiIRCamera.ino b/libraries/USB_Host_Shield/examples/Bluetooth/WiiIRCamera/WiiIRCamera.ino new file mode 100755 index 0000000..4eadbc9 --- /dev/null +++ b/libraries/USB_Host_Shield/examples/Bluetooth/WiiIRCamera/WiiIRCamera.ino @@ -0,0 +1,132 @@ +/* +Example sketch for the Wii libary showing the IR camera functionality. This example +is for the Bluetooth Wii library developed for the USB shield from Circuits@Home + +Created by Allan Glover and Kristian Lauszus. +Contact Kristian: http://blog.tkjelectronics.dk/ or send an email at kristianl@tkjelectronics.com. +Contact Allan at adglover9.81@gmail.com + +To test the Wiimote IR camera, you will need access to an IR source. Sunlight will work but is not ideal. +The simpleist solution is to use the Wii sensor bar, i.e. emitter bar, supplied by the Wii system. +Otherwise, wire up a IR LED yourself. +*/ + +#include +#include +// Satisfy IDE, which only needs to see the include statment in the ino. +#ifdef dobogusinclude +#include +#include +#endif + +#ifndef WIICAMERA // Used to check if WIICAMERA is defined +#error "Please set ENABLE_WII_IR_CAMERA to 1 in settings.h" +#endif + +USB Usb; +//USBHub Hub1(&Usb); // Some dongles have a hub inside + +BTD Btd(&Usb); // You have to create the Bluetooth Dongle instance like so +/* You can create the instance of the class in two ways */ +WII Wii(&Btd, PAIR); // This will start an inquiry and then pair with your Wiimote - you only have to do this once +//WII Wii(&Btd); // After the Wiimote pairs once with the line of code above, you can simply create the instance like so and re upload and then press any button on the Wiimote + +bool printAngle; +uint8_t printObjects; + +void setup() { + Serial.begin(115200); +#if !defined(__MIPSEL__) + while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection +#endif + if (Usb.Init() == -1) { + Serial.print(F("\r\nOSC did not start")); + while (1); //halt + } + Serial.print(F("\r\nWiimote Bluetooth Library Started")); +} + +void loop() { + Usb.Task(); + if (Wii.wiimoteConnected) { + if (Wii.getButtonClick(HOME)) { // You can use getButtonPress to see if the button is held down + Serial.print(F("\r\nHOME")); + Wii.disconnect(); + } + else { + if (Wii.getButtonClick(ONE)) + Wii.IRinitialize(); // Run the initialisation sequence + if (Wii.getButtonClick(MINUS) || Wii.getButtonClick(PLUS)) { + if (!Wii.isIRCameraEnabled()) + Serial.print(F("\r\nEnable IR camera first")); + else { + if (Wii.getButtonPress(MINUS)) { // getButtonClick will only return true once + if (printObjects > 0) + printObjects--; + } + else { + if (printObjects < 4) + printObjects++; + } + Serial.print(F("\r\nTracking ")); + Serial.print(printObjects); + Serial.print(F(" objects")); + } + } + if (Wii.getButtonClick(A)) { + printAngle = !printAngle; + Serial.print(F("\r\nA")); + } + if (Wii.getButtonClick(B)) { + Serial.print(F("\r\nBattery level: ")); + Serial.print(Wii.getBatteryLevel()); // You can get the battery level as well + } + } + if (printObjects > 0) { + if (Wii.getIRx1() != 0x3FF || Wii.getIRy1() != 0x3FF || Wii.getIRs1() != 0) { // Only print if the IR camera is actually seeing something + Serial.print(F("\r\nx1: ")); + Serial.print(Wii.getIRx1()); + Serial.print(F("\ty1: ")); + Serial.print(Wii.getIRy1()); + Serial.print(F("\ts1:")); + Serial.print(Wii.getIRs1()); + } + if (printObjects > 1) { + if (Wii.getIRx2() != 0x3FF || Wii.getIRy2() != 0x3FF || Wii.getIRs2() != 0) { + Serial.print(F("\r\nx2: ")); + Serial.print(Wii.getIRx2()); + Serial.print(F("\ty2: ")); + Serial.print(Wii.getIRy2()); + Serial.print(F("\ts2:")); + Serial.print(Wii.getIRs2()); + } + if (printObjects > 2) { + if (Wii.getIRx3() != 0x3FF || Wii.getIRy3() != 0x3FF || Wii.getIRs3() != 0) { + Serial.print(F("\r\nx3: ")); + Serial.print(Wii.getIRx3()); + Serial.print(F("\ty3: ")); + Serial.print(Wii.getIRy3()); + Serial.print(F("\ts3:")); + Serial.print(Wii.getIRs3()); + } + if (printObjects > 3) { + if (Wii.getIRx4() != 0x3FF || Wii.getIRy4() != 0x3FF || Wii.getIRs4() != 0) { + Serial.print(F("\r\nx4: ")); + Serial.print(Wii.getIRx4()); + Serial.print(F("\ty4: ")); + Serial.print(Wii.getIRy4()); + Serial.print(F("\ts4:")); + Serial.print(Wii.getIRs4()); + } + } + } + } + } + if (printAngle) { // There is no extension bytes available, so the MotionPlus or Nunchuck can't be read + Serial.print(F("\r\nPitch: ")); + Serial.print(Wii.getPitch()); + Serial.print(F("\tRoll: ")); + Serial.print(Wii.getRoll()); + } + } +} diff --git a/libraries/USB_Host_Shield/examples/Bluetooth/WiiMulti/WiiMulti.ino b/libraries/USB_Host_Shield/examples/Bluetooth/WiiMulti/WiiMulti.ino new file mode 100755 index 0000000..898d467 --- /dev/null +++ b/libraries/USB_Host_Shield/examples/Bluetooth/WiiMulti/WiiMulti.ino @@ -0,0 +1,131 @@ +/* + Example sketch for the Wiimote Bluetooth library - developed by Kristian Lauszus + This example show how one can use multiple controllers with the library + For more information visit my blog: http://blog.tkjelectronics.dk/ or + send me an e-mail: kristianl@tkjelectronics.com + */ + +#include +#include +// Satisfy IDE, which only needs to see the include statment in the ino. +#ifdef dobogusinclude +#include +#include +#endif + +USB Usb; +//USBHub Hub1(&Usb); // Some dongles have a hub inside + +BTD Btd(&Usb); // You have to create the Bluetooth Dongle instance like so +WII *Wii[2]; // We will use this pointer to store the two instance, you can easily make it larger if you like, but it will use a lot of RAM! +const uint8_t length = sizeof(Wii) / sizeof(Wii[0]); // Get the lenght of the array +bool printAngle[length]; +bool oldControllerState[length]; + +void setup() { + for (uint8_t i = 0; i < length; i++) { + Wii[i] = new WII(&Btd); // You will have to pair each controller with the dongle before you can define the instances like so, just add PAIR as the second argument + Wii[i]->attachOnInit(onInit); // onInit() is called upon a new connection - you can call the function whatever you like + } + + Serial.begin(115200); +#if !defined(__MIPSEL__) + while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection +#endif + if (Usb.Init() == -1) { + Serial.print(F("\r\nOSC did not start")); + while (1); //halt + } + Serial.print(F("\r\nWiimote Bluetooth Library Started")); +} +void loop() { + Usb.Task(); + + for (uint8_t i = 0; i < length; i++) { + if (Wii[i]->wiimoteConnected) { + if (Wii[i]->getButtonClick(HOME)) { // You can use getButtonPress to see if the button is held down + Serial.print(F("\r\nHOME")); + Wii[i]->disconnect(); + oldControllerState[i] = false; // Reset value + } + else { + if (Wii[i]->getButtonClick(LEFT)) { + Wii[i]->setLedOff(); + Wii[i]->setLedOn(LED1); + Serial.print(F("\r\nLeft")); + } + if (Wii[i]->getButtonClick(RIGHT)) { + Wii[i]->setLedOff(); + Wii[i]->setLedOn(LED3); + Serial.print(F("\r\nRight")); + } + if (Wii[i]->getButtonClick(DOWN)) { + Wii[i]->setLedOff(); + Wii[i]->setLedOn(LED4); + Serial.print(F("\r\nDown")); + } + if (Wii[i]->getButtonClick(UP)) { + Wii[i]->setLedOff(); + Wii[i]->setLedOn(LED2); + Serial.print(F("\r\nUp")); + } + + if (Wii[i]->getButtonClick(PLUS)) + Serial.print(F("\r\nPlus")); + if (Wii[i]->getButtonClick(MINUS)) + Serial.print(F("\r\nMinus")); + + if (Wii[i]->getButtonClick(ONE)) + Serial.print(F("\r\nOne")); + if (Wii[i]->getButtonClick(TWO)) + Serial.print(F("\r\nTwo")); + + if (Wii[i]->getButtonClick(A)) { + printAngle[i] = !printAngle[i]; + Serial.print(F("\r\nA")); + } + if (Wii[i]->getButtonClick(B)) { + Wii[i]->setRumbleToggle(); + Serial.print(F("\r\nB")); + } + } + if (printAngle[i]) { + Serial.print(F("\r\nPitch: ")); + Serial.print(Wii[i]->getPitch()); + Serial.print(F("\tRoll: ")); + Serial.print(Wii[i]->getRoll()); + if (Wii[i]->motionPlusConnected) { + Serial.print(F("\tYaw: ")); + Serial.print(Wii[i]->getYaw()); + } + if (Wii[i]->nunchuckConnected) { + Serial.print(F("\tNunchuck Pitch: ")); + Serial.print(Wii[i]->getNunchuckPitch()); + Serial.print(F("\tNunchuck Roll: ")); + Serial.print(Wii[i]->getNunchuckRoll()); + } + } + } + if (Wii[i]->nunchuckConnected) { + if (Wii[i]->getButtonClick(Z)) + Serial.print(F("\r\nZ")); + if (Wii[i]->getButtonClick(C)) + Serial.print(F("\r\nC")); + if (Wii[i]->getAnalogHat(HatX) > 137 || Wii[i]->getAnalogHat(HatX) < 117 || Wii[i]->getAnalogHat(HatY) > 137 || Wii[i]->getAnalogHat(HatY) < 117) { + Serial.print(F("\r\nHatX: ")); + Serial.print(Wii[i]->getAnalogHat(HatX)); + Serial.print(F("\tHatY: ")); + Serial.print(Wii[i]->getAnalogHat(HatY)); + } + } + } +} + +void onInit() { + for (uint8_t i = 0; i < length; i++) { + if (Wii[i]->wiimoteConnected && !oldControllerState[i]) { + oldControllerState[i] = true; // Used to check which is the new controller + Wii[i]->setLedOn((LEDEnum)(i + 1)); // Cast directly to LEDEnum - see: "controllerEnums.h" + } + } +} diff --git a/libraries/USB_Host_Shield/examples/Bluetooth/WiiUProController/WiiUProController.ino b/libraries/USB_Host_Shield/examples/Bluetooth/WiiUProController/WiiUProController.ino new file mode 100755 index 0000000..870cd13 --- /dev/null +++ b/libraries/USB_Host_Shield/examples/Bluetooth/WiiUProController/WiiUProController.ino @@ -0,0 +1,103 @@ +/* + Example sketch for the Wiimote Bluetooth library - developed by Kristian Lauszus + For more information visit my blog: http://blog.tkjelectronics.dk/ or + send me an e-mail: kristianl@tkjelectronics.com + */ + +#include +#include +// Satisfy IDE, which only needs to see the include statment in the ino. +#ifdef dobogusinclude +#include +#include +#endif + +USB Usb; +//USBHub Hub1(&Usb); // Some dongles have a hub inside + +BTD Btd(&Usb); // You have to create the Bluetooth Dongle instance like so +/* You can create the instance of the class in two ways */ +WII Wii(&Btd, PAIR); // This will start an inquiry and then pair with your Wiimote - you only have to do this once +//WII Wii(&Btd); // After that you can simply create the instance like so and then press any button on the Wiimote + +void setup() { + Serial.begin(115200); +#if !defined(__MIPSEL__) + while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection +#endif + if (Usb.Init() == -1) { + Serial.print(F("\r\nOSC did not start")); + while (1); //halt + } + Serial.print(F("\r\nWiimote Bluetooth Library Started")); +} +void loop() { + Usb.Task(); + if (Wii.wiiUProControllerConnected) { + if (Wii.getButtonClick(HOME)) { // You can use getButtonPress to see if the button is held down + Serial.print(F("\r\nHome")); + Wii.disconnect(); + } + else { + if (Wii.getButtonClick(LEFT)) { + Wii.setLedOff(); + Wii.setLedOn(LED1); + Serial.print(F("\r\nLeft")); + } + if (Wii.getButtonClick(RIGHT)) { + Wii.setLedOff(); + Wii.setLedOn(LED3); + Serial.print(F("\r\nRight")); + } + if (Wii.getButtonClick(DOWN)) { + Wii.setLedOff(); + Wii.setLedOn(LED4); + Serial.print(F("\r\nDown")); + } + if (Wii.getButtonClick(UP)) { + Wii.setLedOff(); + Wii.setLedOn(LED2); + Serial.print(F("\r\nUp")); + } + + if (Wii.getButtonClick(PLUS)) + Serial.print(F("\r\nPlus")); + if (Wii.getButtonClick(MINUS)) + Serial.print(F("\r\nMinus")); + + if (Wii.getButtonClick(A)) + Serial.print(F("\r\nA")); + if (Wii.getButtonClick(B)) { + Wii.setRumbleToggle(); + Serial.print(F("\r\nB")); + } + if (Wii.getButtonClick(X)) + Serial.print(F("\r\nX")); + if (Wii.getButtonClick(Y)) + Serial.print(F("\r\nY")); + + if (Wii.getButtonClick(L)) + Serial.print(F("\r\nL")); + if (Wii.getButtonClick(R)) + Serial.print(F("\r\nR")); + if (Wii.getButtonClick(ZL)) + Serial.print(F("\r\nZL")); + if (Wii.getButtonClick(ZR)) + Serial.print(F("\r\nZR")); + if (Wii.getButtonClick(L3)) + Serial.print(F("\r\nL3")); + if (Wii.getButtonClick(R3)) + Serial.print(F("\r\nR3")); + } + if (Wii.getAnalogHat(LeftHatX) > 2200 || Wii.getAnalogHat(LeftHatX) < 1800 || Wii.getAnalogHat(LeftHatY) > 2200 || Wii.getAnalogHat(LeftHatY) < 1800 || Wii.getAnalogHat(RightHatX) > 2200 || Wii.getAnalogHat(RightHatX) < 1800 || Wii.getAnalogHat(RightHatY) > 2200 || Wii.getAnalogHat(RightHatY) < 1800) { + Serial.print(F("\r\nLeftHatX: ")); + Serial.print(Wii.getAnalogHat(LeftHatX)); + Serial.print(F("\tLeftHatY: ")); + Serial.print(Wii.getAnalogHat(LeftHatY)); + Serial.print(F("\tRightHatX: ")); + Serial.print(Wii.getAnalogHat(RightHatX)); + Serial.print(F("\tRightHatY: ")); + Serial.print(Wii.getAnalogHat(RightHatY)); + } + } +} diff --git a/libraries/USB_Host_Shield/examples/HID/USBHIDBootKbd/USBHIDBootKbd.ino b/libraries/USB_Host_Shield/examples/HID/USBHIDBootKbd/USBHIDBootKbd.ino new file mode 100755 index 0000000..16c7c86 --- /dev/null +++ b/libraries/USB_Host_Shield/examples/HID/USBHIDBootKbd/USBHIDBootKbd.ino @@ -0,0 +1,130 @@ +#include +#include +#include +// Satisfy IDE, which only needs to see the include statment in the ino. +#ifdef dobogusinclude +#include +#include +#endif + + +class KbdRptParser : public KeyboardReportParser +{ + void PrintKey(uint8_t mod, uint8_t key); + + protected: + void OnControlKeysChanged(uint8_t before, uint8_t after); + + void OnKeyDown (uint8_t mod, uint8_t key); + void OnKeyUp (uint8_t mod, uint8_t key); + void OnKeyPressed(uint8_t key); +}; + +void KbdRptParser::PrintKey(uint8_t m, uint8_t key) +{ + MODIFIERKEYS mod; + *((uint8_t*)&mod) = m; + Serial.print((mod.bmLeftCtrl == 1) ? "C" : " "); + Serial.print((mod.bmLeftShift == 1) ? "S" : " "); + Serial.print((mod.bmLeftAlt == 1) ? "A" : " "); + Serial.print((mod.bmLeftGUI == 1) ? "G" : " "); + + Serial.print(" >"); + PrintHex(key, 0x80); + Serial.print("< "); + + Serial.print((mod.bmRightCtrl == 1) ? "C" : " "); + Serial.print((mod.bmRightShift == 1) ? "S" : " "); + Serial.print((mod.bmRightAlt == 1) ? "A" : " "); + Serial.println((mod.bmRightGUI == 1) ? "G" : " "); +}; + +void KbdRptParser::OnKeyDown(uint8_t mod, uint8_t key) +{ + Serial.print("DN "); + PrintKey(mod, key); + uint8_t c = OemToAscii(mod, key); + + if (c) + OnKeyPressed(c); +} + +void KbdRptParser::OnControlKeysChanged(uint8_t before, uint8_t after) { + + MODIFIERKEYS beforeMod; + *((uint8_t*)&beforeMod) = before; + + MODIFIERKEYS afterMod; + *((uint8_t*)&afterMod) = after; + + if (beforeMod.bmLeftCtrl != afterMod.bmLeftCtrl) { + Serial.println("LeftCtrl changed"); + } + if (beforeMod.bmLeftShift != afterMod.bmLeftShift) { + Serial.println("LeftShift changed"); + } + if (beforeMod.bmLeftAlt != afterMod.bmLeftAlt) { + Serial.println("LeftAlt changed"); + } + if (beforeMod.bmLeftGUI != afterMod.bmLeftGUI) { + Serial.println("LeftGUI changed"); + } + + if (beforeMod.bmRightCtrl != afterMod.bmRightCtrl) { + Serial.println("RightCtrl changed"); + } + if (beforeMod.bmRightShift != afterMod.bmRightShift) { + Serial.println("RightShift changed"); + } + if (beforeMod.bmRightAlt != afterMod.bmRightAlt) { + Serial.println("RightAlt changed"); + } + if (beforeMod.bmRightGUI != afterMod.bmRightGUI) { + Serial.println("RightGUI changed"); + } + +} + +void KbdRptParser::OnKeyUp(uint8_t mod, uint8_t key) +{ + Serial.print("UP "); + PrintKey(mod, key); +} + +void KbdRptParser::OnKeyPressed(uint8_t key) +{ + Serial.print("ASCII: "); + Serial.println((char)key); +}; + +USB Usb; +//USBHub Hub(&Usb); +HIDBoot HidKeyboard(&Usb); + +uint32_t next_time; + +KbdRptParser Prs; + +void setup() +{ + Serial.begin( 115200 ); +#if !defined(__MIPSEL__) + while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection +#endif + Serial.println("Start"); + + if (Usb.Init() == -1) + Serial.println("OSC did not start."); + + delay( 200 ); + + next_time = millis() + 5000; + + HidKeyboard.SetReportParser(0, (HIDReportParser*)&Prs); +} + +void loop() +{ + Usb.Task(); +} + diff --git a/libraries/USB_Host_Shield/examples/HID/USBHIDBootKbdAndMouse/USBHIDBootKbdAndMouse.ino b/libraries/USB_Host_Shield/examples/HID/USBHIDBootKbdAndMouse/USBHIDBootKbdAndMouse.ino new file mode 100755 index 0000000..848dac8 --- /dev/null +++ b/libraries/USB_Host_Shield/examples/HID/USBHIDBootKbdAndMouse/USBHIDBootKbdAndMouse.ino @@ -0,0 +1,177 @@ +#include +#include +// Satisfy IDE, which only needs to see the include statment in the ino. +#ifdef dobogusinclude +#include +#include +#endif + +class MouseRptParser : public MouseReportParser +{ + protected: + void OnMouseMove(MOUSEINFO *mi); + void OnLeftButtonUp(MOUSEINFO *mi); + void OnLeftButtonDown(MOUSEINFO *mi); + void OnRightButtonUp(MOUSEINFO *mi); + void OnRightButtonDown(MOUSEINFO *mi); + void OnMiddleButtonUp(MOUSEINFO *mi); + void OnMiddleButtonDown(MOUSEINFO *mi); +}; +void MouseRptParser::OnMouseMove(MOUSEINFO *mi) +{ + Serial.print("dx="); + Serial.print(mi->dX, DEC); + Serial.print(" dy="); + Serial.println(mi->dY, DEC); +}; +void MouseRptParser::OnLeftButtonUp (MOUSEINFO *mi) +{ + Serial.println("L Butt Up"); +}; +void MouseRptParser::OnLeftButtonDown (MOUSEINFO *mi) +{ + Serial.println("L Butt Dn"); +}; +void MouseRptParser::OnRightButtonUp (MOUSEINFO *mi) +{ + Serial.println("R Butt Up"); +}; +void MouseRptParser::OnRightButtonDown (MOUSEINFO *mi) +{ + Serial.println("R Butt Dn"); +}; +void MouseRptParser::OnMiddleButtonUp (MOUSEINFO *mi) +{ + Serial.println("M Butt Up"); +}; +void MouseRptParser::OnMiddleButtonDown (MOUSEINFO *mi) +{ + Serial.println("M Butt Dn"); +}; + +class KbdRptParser : public KeyboardReportParser +{ + void PrintKey(uint8_t mod, uint8_t key); + + protected: + void OnControlKeysChanged(uint8_t before, uint8_t after); + void OnKeyDown (uint8_t mod, uint8_t key); + void OnKeyUp (uint8_t mod, uint8_t key); + void OnKeyPressed(uint8_t key); +}; + +void KbdRptParser::PrintKey(uint8_t m, uint8_t key) +{ + MODIFIERKEYS mod; + *((uint8_t*)&mod) = m; + Serial.print((mod.bmLeftCtrl == 1) ? "C" : " "); + Serial.print((mod.bmLeftShift == 1) ? "S" : " "); + Serial.print((mod.bmLeftAlt == 1) ? "A" : " "); + Serial.print((mod.bmLeftGUI == 1) ? "G" : " "); + + Serial.print(" >"); + PrintHex(key, 0x80); + Serial.print("< "); + + Serial.print((mod.bmRightCtrl == 1) ? "C" : " "); + Serial.print((mod.bmRightShift == 1) ? "S" : " "); + Serial.print((mod.bmRightAlt == 1) ? "A" : " "); + Serial.println((mod.bmRightGUI == 1) ? "G" : " "); +}; + +void KbdRptParser::OnKeyDown(uint8_t mod, uint8_t key) +{ + Serial.print("DN "); + PrintKey(mod, key); + uint8_t c = OemToAscii(mod, key); + + if (c) + OnKeyPressed(c); +} + +void KbdRptParser::OnControlKeysChanged(uint8_t before, uint8_t after) { + + MODIFIERKEYS beforeMod; + *((uint8_t*)&beforeMod) = before; + + MODIFIERKEYS afterMod; + *((uint8_t*)&afterMod) = after; + + if (beforeMod.bmLeftCtrl != afterMod.bmLeftCtrl) { + Serial.println("LeftCtrl changed"); + } + if (beforeMod.bmLeftShift != afterMod.bmLeftShift) { + Serial.println("LeftShift changed"); + } + if (beforeMod.bmLeftAlt != afterMod.bmLeftAlt) { + Serial.println("LeftAlt changed"); + } + if (beforeMod.bmLeftGUI != afterMod.bmLeftGUI) { + Serial.println("LeftGUI changed"); + } + + if (beforeMod.bmRightCtrl != afterMod.bmRightCtrl) { + Serial.println("RightCtrl changed"); + } + if (beforeMod.bmRightShift != afterMod.bmRightShift) { + Serial.println("RightShift changed"); + } + if (beforeMod.bmRightAlt != afterMod.bmRightAlt) { + Serial.println("RightAlt changed"); + } + if (beforeMod.bmRightGUI != afterMod.bmRightGUI) { + Serial.println("RightGUI changed"); + } + +} + +void KbdRptParser::OnKeyUp(uint8_t mod, uint8_t key) +{ + Serial.print("UP "); + PrintKey(mod, key); +} + +void KbdRptParser::OnKeyPressed(uint8_t key) +{ + Serial.print("ASCII: "); + Serial.println((char)key); +}; + +USB Usb; +USBHub Hub(&Usb); + +HIDBoot < HID_PROTOCOL_KEYBOARD | HID_PROTOCOL_MOUSE > HidComposite(&Usb); +HIDBoot HidKeyboard(&Usb); +HIDBoot HidMouse(&Usb); + +//uint32_t next_time; + +KbdRptParser KbdPrs; +MouseRptParser MousePrs; + +void setup() +{ + Serial.begin( 115200 ); +#if !defined(__MIPSEL__) + while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection +#endif + Serial.println("Start"); + + if (Usb.Init() == -1) + Serial.println("OSC did not start."); + + delay( 200 ); + + //next_time = millis() + 5000; + + HidComposite.SetReportParser(0, (HIDReportParser*)&KbdPrs); + HidComposite.SetReportParser(1, (HIDReportParser*)&MousePrs); + HidKeyboard.SetReportParser(0, (HIDReportParser*)&KbdPrs); + HidMouse.SetReportParser(0, (HIDReportParser*)&MousePrs); +} + +void loop() +{ + Usb.Task(); +} + diff --git a/libraries/USB_Host_Shield/examples/HID/USBHIDBootMouse/USBHIDBootMouse.ino b/libraries/USB_Host_Shield/examples/HID/USBHIDBootMouse/USBHIDBootMouse.ino new file mode 100755 index 0000000..ede71bd --- /dev/null +++ b/libraries/USB_Host_Shield/examples/HID/USBHIDBootMouse/USBHIDBootMouse.ino @@ -0,0 +1,82 @@ +#include +#include +// Satisfy IDE, which only needs to see the include statment in the ino. +#ifdef dobogusinclude +#include +#include +#endif + +class MouseRptParser : public MouseReportParser +{ +protected: + void OnMouseMove (MOUSEINFO *mi); + void OnLeftButtonUp (MOUSEINFO *mi); + void OnLeftButtonDown (MOUSEINFO *mi); + void OnRightButtonUp (MOUSEINFO *mi); + void OnRightButtonDown (MOUSEINFO *mi); + void OnMiddleButtonUp (MOUSEINFO *mi); + void OnMiddleButtonDown (MOUSEINFO *mi); +}; +void MouseRptParser::OnMouseMove(MOUSEINFO *mi) +{ + Serial.print("dx="); + Serial.print(mi->dX, DEC); + Serial.print(" dy="); + Serial.println(mi->dY, DEC); +}; +void MouseRptParser::OnLeftButtonUp (MOUSEINFO *mi) +{ + Serial.println("L Butt Up"); +}; +void MouseRptParser::OnLeftButtonDown (MOUSEINFO *mi) +{ + Serial.println("L Butt Dn"); +}; +void MouseRptParser::OnRightButtonUp (MOUSEINFO *mi) +{ + Serial.println("R Butt Up"); +}; +void MouseRptParser::OnRightButtonDown (MOUSEINFO *mi) +{ + Serial.println("R Butt Dn"); +}; +void MouseRptParser::OnMiddleButtonUp (MOUSEINFO *mi) +{ + Serial.println("M Butt Up"); +}; +void MouseRptParser::OnMiddleButtonDown (MOUSEINFO *mi) +{ + Serial.println("M Butt Dn"); +}; + +USB Usb; +USBHub Hub(&Usb); +HIDBoot HidMouse(&Usb); + +uint32_t next_time; + +MouseRptParser Prs; + +void setup() +{ + Serial.begin( 115200 ); +#if !defined(__MIPSEL__) + while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection +#endif + Serial.println("Start"); + + if (Usb.Init() == -1) + Serial.println("OSC did not start."); + + delay( 200 ); + + next_time = millis() + 5000; + + HidMouse.SetReportParser(0,(HIDReportParser*)&Prs); +} + +void loop() +{ + Usb.Task(); +} + diff --git a/libraries/USB_Host_Shield/examples/HID/USBHIDJoystick/USBHIDJoystick.ino b/libraries/USB_Host_Shield/examples/HID/USBHIDJoystick/USBHIDJoystick.ino new file mode 100755 index 0000000..70e5dc6 --- /dev/null +++ b/libraries/USB_Host_Shield/examples/HID/USBHIDJoystick/USBHIDJoystick.ino @@ -0,0 +1,37 @@ +#include +#include +#include +// Satisfy IDE, which only needs to see the include statment in the ino. +#ifdef dobogusinclude +#include +#include +#endif + +#include "hidjoystickrptparser.h" + +USB Usb; +USBHub Hub(&Usb); +HIDUniversal Hid(&Usb); +JoystickEvents JoyEvents; +JoystickReportParser Joy(&JoyEvents); + +void setup() { + Serial.begin(115200); +#if !defined(__MIPSEL__) + while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection +#endif + Serial.println("Start"); + + if (Usb.Init() == -1) + Serial.println("OSC did not start."); + + delay(200); + + if (!Hid.SetReportParser(0, &Joy)) + ErrorMessage (PSTR("SetReportParser"), 1); +} + +void loop() { + Usb.Task(); +} + diff --git a/libraries/USB_Host_Shield/examples/HID/USBHIDJoystick/hidjoystickrptparser.cpp b/libraries/USB_Host_Shield/examples/HID/USBHIDJoystick/hidjoystickrptparser.cpp new file mode 100755 index 0000000..083b95c --- /dev/null +++ b/libraries/USB_Host_Shield/examples/HID/USBHIDJoystick/hidjoystickrptparser.cpp @@ -0,0 +1,84 @@ +#include "hidjoystickrptparser.h" + +JoystickReportParser::JoystickReportParser(JoystickEvents *evt) : +joyEvents(evt), +oldHat(0xDE), +oldButtons(0) { + for (uint8_t i = 0; i < RPT_GEMEPAD_LEN; i++) + oldPad[i] = 0xD; +} + +void JoystickReportParser::Parse(HID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf) { + bool match = true; + + // Checking if there are changes in report since the method was last called + for (uint8_t i = 0; i < RPT_GEMEPAD_LEN; i++) + if (buf[i] != oldPad[i]) { + match = false; + break; + } + + // Calling Game Pad event handler + if (!match && joyEvents) { + joyEvents->OnGamePadChanged((const GamePadEventData*)buf); + + for (uint8_t i = 0; i < RPT_GEMEPAD_LEN; i++) oldPad[i] = buf[i]; + } + + uint8_t hat = (buf[5] & 0xF); + + // Calling Hat Switch event handler + if (hat != oldHat && joyEvents) { + joyEvents->OnHatSwitch(hat); + oldHat = hat; + } + + uint16_t buttons = (0x0000 | buf[6]); + buttons <<= 4; + buttons |= (buf[5] >> 4); + uint16_t changes = (buttons ^ oldButtons); + + // Calling Button Event Handler for every button changed + if (changes) { + for (uint8_t i = 0; i < 0x0C; i++) { + uint16_t mask = (0x0001 << i); + + if (((mask & changes) > 0) && joyEvents) + if ((buttons & mask) > 0) + joyEvents->OnButtonDn(i + 1); + else + joyEvents->OnButtonUp(i + 1); + } + oldButtons = buttons; + } +} + +void JoystickEvents::OnGamePadChanged(const GamePadEventData *evt) { + Serial.print("X1: "); + PrintHex (evt->X, 0x80); + Serial.print("\tY1: "); + PrintHex (evt->Y, 0x80); + Serial.print("\tX2: "); + PrintHex (evt->Z1, 0x80); + Serial.print("\tY2: "); + PrintHex (evt->Z2, 0x80); + Serial.print("\tRz: "); + PrintHex (evt->Rz, 0x80); + Serial.println(""); +} + +void JoystickEvents::OnHatSwitch(uint8_t hat) { + Serial.print("Hat Switch: "); + PrintHex (hat, 0x80); + Serial.println(""); +} + +void JoystickEvents::OnButtonUp(uint8_t but_id) { + Serial.print("Up: "); + Serial.println(but_id, DEC); +} + +void JoystickEvents::OnButtonDn(uint8_t but_id) { + Serial.print("Dn: "); + Serial.println(but_id, DEC); +} diff --git a/libraries/USB_Host_Shield/examples/HID/USBHIDJoystick/hidjoystickrptparser.h b/libraries/USB_Host_Shield/examples/HID/USBHIDJoystick/hidjoystickrptparser.h new file mode 100755 index 0000000..733b8f8 --- /dev/null +++ b/libraries/USB_Host_Shield/examples/HID/USBHIDJoystick/hidjoystickrptparser.h @@ -0,0 +1,33 @@ +#if !defined(__HIDJOYSTICKRPTPARSER_H__) +#define __HIDJOYSTICKRPTPARSER_H__ + +#include + +struct GamePadEventData { + uint8_t X, Y, Z1, Z2, Rz; +}; + +class JoystickEvents { +public: + virtual void OnGamePadChanged(const GamePadEventData *evt); + virtual void OnHatSwitch(uint8_t hat); + virtual void OnButtonUp(uint8_t but_id); + virtual void OnButtonDn(uint8_t but_id); +}; + +#define RPT_GEMEPAD_LEN 5 + +class JoystickReportParser : public HIDReportParser { + JoystickEvents *joyEvents; + + uint8_t oldPad[RPT_GEMEPAD_LEN]; + uint8_t oldHat; + uint16_t oldButtons; + +public: + JoystickReportParser(JoystickEvents *evt); + + virtual void Parse(HID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf); +}; + +#endif // __HIDJOYSTICKRPTPARSER_H__ diff --git a/libraries/USB_Host_Shield/examples/HID/USBHID_desc/USBHID_desc.ino b/libraries/USB_Host_Shield/examples/HID/USBHID_desc/USBHID_desc.ino new file mode 100755 index 0000000..a3056ae --- /dev/null +++ b/libraries/USB_Host_Shield/examples/HID/USBHID_desc/USBHID_desc.ino @@ -0,0 +1,76 @@ +#include +#include +#include +#include +#include "pgmstrings.h" +// Satisfy IDE, which only needs to see the include statment in the ino. +#ifdef dobogusinclude +#include +#include +#endif + +class HIDUniversal2 : public HIDUniversal +{ +public: + HIDUniversal2(USB *usb) : HIDUniversal(usb) {}; + +protected: + uint8_t OnInitSuccessful(); +}; + +uint8_t HIDUniversal2::OnInitSuccessful() +{ + uint8_t rcode; + + HexDumper Hex; + ReportDescParser Rpt; + + if ((rcode = GetReportDescr(0, &Hex))) + goto FailGetReportDescr1; + + if ((rcode = GetReportDescr(0, &Rpt))) + goto FailGetReportDescr2; + + return 0; + +FailGetReportDescr1: + USBTRACE("GetReportDescr1:"); + goto Fail; + +FailGetReportDescr2: + USBTRACE("GetReportDescr2:"); + goto Fail; + +Fail: + Serial.println(rcode, HEX); + Release(); + return rcode; +} + +USB Usb; +//USBHub Hub(&Usb); +HIDUniversal2 Hid(&Usb); +UniversalReportParser Uni; + +void setup() +{ + Serial.begin( 115200 ); +#if !defined(__MIPSEL__) + while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection +#endif + Serial.println("Start"); + + if (Usb.Init() == -1) + Serial.println("OSC did not start."); + + delay( 200 ); + + if (!Hid.SetReportParser(0, &Uni)) + ErrorMessage(PSTR("SetReportParser"), 1 ); +} + +void loop() +{ + Usb.Task(); +} + diff --git a/libraries/USB_Host_Shield/examples/HID/USBHID_desc/pgmstrings.h b/libraries/USB_Host_Shield/examples/HID/USBHID_desc/pgmstrings.h new file mode 100755 index 0000000..bdb0077 --- /dev/null +++ b/libraries/USB_Host_Shield/examples/HID/USBHID_desc/pgmstrings.h @@ -0,0 +1,52 @@ +#if !defined(__PGMSTRINGS_H__) +#define __PGMSTRINGS_H__ + +#define LOBYTE(x) ((char*)(&(x)))[0] +#define HIBYTE(x) ((char*)(&(x)))[1] +#define BUFSIZE 256 //buffer size + + +/* Print strings in Program Memory */ +const char Gen_Error_str[] PROGMEM = "\r\nRequest error. Error code:\t"; +const char Dev_Header_str[] PROGMEM ="\r\nDevice descriptor: "; +const char Dev_Length_str[] PROGMEM ="\r\nDescriptor Length:\t"; +const char Dev_Type_str[] PROGMEM ="\r\nDescriptor type:\t"; +const char Dev_Version_str[] PROGMEM ="\r\nUSB version:\t\t"; +const char Dev_Class_str[] PROGMEM ="\r\nDevice class:\t\t"; +const char Dev_Subclass_str[] PROGMEM ="\r\nDevice Subclass:\t"; +const char Dev_Protocol_str[] PROGMEM ="\r\nDevice Protocol:\t"; +const char Dev_Pktsize_str[] PROGMEM ="\r\nMax.packet size:\t"; +const char Dev_Vendor_str[] PROGMEM ="\r\nVendor ID:\t\t"; +const char Dev_Product_str[] PROGMEM ="\r\nProduct ID:\t\t"; +const char Dev_Revision_str[] PROGMEM ="\r\nRevision ID:\t\t"; +const char Dev_Mfg_str[] PROGMEM ="\r\nMfg.string index:\t"; +const char Dev_Prod_str[] PROGMEM ="\r\nProd.string index:\t"; +const char Dev_Serial_str[] PROGMEM ="\r\nSerial number index:\t"; +const char Dev_Nconf_str[] PROGMEM ="\r\nNumber of conf.:\t"; +const char Conf_Trunc_str[] PROGMEM ="Total length truncated to 256 bytes"; +const char Conf_Header_str[] PROGMEM ="\r\nConfiguration descriptor:"; +const char Conf_Totlen_str[] PROGMEM ="\r\nTotal length:\t\t"; +const char Conf_Nint_str[] PROGMEM ="\r\nNum.intf:\t\t"; +const char Conf_Value_str[] PROGMEM ="\r\nConf.value:\t\t"; +const char Conf_String_str[] PROGMEM ="\r\nConf.string:\t\t"; +const char Conf_Attr_str[] PROGMEM ="\r\nAttr.:\t\t\t"; +const char Conf_Pwr_str[] PROGMEM ="\r\nMax.pwr:\t\t"; +const char Int_Header_str[] PROGMEM ="\r\n\r\nInterface descriptor:"; +const char Int_Number_str[] PROGMEM ="\r\nIntf.number:\t\t"; +const char Int_Alt_str[] PROGMEM ="\r\nAlt.:\t\t\t"; +const char Int_Endpoints_str[] PROGMEM ="\r\nEndpoints:\t\t"; +const char Int_Class_str[] PROGMEM ="\r\nIntf. Class:\t\t"; +const char Int_Subclass_str[] PROGMEM ="\r\nIntf. Subclass:\t\t"; +const char Int_Protocol_str[] PROGMEM ="\r\nIntf. Protocol:\t\t"; +const char Int_String_str[] PROGMEM ="\r\nIntf.string:\t\t"; +const char End_Header_str[] PROGMEM ="\r\n\r\nEndpoint descriptor:"; +const char End_Address_str[] PROGMEM ="\r\nEndpoint address:\t"; +const char End_Attr_str[] PROGMEM ="\r\nAttr.:\t\t\t"; +const char End_Pktsize_str[] PROGMEM ="\r\nMax.pkt size:\t\t"; +const char End_Interval_str[] PROGMEM ="\r\nPolling interval:\t"; +const char Unk_Header_str[] PROGMEM = "\r\nUnknown descriptor:"; +const char Unk_Length_str[] PROGMEM ="\r\nLength:\t\t"; +const char Unk_Type_str[] PROGMEM ="\r\nType:\t\t"; +const char Unk_Contents_str[] PROGMEM ="\r\nContents:\t"; + +#endif // __PGMSTRINGS_H__ \ No newline at end of file diff --git a/libraries/USB_Host_Shield/examples/HID/le3dp/le3dp.ino b/libraries/USB_Host_Shield/examples/HID/le3dp/le3dp.ino new file mode 100755 index 0000000..df9cbf1 --- /dev/null +++ b/libraries/USB_Host_Shield/examples/HID/le3dp/le3dp.ino @@ -0,0 +1,41 @@ +/* Simplified Logitech Extreme 3D Pro Joystick Report Parser */ + +#include +#include +#include + +#include "le3dp_rptparser.h" +// Satisfy IDE, which only needs to see the include statment in the ino. +#ifdef dobogusinclude +#include +#include +#endif + +USB Usb; +USBHub Hub(&Usb); +HIDUniversal Hid(&Usb); +JoystickEvents JoyEvents; +JoystickReportParser Joy(&JoyEvents); + +void setup() +{ + Serial.begin( 115200 ); +#if !defined(__MIPSEL__) + while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection +#endif + Serial.println("Start"); + + if (Usb.Init() == -1) + Serial.println("OSC did not start."); + + delay( 200 ); + + if (!Hid.SetReportParser(0, &Joy)) + ErrorMessage(PSTR("SetReportParser"), 1 ); +} + +void loop() +{ + Usb.Task(); +} + diff --git a/libraries/USB_Host_Shield/examples/HID/le3dp/le3dp_rptparser.cpp b/libraries/USB_Host_Shield/examples/HID/le3dp/le3dp_rptparser.cpp new file mode 100755 index 0000000..baece13 --- /dev/null +++ b/libraries/USB_Host_Shield/examples/HID/le3dp/le3dp_rptparser.cpp @@ -0,0 +1,43 @@ +#include "le3dp_rptparser.h" + +JoystickReportParser::JoystickReportParser(JoystickEvents *evt) : + joyEvents(evt) +{} + +void JoystickReportParser::Parse(HID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf) +{ + bool match = true; + + // Checking if there are changes in report since the method was last called + for (uint8_t i=0; iOnGamePadChanged((const GamePadEventData*)buf); + + for (uint8_t i=0; i(evt->x, 0x80); + Serial.print(" Y: "); + PrintHex(evt->y, 0x80); + Serial.print(" Hat Switch: "); + PrintHex(evt->hat, 0x80); + Serial.print(" Twist: "); + PrintHex(evt->twist, 0x80); + Serial.print(" Slider: "); + PrintHex(evt->slider, 0x80); + Serial.print(" Buttons A: "); + PrintHex(evt->buttons_a, 0x80); + Serial.print(" Buttons B: "); + PrintHex(evt->buttons_b, 0x80); + Serial.println(""); +} diff --git a/libraries/USB_Host_Shield/examples/HID/le3dp/le3dp_rptparser.h b/libraries/USB_Host_Shield/examples/HID/le3dp/le3dp_rptparser.h new file mode 100755 index 0000000..2400364 --- /dev/null +++ b/libraries/USB_Host_Shield/examples/HID/le3dp/le3dp_rptparser.h @@ -0,0 +1,42 @@ +#if !defined(__HIDJOYSTICKRPTPARSER_H__) +#define __HIDJOYSTICKRPTPARSER_H__ + +#include + +struct GamePadEventData +{ + union { //axes and hut switch + uint32_t axes; + struct { + uint32_t x : 10; + uint32_t y : 10; + uint32_t hat : 4; + uint32_t twist : 8; + }; + }; + uint8_t buttons_a; + uint8_t slider; + uint8_t buttons_b; +}; + +class JoystickEvents +{ +public: + virtual void OnGamePadChanged(const GamePadEventData *evt); +}; + +#define RPT_GAMEPAD_LEN sizeof(GamePadEventData)/sizeof(uint8_t) + +class JoystickReportParser : public HIDReportParser +{ + JoystickEvents *joyEvents; + + uint8_t oldPad[RPT_GAMEPAD_LEN]; + +public: + JoystickReportParser(JoystickEvents *evt); + + virtual void Parse(HID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf); +}; + +#endif // __HIDJOYSTICKRPTPARSER_H__ diff --git a/libraries/USB_Host_Shield/examples/HID/scale/scale.ino b/libraries/USB_Host_Shield/examples/HID/scale/scale.ino new file mode 100755 index 0000000..e335aef --- /dev/null +++ b/libraries/USB_Host_Shield/examples/HID/scale/scale.ino @@ -0,0 +1,50 @@ +/* Digital Scale Output. Written for Stamps.com Model 510 */ +/* 5lb Digital Scale; any HID scale with Usage page 0x8d should work */ + +#include +#include +#include + +#include "scale_rptparser.h" +// Satisfy IDE, which only needs to see the include statment in the ino. +#ifdef dobogusinclude +#include +#include +#endif + +USB Usb; +USBHub Hub(&Usb); +HIDUniversal Hid(&Usb); +Max_LCD LCD(&Usb); +ScaleEvents ScaleEvents(&LCD); +ScaleReportParser Scale(&ScaleEvents); + +void setup() +{ + Serial.begin( 115200 ); +#if !defined(__MIPSEL__) + while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection +#endif + Serial.println("Start"); + + if (Usb.Init() == -1) + Serial.println("OSC did not start."); + + // set up the LCD's number of rows and columns: + LCD.begin(16, 2); + LCD.clear(); + LCD.home(); + LCD.setCursor(0,0); + LCD.write('R'); + + delay( 200 ); + + if (!Hid.SetReportParser(0, &Scale)) + ErrorMessage(PSTR("SetReportParser"), 1 ); +} + +void loop() +{ + Usb.Task(); +} + diff --git a/libraries/USB_Host_Shield/examples/HID/scale/scale_rptparser.cpp b/libraries/USB_Host_Shield/examples/HID/scale/scale_rptparser.cpp new file mode 100755 index 0000000..01ed980 --- /dev/null +++ b/libraries/USB_Host_Shield/examples/HID/scale/scale_rptparser.cpp @@ -0,0 +1,150 @@ +/* Parser for standard HID scale (usage page 0x8d) data input report (ID 3) */ +#include "scale_rptparser.h" + +const char* UNITS[13] = { + "units", // unknown unit + "mg", // milligram + "g", // gram + "kg", // kilogram + "cd", // carat + "taels", // lian + "gr", // grain + "dwt", // pennyweight + "tonnes", // metric tons + "tons", // avoir ton + "ozt", // troy ounce + "oz", // ounce + "lbs" // pound +}; + +ScaleReportParser::ScaleReportParser(ScaleEvents *evt) : + scaleEvents(evt) +{} + +void ScaleReportParser::Parse(HID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf) +{ + bool match = true; + + // Checking if there are changes in report since the method was last called + for (uint8_t i=0; iOnScaleChanged((const ScaleEventData*)buf); + + for (uint8_t i=0; iwrite( *str++ ); + + } +} + +void ScaleEvents::OnScaleChanged(const ScaleEventData *evt) +{ + + pLcd->clear(); + pLcd->home(); + pLcd->setCursor(0,0); + + if( evt->reportID != 3 ) { + + const char inv_report[]="Invalid report!"; + + Serial.println(inv_report); + LcdPrint(inv_report); + + return; + + }//if( evt->reportID != 3... + + switch( evt->status ) { + + case REPORT_FAULT: + Serial.println(F("Report fault")); + break; + + case ZEROED: + Serial.println(F("Scale zero set")); + break; + + case WEIGHING: { + + const char progress[] = "Weighing..."; + Serial.println(progress); + LcdPrint(progress); + break; + } + + case WEIGHT_VALID: { + + char buf[10]; + double weight = evt->weight * pow( 10, evt->exp ); + + + + Serial.print(F("Weight: ")); + Serial.print( weight ); + Serial.print(F(" ")); + Serial.println( UNITS[ evt->unit ]); + + LcdPrint("Weight: "); + dtostrf( weight, 4, 2, buf ); + LcdPrint( buf ); + LcdPrint( UNITS[ evt->unit ]); + + break; + + }//case WEIGHT_VALID... + + case WEIGHT_NEGATIVE: { + + const char negweight[] = "Negative weight"; + Serial.println(negweight); + LcdPrint(negweight); + break; + } + + case OVERWEIGHT: { + + const char overweight[] = "Max.weight reached"; + Serial.println(overweight); + LcdPrint( overweight ); + break; + } + + case CALIBRATE_ME: + + Serial.println(F("Scale calibration required")); + break; + + case ZERO_ME: + + Serial.println(F("Scale zeroing required")); + break; + + default: + + Serial.print(F("Undefined status code: ")); + Serial.println( evt->status ); + break; + + }//switch( evt->status... + +} diff --git a/libraries/USB_Host_Shield/examples/HID/scale/scale_rptparser.h b/libraries/USB_Host_Shield/examples/HID/scale/scale_rptparser.h new file mode 100755 index 0000000..57fbb03 --- /dev/null +++ b/libraries/USB_Host_Shield/examples/HID/scale/scale_rptparser.h @@ -0,0 +1,55 @@ +#if !defined(__SCALERPTPARSER_H__) +#define __SCALERPTPARSER_H__ + +#include +#include + +/* Scale status constants */ +#define REPORT_FAULT 0x01 +#define ZEROED 0x02 +#define WEIGHING 0x03 +#define WEIGHT_VALID 0x04 +#define WEIGHT_NEGATIVE 0x05 +#define OVERWEIGHT 0x06 +#define CALIBRATE_ME 0x07 +#define ZERO_ME 0x08 + +/* input data report */ +struct ScaleEventData +{ + uint8_t reportID; //must be 3 + uint8_t status; + uint8_t unit; + int8_t exp; //scale factor for the weight + uint16_t weight; // +}; + +class ScaleEvents +{ + + Max_LCD* pLcd; + + void LcdPrint( const char* str ); + +public: + + ScaleEvents( Max_LCD* pLCD ); + + virtual void OnScaleChanged(const ScaleEventData *evt); +}; + +#define RPT_SCALE_LEN sizeof(ScaleEventData)/sizeof(uint8_t) + +class ScaleReportParser : public HIDReportParser +{ + ScaleEvents *scaleEvents; + + uint8_t oldScale[RPT_SCALE_LEN]; + +public: + ScaleReportParser(ScaleEvents *evt); + + virtual void Parse(HID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf); +}; + +#endif // __SCALERPTPARSER_H__ diff --git a/libraries/USB_Host_Shield/examples/PS3USB/PS3USB.ino b/libraries/USB_Host_Shield/examples/PS3USB/PS3USB.ino new file mode 100755 index 0000000..ca5684d --- /dev/null +++ b/libraries/USB_Host_Shield/examples/PS3USB/PS3USB.ino @@ -0,0 +1,147 @@ +/* + Example sketch for the PS3 USB library - developed by Kristian Lauszus + For more information visit my blog: http://blog.tkjelectronics.dk/ or + send me an e-mail: kristianl@tkjelectronics.com + */ + +#include +// Satisfy IDE, which only needs to see the include statment in the ino. +#ifdef dobogusinclude +#include +#include +#endif + +USB Usb; +/* You can create the instance of the class in two ways */ +PS3USB PS3(&Usb); // This will just create the instance +//PS3USB PS3(&Usb,0x00,0x15,0x83,0x3D,0x0A,0x57); // This will also store the bluetooth address - this can be obtained from the dongle when running the sketch + +bool printAngle; +uint8_t state = 0; + +void setup() { + Serial.begin(115200); +#if !defined(__MIPSEL__) + while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection +#endif + if (Usb.Init() == -1) { + Serial.print(F("\r\nOSC did not start")); + while (1); //halt + } + Serial.print(F("\r\nPS3 USB Library Started")); +} +void loop() { + Usb.Task(); + + if (PS3.PS3Connected || PS3.PS3NavigationConnected) { + if (PS3.getAnalogHat(LeftHatX) > 137 || PS3.getAnalogHat(LeftHatX) < 117 || PS3.getAnalogHat(LeftHatY) > 137 || PS3.getAnalogHat(LeftHatY) < 117 || PS3.getAnalogHat(RightHatX) > 137 || PS3.getAnalogHat(RightHatX) < 117 || PS3.getAnalogHat(RightHatY) > 137 || PS3.getAnalogHat(RightHatY) < 117) { + Serial.print(F("\r\nLeftHatX: ")); + Serial.print(PS3.getAnalogHat(LeftHatX)); + Serial.print(F("\tLeftHatY: ")); + Serial.print(PS3.getAnalogHat(LeftHatY)); + if (PS3.PS3Connected) { // The Navigation controller only have one joystick + Serial.print(F("\tRightHatX: ")); + Serial.print(PS3.getAnalogHat(RightHatX)); + Serial.print(F("\tRightHatY: ")); + Serial.print(PS3.getAnalogHat(RightHatY)); + } + } + // Analog button values can be read from almost all buttons + if (PS3.getAnalogButton(L2) || PS3.getAnalogButton(R2)) { + Serial.print(F("\r\nL2: ")); + Serial.print(PS3.getAnalogButton(L2)); + if (!PS3.PS3NavigationConnected) { + Serial.print(F("\tR2: ")); + Serial.print(PS3.getAnalogButton(R2)); + } + } + if (PS3.getButtonClick(PS)) + Serial.print(F("\r\nPS")); + + if (PS3.getButtonClick(TRIANGLE)) + Serial.print(F("\r\nTraingle")); + if (PS3.getButtonClick(CIRCLE)) + Serial.print(F("\r\nCircle")); + if (PS3.getButtonClick(CROSS)) + Serial.print(F("\r\nCross")); + if (PS3.getButtonClick(SQUARE)) + Serial.print(F("\r\nSquare")); + + if (PS3.getButtonClick(UP)) { + Serial.print(F("\r\nUp")); + PS3.setLedOff(); + PS3.setLedOn(LED4); + } + if (PS3.getButtonClick(RIGHT)) { + Serial.print(F("\r\nRight")); + PS3.setLedOff(); + PS3.setLedOn(LED1); + } + if (PS3.getButtonClick(DOWN)) { + Serial.print(F("\r\nDown")); + PS3.setLedOff(); + PS3.setLedOn(LED2); + } + if (PS3.getButtonClick(LEFT)) { + Serial.print(F("\r\nLeft")); + PS3.setLedOff(); + PS3.setLedOn(LED3); + } + + if (PS3.getButtonClick(L1)) + Serial.print(F("\r\nL1")); + if (PS3.getButtonClick(L3)) + Serial.print(F("\r\nL3")); + if (PS3.getButtonClick(R1)) + Serial.print(F("\r\nR1")); + if (PS3.getButtonClick(R3)) + Serial.print(F("\r\nR3")); + + if (PS3.getButtonClick(SELECT)) { + Serial.print(F("\r\nSelect - ")); + PS3.printStatusString(); + } + if (PS3.getButtonClick(START)) { + Serial.print(F("\r\nStart")); + printAngle = !printAngle; + } + if (printAngle) { + Serial.print(F("\r\nPitch: ")); + Serial.print(PS3.getAngle(Pitch)); + Serial.print(F("\tRoll: ")); + Serial.print(PS3.getAngle(Roll)); + } + } + else if (PS3.PS3MoveConnected) { // One can only set the color of the bulb, set the rumble, set and get the bluetooth address and calibrate the magnetometer via USB + if (state == 0) { + PS3.moveSetRumble(0); + PS3.moveSetBulb(Off); + } else if (state == 1) { + PS3.moveSetRumble(75); + PS3.moveSetBulb(Red); + } else if (state == 2) { + PS3.moveSetRumble(125); + PS3.moveSetBulb(Green); + } else if (state == 3) { + PS3.moveSetRumble(150); + PS3.moveSetBulb(Blue); + } else if (state == 4) { + PS3.moveSetRumble(175); + PS3.moveSetBulb(Yellow); + } else if (state == 5) { + PS3.moveSetRumble(200); + PS3.moveSetBulb(Lightblue); + } else if (state == 6) { + PS3.moveSetRumble(225); + PS3.moveSetBulb(Purble); + } else if (state == 7) { + PS3.moveSetRumble(250); + PS3.moveSetBulb(White); + } + + state++; + if (state > 7) + state = 0; + delay(1000); + } +} diff --git a/libraries/USB_Host_Shield/examples/PS4USB/PS4USB.ino b/libraries/USB_Host_Shield/examples/PS4USB/PS4USB.ino new file mode 100755 index 0000000..5d49f23 --- /dev/null +++ b/libraries/USB_Host_Shield/examples/PS4USB/PS4USB.ino @@ -0,0 +1,133 @@ +/* + Example sketch for the PS4 USB library - developed by Kristian Lauszus + For more information visit my blog: http://blog.tkjelectronics.dk/ or + send me an e-mail: kristianl@tkjelectronics.com + */ + +#include + +// Satisfy IDE, which only needs to see the include statment in the ino. +#ifdef dobogusinclude +#include +#include +#endif + +USB Usb; +PS4USB PS4(&Usb); + +bool printAngle, printTouch; +uint8_t oldL2Value, oldR2Value; + +void setup() { + Serial.begin(115200); +#if !defined(__MIPSEL__) + while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection +#endif + if (Usb.Init() == -1) { + Serial.print(F("\r\nOSC did not start")); + while (1); // Halt + } + Serial.print(F("\r\nPS4 USB Library Started")); +} + +void loop() { + Usb.Task(); + + if (PS4.connected()) { + if (PS4.getAnalogHat(LeftHatX) > 137 || PS4.getAnalogHat(LeftHatX) < 117 || PS4.getAnalogHat(LeftHatY) > 137 || PS4.getAnalogHat(LeftHatY) < 117 || PS4.getAnalogHat(RightHatX) > 137 || PS4.getAnalogHat(RightHatX) < 117 || PS4.getAnalogHat(RightHatY) > 137 || PS4.getAnalogHat(RightHatY) < 117) { + Serial.print(F("\r\nLeftHatX: ")); + Serial.print(PS4.getAnalogHat(LeftHatX)); + Serial.print(F("\tLeftHatY: ")); + Serial.print(PS4.getAnalogHat(LeftHatY)); + Serial.print(F("\tRightHatX: ")); + Serial.print(PS4.getAnalogHat(RightHatX)); + Serial.print(F("\tRightHatY: ")); + Serial.print(PS4.getAnalogHat(RightHatY)); + } + + if (PS4.getAnalogButton(L2) || PS4.getAnalogButton(R2)) { // These are the only analog buttons on the PS4 controller + Serial.print(F("\r\nL2: ")); + Serial.print(PS4.getAnalogButton(L2)); + Serial.print(F("\tR2: ")); + Serial.print(PS4.getAnalogButton(R2)); + } + if (PS4.getAnalogButton(L2) != oldL2Value || PS4.getAnalogButton(R2) != oldR2Value) // Only write value if it's different + PS4.setRumbleOn(PS4.getAnalogButton(L2), PS4.getAnalogButton(R2)); + oldL2Value = PS4.getAnalogButton(L2); + oldR2Value = PS4.getAnalogButton(R2); + + if (PS4.getButtonClick(PS)) + Serial.print(F("\r\nPS")); + if (PS4.getButtonClick(TRIANGLE)) { + Serial.print(F("\r\nTraingle")); + PS4.setRumbleOn(RumbleLow); + } + if (PS4.getButtonClick(CIRCLE)) { + Serial.print(F("\r\nCircle")); + PS4.setRumbleOn(RumbleHigh); + } + if (PS4.getButtonClick(CROSS)) { + Serial.print(F("\r\nCross")); + PS4.setLedFlash(10, 10); // Set it to blink rapidly + } + if (PS4.getButtonClick(SQUARE)) { + Serial.print(F("\r\nSquare")); + PS4.setLedFlash(0, 0); // Turn off blinking + } + + if (PS4.getButtonClick(UP)) { + Serial.print(F("\r\nUp")); + PS4.setLed(Red); + } if (PS4.getButtonClick(RIGHT)) { + Serial.print(F("\r\nRight")); + PS4.setLed(Blue); + } if (PS4.getButtonClick(DOWN)) { + Serial.print(F("\r\nDown")); + PS4.setLed(Yellow); + } if (PS4.getButtonClick(LEFT)) { + Serial.print(F("\r\nLeft")); + PS4.setLed(Green); + } + + if (PS4.getButtonClick(L1)) + Serial.print(F("\r\nL1")); + if (PS4.getButtonClick(L3)) + Serial.print(F("\r\nL3")); + if (PS4.getButtonClick(R1)) + Serial.print(F("\r\nR1")); + if (PS4.getButtonClick(R3)) + Serial.print(F("\r\nR3")); + + if (PS4.getButtonClick(SHARE)) + Serial.print(F("\r\nShare")); + if (PS4.getButtonClick(OPTIONS)) { + Serial.print(F("\r\nOptions")); + printAngle = !printAngle; + } + if (PS4.getButtonClick(TOUCHPAD)) { + Serial.print(F("\r\nTouchpad")); + printTouch = !printTouch; + } + + if (printAngle) { // Print angle calculated using the accelerometer only + Serial.print(F("\r\nPitch: ")); + Serial.print(PS4.getAngle(Pitch)); + Serial.print(F("\tRoll: ")); + Serial.print(PS4.getAngle(Roll)); + } + + if (printTouch) { // Print the x, y coordinates of the touchpad + if (PS4.isTouching(0) || PS4.isTouching(1)) // Print newline and carriage return if any of the fingers are touching the touchpad + Serial.print(F("\r\n")); + for (uint8_t i = 0; i < 2; i++) { // The touchpad track two fingers + if (PS4.isTouching(i)) { // Print the position of the finger if it is touching the touchpad + Serial.print(F("X")); Serial.print(i + 1); Serial.print(F(": ")); + Serial.print(PS4.getX(i)); + Serial.print(F("\tY")); Serial.print(i + 1); Serial.print(F(": ")); + Serial.print(PS4.getY(i)); + Serial.print(F("\t")); + } + } + } + } +} diff --git a/libraries/USB_Host_Shield/examples/PSBuzz/PSBuzz.ino b/libraries/USB_Host_Shield/examples/PSBuzz/PSBuzz.ino new file mode 100755 index 0000000..60271f4 --- /dev/null +++ b/libraries/USB_Host_Shield/examples/PSBuzz/PSBuzz.ino @@ -0,0 +1,49 @@ +/* + Example sketch for the Playstation Buzz library - developed by Kristian Lauszus + For more information visit my blog: http://blog.tkjelectronics.dk/ or + send me an e-mail: kristianl@tkjelectronics.com + */ + +#include + +// Satisfy IDE, which only needs to see the include statment in the ino. +#ifdef dobogusinclude +#include +#include +#endif + +USB Usb; +PSBuzz Buzz(&Usb); + +void setup() { + Serial.begin(115200); +#if !defined(__MIPSEL__) + while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection +#endif + if (Usb.Init() == -1) { + Serial.print(F("\r\nOSC did not start")); + while (1); // Halt + } + Serial.println(F("\r\nPS Buzz Library Started")); +} + +void loop() { + Usb.Task(); + + if (Buzz.connected()) { + for (uint8_t i = 0; i < 4; i++) { + if (Buzz.getButtonClick(RED, i)) { + Buzz.setLedToggle(i); // Toggle the LED + Serial.println(F("RED")); + } + if (Buzz.getButtonClick(YELLOW, i)) + Serial.println(F("YELLOW")); + if (Buzz.getButtonClick(GREEN, i)) + Serial.println(F("GREEN")); + if (Buzz.getButtonClick(ORANGE, i)) + Serial.println(F("ORANGE")); + if (Buzz.getButtonClick(BLUE, i)) + Serial.println(F("BLUE")); + } + } +} diff --git a/libraries/USB_Host_Shield/examples/USB_desc/USB_desc.ino b/libraries/USB_Host_Shield/examples/USB_desc/USB_desc.ino new file mode 100755 index 0000000..e659649 --- /dev/null +++ b/libraries/USB_Host_Shield/examples/USB_desc/USB_desc.ino @@ -0,0 +1,348 @@ +#include + +#include "pgmstrings.h" +// Satisfy IDE, which only needs to see the include statment in the ino. +#ifdef dobogusinclude +#include +#include +#endif + +USB Usb; +//USBHub Hub1(&Usb); +//USBHub Hub2(&Usb); +//USBHub Hub3(&Usb); +//USBHub Hub4(&Usb); +//USBHub Hub5(&Usb); +//USBHub Hub6(&Usb); +//USBHub Hub7(&Usb); + +uint32_t next_time; + +void PrintAllAddresses(UsbDevice *pdev) +{ + UsbDeviceAddress adr; + adr.devAddress = pdev->address.devAddress; + Serial.print("\r\nAddr:"); + Serial.print(adr.devAddress, HEX); + Serial.print("("); + Serial.print(adr.bmHub, HEX); + Serial.print("."); + Serial.print(adr.bmParent, HEX); + Serial.print("."); + Serial.print(adr.bmAddress, HEX); + Serial.println(")"); +} + +void PrintAddress(uint8_t addr) +{ + UsbDeviceAddress adr; + adr.devAddress = addr; + Serial.print("\r\nADDR:\t"); + Serial.println(adr.devAddress,HEX); + Serial.print("DEV:\t"); + Serial.println(adr.bmAddress,HEX); + Serial.print("PRNT:\t"); + Serial.println(adr.bmParent,HEX); + Serial.print("HUB:\t"); + Serial.println(adr.bmHub,HEX); +} + +void setup() +{ + Serial.begin( 115200 ); +#if !defined(__MIPSEL__) + while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection +#endif + Serial.println("Start"); + + if (Usb.Init() == -1) + Serial.println("OSC did not start."); + + delay( 200 ); + + next_time = millis() + 10000; +} + +byte getdevdescr( byte addr, byte &num_conf ); + +void PrintDescriptors(uint8_t addr) +{ + uint8_t rcode = 0; + byte num_conf = 0; + + rcode = getdevdescr( (byte)addr, num_conf ); + if( rcode ) + { + printProgStr(Gen_Error_str); + print_hex( rcode, 8 ); + } + Serial.print("\r\n"); + + for (int i=0; iaddress.devAddress, 8); + Serial.println("\r\n--"); + PrintDescriptors( pdev->address.devAddress ); +} + +void loop() +{ + Usb.Task(); + + if( Usb.getUsbTaskState() == USB_STATE_RUNNING ) + { + //if (millis() >= next_time) + { + Usb.ForEachUsbDevice(&PrintAllDescriptors); + Usb.ForEachUsbDevice(&PrintAllAddresses); + + while( 1 ); //stop + } + } +} + +byte getdevdescr( byte addr, byte &num_conf ) +{ + USB_DEVICE_DESCRIPTOR buf; + byte rcode; + rcode = Usb.getDevDescr( addr, 0, 0x12, ( uint8_t *)&buf ); + if( rcode ) { + return( rcode ); + } + printProgStr(Dev_Header_str); + printProgStr(Dev_Length_str); + print_hex( buf.bLength, 8 ); + printProgStr(Dev_Type_str); + print_hex( buf.bDescriptorType, 8 ); + printProgStr(Dev_Version_str); + print_hex( buf.bcdUSB, 16 ); + printProgStr(Dev_Class_str); + print_hex( buf.bDeviceClass, 8 ); + printProgStr(Dev_Subclass_str); + print_hex( buf.bDeviceSubClass, 8 ); + printProgStr(Dev_Protocol_str); + print_hex( buf.bDeviceProtocol, 8 ); + printProgStr(Dev_Pktsize_str); + print_hex( buf.bMaxPacketSize0, 8 ); + printProgStr(Dev_Vendor_str); + print_hex( buf.idVendor, 16 ); + printProgStr(Dev_Product_str); + print_hex( buf.idProduct, 16 ); + printProgStr(Dev_Revision_str); + print_hex( buf.bcdDevice, 16 ); + printProgStr(Dev_Mfg_str); + print_hex( buf.iManufacturer, 8 ); + printProgStr(Dev_Prod_str); + print_hex( buf.iProduct, 8 ); + printProgStr(Dev_Serial_str); + print_hex( buf.iSerialNumber, 8 ); + printProgStr(Dev_Nconf_str); + print_hex( buf.bNumConfigurations, 8 ); + num_conf = buf.bNumConfigurations; + return( 0 ); +} + +void printhubdescr(uint8_t *descrptr, uint8_t addr) +{ + HubDescriptor *pHub = (HubDescriptor*) descrptr; + uint8_t len = *((uint8_t*)descrptr); + + printProgStr(PSTR("\r\n\r\nHub Descriptor:\r\n")); + printProgStr(PSTR("bDescLength:\t\t")); + Serial.println(pHub->bDescLength, HEX); + + printProgStr(PSTR("bDescriptorType:\t")); + Serial.println(pHub->bDescriptorType, HEX); + + printProgStr(PSTR("bNbrPorts:\t\t")); + Serial.println(pHub->bNbrPorts, HEX); + + printProgStr(PSTR("LogPwrSwitchMode:\t")); + Serial.println(pHub->LogPwrSwitchMode, BIN); + + printProgStr(PSTR("CompoundDevice:\t\t")); + Serial.println(pHub->CompoundDevice, BIN); + + printProgStr(PSTR("OverCurrentProtectMode:\t")); + Serial.println(pHub->OverCurrentProtectMode, BIN); + + printProgStr(PSTR("TTThinkTime:\t\t")); + Serial.println(pHub->TTThinkTime, BIN); + + printProgStr(PSTR("PortIndicatorsSupported:")); + Serial.println(pHub->PortIndicatorsSupported, BIN); + + printProgStr(PSTR("Reserved:\t\t")); + Serial.println(pHub->Reserved, HEX); + + printProgStr(PSTR("bPwrOn2PwrGood:\t\t")); + Serial.println(pHub->bPwrOn2PwrGood, HEX); + + printProgStr(PSTR("bHubContrCurrent:\t")); + Serial.println(pHub->bHubContrCurrent, HEX); + + for (uint8_t i=7; ibNbrPorts; i++) + // PrintHubPortStatus(&Usb, addr, i, 1); +} + +byte getconfdescr( byte addr, byte conf ) +{ + uint8_t buf[ BUFSIZE ]; + uint8_t* buf_ptr = buf; + byte rcode; + byte descr_length; + byte descr_type; + unsigned int total_length; + rcode = Usb.getConfDescr( addr, 0, 4, conf, buf ); //get total length + LOBYTE( total_length ) = buf[ 2 ]; + HIBYTE( total_length ) = buf[ 3 ]; + if( total_length > 256 ) { //check if total length is larger than buffer + printProgStr(Conf_Trunc_str); + total_length = 256; + } + rcode = Usb.getConfDescr( addr, 0, total_length, conf, buf ); //get the whole descriptor + while( buf_ptr < buf + total_length ) { //parsing descriptors + descr_length = *( buf_ptr ); + descr_type = *( buf_ptr + 1 ); + switch( descr_type ) { + case( USB_DESCRIPTOR_CONFIGURATION ): + printconfdescr( buf_ptr ); + break; + case( USB_DESCRIPTOR_INTERFACE ): + printintfdescr( buf_ptr ); + break; + case( USB_DESCRIPTOR_ENDPOINT ): + printepdescr( buf_ptr ); + break; + case 0x29: + printhubdescr( buf_ptr, addr ); + break; + default: + printunkdescr( buf_ptr ); + break; + }//switch( descr_type + buf_ptr = ( buf_ptr + descr_length ); //advance buffer pointer + }//while( buf_ptr <=... + return( 0 ); +} +/* prints hex numbers with leading zeroes */ +// copyright, Peter H Anderson, Baltimore, MD, Nov, '07 +// source: http://www.phanderson.com/arduino/arduino_display.html +void print_hex(int v, int num_places) +{ + int mask=0, n, num_nibbles, digit; + + for (n=1; n<=num_places; n++) { + mask = (mask << 1) | 0x0001; + } + v = v & mask; // truncate v to specified number of places + + num_nibbles = num_places / 4; + if ((num_places % 4) != 0) { + ++num_nibbles; + } + do { + digit = ((v >> (num_nibbles-1) * 4)) & 0x0f; + Serial.print(digit, HEX); + } + while(--num_nibbles); +} +/* function to print configuration descriptor */ +void printconfdescr( uint8_t* descr_ptr ) +{ + USB_CONFIGURATION_DESCRIPTOR* conf_ptr = ( USB_CONFIGURATION_DESCRIPTOR* )descr_ptr; + printProgStr(Conf_Header_str); + printProgStr(Conf_Totlen_str); + print_hex( conf_ptr->wTotalLength, 16 ); + printProgStr(Conf_Nint_str); + print_hex( conf_ptr->bNumInterfaces, 8 ); + printProgStr(Conf_Value_str); + print_hex( conf_ptr->bConfigurationValue, 8 ); + printProgStr(Conf_String_str); + print_hex( conf_ptr->iConfiguration, 8 ); + printProgStr(Conf_Attr_str); + print_hex( conf_ptr->bmAttributes, 8 ); + printProgStr(Conf_Pwr_str); + print_hex( conf_ptr->bMaxPower, 8 ); + return; +} +/* function to print interface descriptor */ +void printintfdescr( uint8_t* descr_ptr ) +{ + USB_INTERFACE_DESCRIPTOR* intf_ptr = ( USB_INTERFACE_DESCRIPTOR* )descr_ptr; + printProgStr(Int_Header_str); + printProgStr(Int_Number_str); + print_hex( intf_ptr->bInterfaceNumber, 8 ); + printProgStr(Int_Alt_str); + print_hex( intf_ptr->bAlternateSetting, 8 ); + printProgStr(Int_Endpoints_str); + print_hex( intf_ptr->bNumEndpoints, 8 ); + printProgStr(Int_Class_str); + print_hex( intf_ptr->bInterfaceClass, 8 ); + printProgStr(Int_Subclass_str); + print_hex( intf_ptr->bInterfaceSubClass, 8 ); + printProgStr(Int_Protocol_str); + print_hex( intf_ptr->bInterfaceProtocol, 8 ); + printProgStr(Int_String_str); + print_hex( intf_ptr->iInterface, 8 ); + return; +} +/* function to print endpoint descriptor */ +void printepdescr( uint8_t* descr_ptr ) +{ + USB_ENDPOINT_DESCRIPTOR* ep_ptr = ( USB_ENDPOINT_DESCRIPTOR* )descr_ptr; + printProgStr(End_Header_str); + printProgStr(End_Address_str); + print_hex( ep_ptr->bEndpointAddress, 8 ); + printProgStr(End_Attr_str); + print_hex( ep_ptr->bmAttributes, 8 ); + printProgStr(End_Pktsize_str); + print_hex( ep_ptr->wMaxPacketSize, 16 ); + printProgStr(End_Interval_str); + print_hex( ep_ptr->bInterval, 8 ); + + return; +} +/*function to print unknown descriptor */ +void printunkdescr( uint8_t* descr_ptr ) +{ + byte length = *descr_ptr; + byte i; + printProgStr(Unk_Header_str); + printProgStr(Unk_Length_str); + print_hex( *descr_ptr, 8 ); + printProgStr(Unk_Type_str); + print_hex( *(descr_ptr + 1 ), 8 ); + printProgStr(Unk_Contents_str); + descr_ptr += 2; + for( i = 0; i < length; i++ ) { + print_hex( *descr_ptr, 8 ); + descr_ptr++; + } +} + + +/* Print a string from Program Memory directly to save RAM */ +void printProgStr(prog_char str[]) +{ + char c; + if(!str) return; + while((c = pgm_read_byte(str++))) + Serial.print(c); +} diff --git a/libraries/USB_Host_Shield/examples/USB_desc/pgmstrings.h b/libraries/USB_Host_Shield/examples/USB_desc/pgmstrings.h new file mode 100755 index 0000000..bdb0077 --- /dev/null +++ b/libraries/USB_Host_Shield/examples/USB_desc/pgmstrings.h @@ -0,0 +1,52 @@ +#if !defined(__PGMSTRINGS_H__) +#define __PGMSTRINGS_H__ + +#define LOBYTE(x) ((char*)(&(x)))[0] +#define HIBYTE(x) ((char*)(&(x)))[1] +#define BUFSIZE 256 //buffer size + + +/* Print strings in Program Memory */ +const char Gen_Error_str[] PROGMEM = "\r\nRequest error. Error code:\t"; +const char Dev_Header_str[] PROGMEM ="\r\nDevice descriptor: "; +const char Dev_Length_str[] PROGMEM ="\r\nDescriptor Length:\t"; +const char Dev_Type_str[] PROGMEM ="\r\nDescriptor type:\t"; +const char Dev_Version_str[] PROGMEM ="\r\nUSB version:\t\t"; +const char Dev_Class_str[] PROGMEM ="\r\nDevice class:\t\t"; +const char Dev_Subclass_str[] PROGMEM ="\r\nDevice Subclass:\t"; +const char Dev_Protocol_str[] PROGMEM ="\r\nDevice Protocol:\t"; +const char Dev_Pktsize_str[] PROGMEM ="\r\nMax.packet size:\t"; +const char Dev_Vendor_str[] PROGMEM ="\r\nVendor ID:\t\t"; +const char Dev_Product_str[] PROGMEM ="\r\nProduct ID:\t\t"; +const char Dev_Revision_str[] PROGMEM ="\r\nRevision ID:\t\t"; +const char Dev_Mfg_str[] PROGMEM ="\r\nMfg.string index:\t"; +const char Dev_Prod_str[] PROGMEM ="\r\nProd.string index:\t"; +const char Dev_Serial_str[] PROGMEM ="\r\nSerial number index:\t"; +const char Dev_Nconf_str[] PROGMEM ="\r\nNumber of conf.:\t"; +const char Conf_Trunc_str[] PROGMEM ="Total length truncated to 256 bytes"; +const char Conf_Header_str[] PROGMEM ="\r\nConfiguration descriptor:"; +const char Conf_Totlen_str[] PROGMEM ="\r\nTotal length:\t\t"; +const char Conf_Nint_str[] PROGMEM ="\r\nNum.intf:\t\t"; +const char Conf_Value_str[] PROGMEM ="\r\nConf.value:\t\t"; +const char Conf_String_str[] PROGMEM ="\r\nConf.string:\t\t"; +const char Conf_Attr_str[] PROGMEM ="\r\nAttr.:\t\t\t"; +const char Conf_Pwr_str[] PROGMEM ="\r\nMax.pwr:\t\t"; +const char Int_Header_str[] PROGMEM ="\r\n\r\nInterface descriptor:"; +const char Int_Number_str[] PROGMEM ="\r\nIntf.number:\t\t"; +const char Int_Alt_str[] PROGMEM ="\r\nAlt.:\t\t\t"; +const char Int_Endpoints_str[] PROGMEM ="\r\nEndpoints:\t\t"; +const char Int_Class_str[] PROGMEM ="\r\nIntf. Class:\t\t"; +const char Int_Subclass_str[] PROGMEM ="\r\nIntf. Subclass:\t\t"; +const char Int_Protocol_str[] PROGMEM ="\r\nIntf. Protocol:\t\t"; +const char Int_String_str[] PROGMEM ="\r\nIntf.string:\t\t"; +const char End_Header_str[] PROGMEM ="\r\n\r\nEndpoint descriptor:"; +const char End_Address_str[] PROGMEM ="\r\nEndpoint address:\t"; +const char End_Attr_str[] PROGMEM ="\r\nAttr.:\t\t\t"; +const char End_Pktsize_str[] PROGMEM ="\r\nMax.pkt size:\t\t"; +const char End_Interval_str[] PROGMEM ="\r\nPolling interval:\t"; +const char Unk_Header_str[] PROGMEM = "\r\nUnknown descriptor:"; +const char Unk_Length_str[] PROGMEM ="\r\nLength:\t\t"; +const char Unk_Type_str[] PROGMEM ="\r\nType:\t\t"; +const char Unk_Contents_str[] PROGMEM ="\r\nContents:\t"; + +#endif // __PGMSTRINGS_H__ \ No newline at end of file diff --git a/libraries/USB_Host_Shield/examples/Xbox/XBOXOLD/XBOXOLD.ino b/libraries/USB_Host_Shield/examples/Xbox/XBOXOLD/XBOXOLD.ino new file mode 100755 index 0000000..c9ecaed --- /dev/null +++ b/libraries/USB_Host_Shield/examples/Xbox/XBOXOLD/XBOXOLD.ino @@ -0,0 +1,109 @@ +/* + Example sketch for the original Xbox library - developed by Kristian Lauszus + For more information visit my blog: http://blog.tkjelectronics.dk/ or + send me an e-mail: kristianl@tkjelectronics.com + */ + +#include +#include +// Satisfy IDE, which only needs to see the include statment in the ino. +#ifdef dobogusinclude +#include +#include +#endif + +USB Usb; +USBHub Hub1(&Usb); // The controller has a built in hub, so this instance is needed +XBOXOLD Xbox(&Usb); + +void setup() { + Serial.begin(115200); +#if !defined(__MIPSEL__) + while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection +#endif + if (Usb.Init() == -1) { + Serial.print(F("\r\nOSC did not start")); + while (1); // halt + } + Serial.print(F("\r\nXBOX Library Started")); +} +void loop() { + Usb.Task(); + if (Xbox.XboxConnected) { + if (Xbox.getButtonPress(BLACK) || Xbox.getButtonPress(WHITE)) { + Serial.print("BLACK: "); + Serial.print(Xbox.getButtonPress(BLACK)); + Serial.print("\tWHITE: "); + Serial.println(Xbox.getButtonPress(WHITE)); + Xbox.setRumbleOn(Xbox.getButtonPress(BLACK), Xbox.getButtonPress(WHITE)); + } else + Xbox.setRumbleOn(0, 0); + + if (Xbox.getAnalogHat(LeftHatX) > 7500 || Xbox.getAnalogHat(LeftHatX) < -7500 || Xbox.getAnalogHat(LeftHatY) > 7500 || Xbox.getAnalogHat(LeftHatY) < -7500 || Xbox.getAnalogHat(RightHatX) > 7500 || Xbox.getAnalogHat(RightHatX) < -7500 || Xbox.getAnalogHat(RightHatY) > 7500 || Xbox.getAnalogHat(RightHatY) < -7500) { + if (Xbox.getAnalogHat(LeftHatX) > 7500 || Xbox.getAnalogHat(LeftHatX) < -7500) { + Serial.print(F("LeftHatX: ")); + Serial.print(Xbox.getAnalogHat(LeftHatX)); + Serial.print("\t"); + } + if (Xbox.getAnalogHat(LeftHatY) > 7500 || Xbox.getAnalogHat(LeftHatY) < -7500) { + Serial.print(F("LeftHatY: ")); + Serial.print(Xbox.getAnalogHat(LeftHatY)); + Serial.print("\t"); + } + if (Xbox.getAnalogHat(RightHatX) > 7500 || Xbox.getAnalogHat(RightHatX) < -7500) { + Serial.print(F("RightHatX: ")); + Serial.print(Xbox.getAnalogHat(RightHatX)); + Serial.print("\t"); + } + if (Xbox.getAnalogHat(RightHatY) > 7500 || Xbox.getAnalogHat(RightHatY) < -7500) { + Serial.print(F("RightHatY: ")); + Serial.print(Xbox.getAnalogHat(RightHatY)); + } + Serial.println(); + } + + if (Xbox.getButtonClick(UP)) + Serial.println(F("Up")); + if (Xbox.getButtonClick(DOWN)) + Serial.println(F("Down")); + if (Xbox.getButtonClick(LEFT)) + Serial.println(F("Left")); + if (Xbox.getButtonClick(RIGHT)) + Serial.println(F("Right")); + + if (Xbox.getButtonClick(START)) + Serial.println(F("Start")); + if (Xbox.getButtonClick(BACK)) + Serial.println(F("Back")); + if (Xbox.getButtonClick(L3)) + Serial.println(F("L3")); + if (Xbox.getButtonClick(R3)) + Serial.println(F("R3")); + + if (Xbox.getButtonPress(A)) { + Serial.print(F("A: ")); + Serial.println(Xbox.getButtonPress(A)); + } + if (Xbox.getButtonPress(B)) { + Serial.print(F("B: ")); + Serial.println(Xbox.getButtonPress(B)); + } + if (Xbox.getButtonPress(X)) { + Serial.print(F("X: ")); + Serial.println(Xbox.getButtonPress(X)); + } + if (Xbox.getButtonPress(Y)) { + Serial.print(F("Y: ")); + Serial.println(Xbox.getButtonPress(Y)); + } + if (Xbox.getButtonPress(L1)) { + Serial.print(F("L1: ")); + Serial.println(Xbox.getButtonPress(L1)); + } + if (Xbox.getButtonPress(R1)) { + Serial.print(F("R1: ")); + Serial.println(Xbox.getButtonPress(R1)); + } + } + delay(1); +} diff --git a/libraries/USB_Host_Shield/examples/Xbox/XBOXRECV/XBOXRECV.ino b/libraries/USB_Host_Shield/examples/Xbox/XBOXRECV/XBOXRECV.ino new file mode 100755 index 0000000..a39ddbb --- /dev/null +++ b/libraries/USB_Host_Shield/examples/Xbox/XBOXRECV/XBOXRECV.ino @@ -0,0 +1,121 @@ +/* + Example sketch for the Xbox Wireless Reciver library - developed by Kristian Lauszus + It supports up to four controllers wirelessly + For more information see the blog post: http://blog.tkjelectronics.dk/2012/12/xbox-360-receiver-added-to-the-usb-host-library/ or + send me an e-mail: kristianl@tkjelectronics.com + */ + +#include +// Satisfy IDE, which only needs to see the include statment in the ino. +#ifdef dobogusinclude +#include +#include +#endif + +USB Usb; +XBOXRECV Xbox(&Usb); + +void setup() { + Serial.begin(115200); +#if !defined(__MIPSEL__) + while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection +#endif + if (Usb.Init() == -1) { + Serial.print(F("\r\nOSC did not start")); + while (1); //halt + } + Serial.print(F("\r\nXbox Wireless Receiver Library Started")); +} +void loop() { + Usb.Task(); + if (Xbox.XboxReceiverConnected) { + for (uint8_t i = 0; i < 4; i++) { + if (Xbox.Xbox360Connected[i]) { + if (Xbox.getButtonPress(L2, i) || Xbox.getButtonPress(R2, i)) { + Serial.print("L2: "); + Serial.print(Xbox.getButtonPress(L2, i)); + Serial.print("\tR2: "); + Serial.println(Xbox.getButtonPress(R2, i)); + Xbox.setRumbleOn(Xbox.getButtonPress(L2, i), Xbox.getButtonPress(R2, i), i); + } + + if (Xbox.getAnalogHat(LeftHatX, i) > 7500 || Xbox.getAnalogHat(LeftHatX, i) < -7500 || Xbox.getAnalogHat(LeftHatY, i) > 7500 || Xbox.getAnalogHat(LeftHatY, i) < -7500 || Xbox.getAnalogHat(RightHatX, i) > 7500 || Xbox.getAnalogHat(RightHatX, i) < -7500 || Xbox.getAnalogHat(RightHatY, i) > 7500 || Xbox.getAnalogHat(RightHatY, i) < -7500) { + if (Xbox.getAnalogHat(LeftHatX, i) > 7500 || Xbox.getAnalogHat(LeftHatX, i) < -7500) { + Serial.print(F("LeftHatX: ")); + Serial.print(Xbox.getAnalogHat(LeftHatX, i)); + Serial.print("\t"); + } + if (Xbox.getAnalogHat(LeftHatY, i) > 7500 || Xbox.getAnalogHat(LeftHatY, i) < -7500) { + Serial.print(F("LeftHatY: ")); + Serial.print(Xbox.getAnalogHat(LeftHatY, i)); + Serial.print("\t"); + } + if (Xbox.getAnalogHat(RightHatX, i) > 7500 || Xbox.getAnalogHat(RightHatX, i) < -7500) { + Serial.print(F("RightHatX: ")); + Serial.print(Xbox.getAnalogHat(RightHatX, i)); + Serial.print("\t"); + } + if (Xbox.getAnalogHat(RightHatY, i) > 7500 || Xbox.getAnalogHat(RightHatY, i) < -7500) { + Serial.print(F("RightHatY: ")); + Serial.print(Xbox.getAnalogHat(RightHatY, i)); + } + Serial.println(); + } + + if (Xbox.getButtonClick(UP, i)) { + Xbox.setLedOn(LED1, i); + Serial.println(F("Up")); + } + if (Xbox.getButtonClick(DOWN, i)) { + Xbox.setLedOn(LED4, i); + Serial.println(F("Down")); + } + if (Xbox.getButtonClick(LEFT, i)) { + Xbox.setLedOn(LED3, i); + Serial.println(F("Left")); + } + if (Xbox.getButtonClick(RIGHT, i)) { + Xbox.setLedOn(LED2, i); + Serial.println(F("Right")); + } + + if (Xbox.getButtonClick(START, i)) { + Xbox.setLedMode(ALTERNATING, i); + Serial.println(F("Start")); + } + if (Xbox.getButtonClick(BACK, i)) { + Xbox.setLedBlink(ALL, i); + Serial.println(F("Back")); + } + if (Xbox.getButtonClick(L3, i)) + Serial.println(F("L3")); + if (Xbox.getButtonClick(R3, i)) + Serial.println(F("R3")); + + if (Xbox.getButtonClick(L1, i)) + Serial.println(F("L1")); + if (Xbox.getButtonClick(R1, i)) + Serial.println(F("R1")); + if (Xbox.getButtonClick(XBOX, i)) { + Xbox.setLedMode(ROTATING, i); + Serial.print(F("Xbox (Battery: ")); + Serial.print(Xbox.getBatteryLevel(i)); // The battery level in the range 0-3 + Serial.println(F(")")); + } + if (Xbox.getButtonClick(SYNC, i)) { + Serial.println(F("Sync")); + Xbox.disconnect(i); + } + + if (Xbox.getButtonClick(A, i)) + Serial.println(F("A")); + if (Xbox.getButtonClick(B, i)) + Serial.println(F("B")); + if (Xbox.getButtonClick(X, i)) + Serial.println(F("X")); + if (Xbox.getButtonClick(Y, i)) + Serial.println(F("Y")); + } + } + } +} diff --git a/libraries/USB_Host_Shield/examples/Xbox/XBOXUSB/XBOXUSB.ino b/libraries/USB_Host_Shield/examples/Xbox/XBOXUSB/XBOXUSB.ino new file mode 100755 index 0000000..a4e14f1 --- /dev/null +++ b/libraries/USB_Host_Shield/examples/Xbox/XBOXUSB/XBOXUSB.ino @@ -0,0 +1,112 @@ +/* + Example sketch for the Xbox 360 USB library - developed by Kristian Lauszus + For more information visit my blog: http://blog.tkjelectronics.dk/ or + send me an e-mail: kristianl@tkjelectronics.com + */ + +#include +// Satisfy IDE, which only needs to see the include statment in the ino. +#ifdef dobogusinclude +#include +#include +#endif + +USB Usb; +XBOXUSB Xbox(&Usb); + +void setup() { + Serial.begin(115200); +#if !defined(__MIPSEL__) + while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection +#endif + if (Usb.Init() == -1) { + Serial.print(F("\r\nOSC did not start")); + while (1); //halt + } + Serial.print(F("\r\nXBOX USB Library Started")); +} +void loop() { + Usb.Task(); + if (Xbox.Xbox360Connected) { + if (Xbox.getButtonPress(L2) || Xbox.getButtonPress(R2)) { + Serial.print("L2: "); + Serial.print(Xbox.getButtonPress(L2)); + Serial.print("\tR2: "); + Serial.println(Xbox.getButtonPress(R2)); + Xbox.setRumbleOn(Xbox.getButtonPress(L2), Xbox.getButtonPress(R2)); + } else + Xbox.setRumbleOn(0, 0); + + if (Xbox.getAnalogHat(LeftHatX) > 7500 || Xbox.getAnalogHat(LeftHatX) < -7500 || Xbox.getAnalogHat(LeftHatY) > 7500 || Xbox.getAnalogHat(LeftHatY) < -7500 || Xbox.getAnalogHat(RightHatX) > 7500 || Xbox.getAnalogHat(RightHatX) < -7500 || Xbox.getAnalogHat(RightHatY) > 7500 || Xbox.getAnalogHat(RightHatY) < -7500) { + if (Xbox.getAnalogHat(LeftHatX) > 7500 || Xbox.getAnalogHat(LeftHatX) < -7500) { + Serial.print(F("LeftHatX: ")); + Serial.print(Xbox.getAnalogHat(LeftHatX)); + Serial.print("\t"); + } + if (Xbox.getAnalogHat(LeftHatY) > 7500 || Xbox.getAnalogHat(LeftHatY) < -7500) { + Serial.print(F("LeftHatY: ")); + Serial.print(Xbox.getAnalogHat(LeftHatY)); + Serial.print("\t"); + } + if (Xbox.getAnalogHat(RightHatX) > 7500 || Xbox.getAnalogHat(RightHatX) < -7500) { + Serial.print(F("RightHatX: ")); + Serial.print(Xbox.getAnalogHat(RightHatX)); + Serial.print("\t"); + } + if (Xbox.getAnalogHat(RightHatY) > 7500 || Xbox.getAnalogHat(RightHatY) < -7500) { + Serial.print(F("RightHatY: ")); + Serial.print(Xbox.getAnalogHat(RightHatY)); + } + Serial.println(); + } + + if (Xbox.getButtonClick(UP)) { + Xbox.setLedOn(LED1); + Serial.println(F("Up")); + } + if (Xbox.getButtonClick(DOWN)) { + Xbox.setLedOn(LED4); + Serial.println(F("Down")); + } + if (Xbox.getButtonClick(LEFT)) { + Xbox.setLedOn(LED3); + Serial.println(F("Left")); + } + if (Xbox.getButtonClick(RIGHT)) { + Xbox.setLedOn(LED2); + Serial.println(F("Right")); + } + + if (Xbox.getButtonClick(START)) { + Xbox.setLedMode(ALTERNATING); + Serial.println(F("Start")); + } + if (Xbox.getButtonClick(BACK)) { + Xbox.setLedBlink(ALL); + Serial.println(F("Back")); + } + if (Xbox.getButtonClick(L3)) + Serial.println(F("L3")); + if (Xbox.getButtonClick(R3)) + Serial.println(F("R3")); + + if (Xbox.getButtonClick(L1)) + Serial.println(F("L1")); + if (Xbox.getButtonClick(R1)) + Serial.println(F("R1")); + if (Xbox.getButtonClick(XBOX)) { + Xbox.setLedMode(ROTATING); + Serial.println(F("Xbox")); + } + + if (Xbox.getButtonClick(A)) + Serial.println(F("A")); + if (Xbox.getButtonClick(B)) + Serial.println(F("B")); + if (Xbox.getButtonClick(X)) + Serial.println(F("X")); + if (Xbox.getButtonClick(Y)) + Serial.println(F("Y")); + } + delay(1); +} diff --git a/libraries/USB_Host_Shield/examples/acm/acm_terminal/acm_terminal.ino b/libraries/USB_Host_Shield/examples/acm/acm_terminal/acm_terminal.ino new file mode 100755 index 0000000..696b4d0 --- /dev/null +++ b/libraries/USB_Host_Shield/examples/acm/acm_terminal/acm_terminal.ino @@ -0,0 +1,100 @@ +#include +#include + +#include "pgmstrings.h" + +// Satisfy IDE, which only needs to see the include statment in the ino. +#ifdef dobogusinclude +#include +#include +#endif + +class ACMAsyncOper : public CDCAsyncOper +{ +public: + uint8_t OnInit(ACM *pacm); +}; + +uint8_t ACMAsyncOper::OnInit(ACM *pacm) +{ + uint8_t rcode; + // Set DTR = 1 RTS=1 + rcode = pacm->SetControlLineState(3); + + if (rcode) + { + ErrorMessage(PSTR("SetControlLineState"), rcode); + return rcode; + } + + LINE_CODING lc; + lc.dwDTERate = 115200; + lc.bCharFormat = 0; + lc.bParityType = 0; + lc.bDataBits = 8; + + rcode = pacm->SetLineCoding(&lc); + + if (rcode) + ErrorMessage(PSTR("SetLineCoding"), rcode); + + return rcode; +} + +USB Usb; +//USBHub Hub(&Usb); +ACMAsyncOper AsyncOper; +ACM Acm(&Usb, &AsyncOper); + +void setup() +{ + Serial.begin( 115200 ); +#if !defined(__MIPSEL__) + while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection +#endif + Serial.println("Start"); + + if (Usb.Init() == -1) + Serial.println("OSCOKIRQ failed to assert"); + + delay( 200 ); +} + +void loop() +{ + Usb.Task(); + + if( Acm.isReady()) { + uint8_t rcode; + + /* reading the keyboard */ + if(Serial.available()) { + uint8_t data= Serial.read(); + /* sending to the phone */ + rcode = Acm.SndData(1, &data); + if (rcode) + ErrorMessage(PSTR("SndData"), rcode); + }//if(Serial.available()... + + delay(50); + + /* reading the phone */ + /* buffer size must be greater or equal to max.packet size */ + /* it it set to 64 (largest possible max.packet size) here, can be tuned down + for particular endpoint */ + uint8_t buf[64]; + uint16_t rcvd = 64; + rcode = Acm.RcvData(&rcvd, buf); + if (rcode && rcode != hrNAK) + ErrorMessage(PSTR("Ret"), rcode); + + if( rcvd ) { //more than zero bytes received + for(uint16_t i=0; i < rcvd; i++ ) { + Serial.print((char)buf[i]); //printing on the screen + } + } + delay(10); + }//if( Usb.getUsbTaskState() == USB_STATE_RUNNING.. +} + + diff --git a/libraries/USB_Host_Shield/examples/acm/acm_terminal/pgmstrings.h b/libraries/USB_Host_Shield/examples/acm/acm_terminal/pgmstrings.h new file mode 100755 index 0000000..bdb0077 --- /dev/null +++ b/libraries/USB_Host_Shield/examples/acm/acm_terminal/pgmstrings.h @@ -0,0 +1,52 @@ +#if !defined(__PGMSTRINGS_H__) +#define __PGMSTRINGS_H__ + +#define LOBYTE(x) ((char*)(&(x)))[0] +#define HIBYTE(x) ((char*)(&(x)))[1] +#define BUFSIZE 256 //buffer size + + +/* Print strings in Program Memory */ +const char Gen_Error_str[] PROGMEM = "\r\nRequest error. Error code:\t"; +const char Dev_Header_str[] PROGMEM ="\r\nDevice descriptor: "; +const char Dev_Length_str[] PROGMEM ="\r\nDescriptor Length:\t"; +const char Dev_Type_str[] PROGMEM ="\r\nDescriptor type:\t"; +const char Dev_Version_str[] PROGMEM ="\r\nUSB version:\t\t"; +const char Dev_Class_str[] PROGMEM ="\r\nDevice class:\t\t"; +const char Dev_Subclass_str[] PROGMEM ="\r\nDevice Subclass:\t"; +const char Dev_Protocol_str[] PROGMEM ="\r\nDevice Protocol:\t"; +const char Dev_Pktsize_str[] PROGMEM ="\r\nMax.packet size:\t"; +const char Dev_Vendor_str[] PROGMEM ="\r\nVendor ID:\t\t"; +const char Dev_Product_str[] PROGMEM ="\r\nProduct ID:\t\t"; +const char Dev_Revision_str[] PROGMEM ="\r\nRevision ID:\t\t"; +const char Dev_Mfg_str[] PROGMEM ="\r\nMfg.string index:\t"; +const char Dev_Prod_str[] PROGMEM ="\r\nProd.string index:\t"; +const char Dev_Serial_str[] PROGMEM ="\r\nSerial number index:\t"; +const char Dev_Nconf_str[] PROGMEM ="\r\nNumber of conf.:\t"; +const char Conf_Trunc_str[] PROGMEM ="Total length truncated to 256 bytes"; +const char Conf_Header_str[] PROGMEM ="\r\nConfiguration descriptor:"; +const char Conf_Totlen_str[] PROGMEM ="\r\nTotal length:\t\t"; +const char Conf_Nint_str[] PROGMEM ="\r\nNum.intf:\t\t"; +const char Conf_Value_str[] PROGMEM ="\r\nConf.value:\t\t"; +const char Conf_String_str[] PROGMEM ="\r\nConf.string:\t\t"; +const char Conf_Attr_str[] PROGMEM ="\r\nAttr.:\t\t\t"; +const char Conf_Pwr_str[] PROGMEM ="\r\nMax.pwr:\t\t"; +const char Int_Header_str[] PROGMEM ="\r\n\r\nInterface descriptor:"; +const char Int_Number_str[] PROGMEM ="\r\nIntf.number:\t\t"; +const char Int_Alt_str[] PROGMEM ="\r\nAlt.:\t\t\t"; +const char Int_Endpoints_str[] PROGMEM ="\r\nEndpoints:\t\t"; +const char Int_Class_str[] PROGMEM ="\r\nIntf. Class:\t\t"; +const char Int_Subclass_str[] PROGMEM ="\r\nIntf. Subclass:\t\t"; +const char Int_Protocol_str[] PROGMEM ="\r\nIntf. Protocol:\t\t"; +const char Int_String_str[] PROGMEM ="\r\nIntf.string:\t\t"; +const char End_Header_str[] PROGMEM ="\r\n\r\nEndpoint descriptor:"; +const char End_Address_str[] PROGMEM ="\r\nEndpoint address:\t"; +const char End_Attr_str[] PROGMEM ="\r\nAttr.:\t\t\t"; +const char End_Pktsize_str[] PROGMEM ="\r\nMax.pkt size:\t\t"; +const char End_Interval_str[] PROGMEM ="\r\nPolling interval:\t"; +const char Unk_Header_str[] PROGMEM = "\r\nUnknown descriptor:"; +const char Unk_Length_str[] PROGMEM ="\r\nLength:\t\t"; +const char Unk_Type_str[] PROGMEM ="\r\nType:\t\t"; +const char Unk_Contents_str[] PROGMEM ="\r\nContents:\t"; + +#endif // __PGMSTRINGS_H__ \ No newline at end of file diff --git a/libraries/USB_Host_Shield/examples/adk/ArduinoBlinkLED/ArduinoBlinkLED.ino b/libraries/USB_Host_Shield/examples/adk/ArduinoBlinkLED/ArduinoBlinkLED.ino new file mode 100755 index 0000000..d59b9bb --- /dev/null +++ b/libraries/USB_Host_Shield/examples/adk/ArduinoBlinkLED/ArduinoBlinkLED.ino @@ -0,0 +1,89 @@ +// The source for the Android application can be found at the following link: https://github.com/Lauszus/ArduinoBlinkLED +// The code for the Android application is heavily based on this guide: http://allaboutee.com/2011/12/31/arduino-adk-board-blink-an-led-with-your-phone-code-and-explanation/ by Miguel +#include + +// +// CAUTION! WARNING! ATTENTION! VORSICHT! ADVARSEL! ¡CUIDADO! Ð’ÐИМÐÐИЕ! +// +// Pin 13 is occupied by the SCK pin on various Arduino boards, +// including Uno, Duemilanove, etc., so use a different pin for those boards. +// +// CAUTION! WARNING! ATTENTION! VORSICHT! ADVARSEL! ¡CUIDADO! Ð’ÐИМÐÐИЕ! +// +#if defined(LED_BUILTIN) +#define LED LED_BUILTIN // Use built in LED +#else +#define LED 9 // Set to something here that makes sense for your board. +#endif + + +// Satisfy IDE, which only needs to see the include statment in the ino. +#ifdef dobogusinclude +#include +#include +#endif + +USB Usb; +ADK adk(&Usb, "TKJElectronics", // Manufacturer Name + "ArduinoBlinkLED", // Model Name + "Example sketch for the USB Host Shield", // Description (user-visible string) + "1.0", // Version + "http://www.tkjelectronics.dk/uploads/ArduinoBlinkLED.apk", // URL (web page to visit if no installed apps support the accessory) + "123456789"); // Serial Number (optional) + +uint32_t timer; +bool connected; + +void setup() { + Serial.begin(115200); +#if !defined(__MIPSEL__) + while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection +#endif + if (Usb.Init() == -1) { + Serial.print("\r\nOSCOKIRQ failed to assert"); + while (1); // halt + } + pinMode(LED, OUTPUT); + Serial.print("\r\nArduino Blink LED Started"); +} + +void loop() { + Usb.Task(); + + if (adk.isReady()) { + if (!connected) { + connected = true; + Serial.print(F("\r\nConnected to accessory")); + } + + uint8_t msg[1]; + uint16_t len = sizeof(msg); + uint8_t rcode = adk.RcvData(&len, msg); + if (rcode && rcode != hrNAK) { + Serial.print(F("\r\nData rcv: ")); + Serial.print(rcode, HEX); + } else if (len > 0) { + Serial.print(F("\r\nData Packet: ")); + Serial.print(msg[0]); + digitalWrite(LED, msg[0] ? HIGH : LOW); + } + + if (millis() - timer >= 1000) { // Send data every 1s + timer = millis(); + rcode = adk.SndData(sizeof(timer), (uint8_t*)&timer); + if (rcode && rcode != hrNAK) { + Serial.print(F("\r\nData send: ")); + Serial.print(rcode, HEX); + } else if (rcode != hrNAK) { + Serial.print(F("\r\nTimer: ")); + Serial.print(timer); + } + } + } else { + if (connected) { + connected = false; + Serial.print(F("\r\nDisconnected from accessory")); + digitalWrite(LED, LOW); + } + } +} diff --git a/libraries/USB_Host_Shield/examples/adk/adk_barcode/adk_barcode.ino b/libraries/USB_Host_Shield/examples/adk/adk_barcode/adk_barcode.ino new file mode 100755 index 0000000..c529700 --- /dev/null +++ b/libraries/USB_Host_Shield/examples/adk/adk_barcode/adk_barcode.ino @@ -0,0 +1,90 @@ +/**/ +/* A sketch demonstrating data exchange between two USB devices - a HID barcode scanner and ADK-compatible Android phone */ +/**/ +#include +#include +#include +// Satisfy IDE, which only needs to see the include statment in the ino. +#ifdef dobogusinclude +#include +#include +#endif + +USB Usb; +USBHub Hub1(&Usb); +USBHub Hub2(&Usb); +HIDBoot Keyboard(&Usb); + +ADK adk(&Usb,"Circuits@Home, ltd.", + "USB Host Shield", + "Arduino Terminal for Android", + "1.0", + "http://www.circuitsathome.com", + "0000000000000001"); + + +class KbdRptParser : public KeyboardReportParser +{ + +protected: + void OnKeyDown (uint8_t mod, uint8_t key); + void OnKeyPressed(uint8_t key); +}; + +void KbdRptParser::OnKeyDown(uint8_t mod, uint8_t key) +{ + uint8_t c = OemToAscii(mod, key); + + if (c) + OnKeyPressed(c); +} + +/* what to do when symbol arrives */ +void KbdRptParser::OnKeyPressed(uint8_t key) +{ +const char* new_line = "\n"; +uint8_t rcode; +uint8_t keylcl; + + if( adk.isReady() == false ) { + return; + } + + keylcl = key; + + if( keylcl == 0x13 ) { + rcode = adk.SndData( strlen( new_line ), (uint8_t *)new_line ); + } + else { + rcode = adk.SndData( 1, &keylcl ); + } + + Serial.print((char) keylcl ); + Serial.print(" : "); + Serial.println( keylcl, HEX ); +}; + +KbdRptParser Prs; + +void setup() +{ + Serial.begin(115200); +#if !defined(__MIPSEL__) + while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection +#endif + Serial.println("\r\nADK demo start"); + + if (Usb.Init() == -1) { + Serial.println("OSCOKIRQ failed to assert"); + while(1); //halt + }//if (Usb.Init() == -1... + + Keyboard.SetReportParser(0, (HIDReportParser*)&Prs); + + delay( 200 ); +} + +void loop() +{ + Usb.Task(); +} diff --git a/libraries/USB_Host_Shield/examples/adk/demokit_20/demokit_20.ino b/libraries/USB_Host_Shield/examples/adk/demokit_20/demokit_20.ino new file mode 100755 index 0000000..fbbe1c7 --- /dev/null +++ b/libraries/USB_Host_Shield/examples/adk/demokit_20/demokit_20.ino @@ -0,0 +1,102 @@ +#include +#include +// Satisfy IDE, which only needs to see the include statment in the ino. +#ifdef dobogusinclude +#include +#include +#endif + +USB Usb; +USBHub hub0(&Usb); +USBHub hub1(&Usb); +ADK adk(&Usb,"Google, Inc.", + "DemoKit", + "DemoKit Arduino Board", + "1.0", + "http://www.android.com", + "0000000012345678"); +uint8_t b, b1; + + +#define LED1_RED 3 +#define BUTTON1 2 + +void init_buttons() +{ + pinMode(BUTTON1, INPUT); + + // enable the internal pullups + digitalWrite(BUTTON1, HIGH); +} + +void init_leds() +{ + digitalWrite(LED1_RED, 0); + + pinMode(LED1_RED, OUTPUT); +} + +void setup() +{ + Serial.begin(115200); +#if !defined(__MIPSEL__) + while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection +#endif + Serial.println("\r\nADK demo start"); + + if (Usb.Init() == -1) { + Serial.println("OSCOKIRQ failed to assert"); + while(1); //halt + }//if (Usb.Init() == -1... + + + init_leds(); + init_buttons(); + b1 = digitalRead(BUTTON1); +} + +void loop() +{ + uint8_t rcode; + uint8_t msg[3] = { 0x00 }; + Usb.Task(); + + if( adk.isReady() == false ) { + analogWrite(LED1_RED, 255); + return; + } + uint16_t len = sizeof(msg); + + rcode = adk.RcvData(&len, msg); + if( rcode ) { + USBTRACE2("Data rcv. :", rcode ); + } + if(len > 0) { + USBTRACE("\r\nData Packet."); + // assumes only one command per packet + if (msg[0] == 0x2) { + switch( msg[1] ) { + case 0: + analogWrite(LED1_RED, 255 - msg[2]); + break; + }//switch( msg[1]... + }//if (msg[0] == 0x2... + }//if( len > 0... + + msg[0] = 0x1; + + b = digitalRead(BUTTON1); + if (b != b1) { + USBTRACE("\r\nButton state changed"); + msg[1] = 0; + msg[2] = b ? 0 : 1; + rcode = adk.SndData( 3, msg ); + if( rcode ) { + USBTRACE2("Button send: ", rcode ); + } + b1 = b; + }//if (b != b1... + + + delay( 10 ); +} diff --git a/libraries/USB_Host_Shield/examples/adk/term_test/term_test.ino b/libraries/USB_Host_Shield/examples/adk/term_test/term_test.ino new file mode 100755 index 0000000..8bc3ce5 --- /dev/null +++ b/libraries/USB_Host_Shield/examples/adk/term_test/term_test.ino @@ -0,0 +1,64 @@ +#include +#include +// Satisfy IDE, which only needs to see the include statment in the ino. +#ifdef dobogusinclude +#include +#include +#endif + +USB Usb; +//USBHub Hub(&Usb); + +ADK adk(&Usb,"Circuits@Home, ltd.", + "USB Host Shield", + "Arduino Terminal for Android", + "1.0", + "http://www.circuitsathome.com", + "0000000000000001"); + +void setup() +{ + Serial.begin(115200); +#if !defined(__MIPSEL__) + while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection +#endif + Serial.println("\r\nADK demo start"); + + if (Usb.Init() == -1) { + Serial.println("OSCOKIRQ failed to assert"); + while(1); //halt + }//if (Usb.Init() == -1... +} + +void loop() +{ + uint8_t rcode; + uint8_t msg[64] = { 0x00 }; + const char* recv = "Received: "; + + Usb.Task(); + + if( adk.isReady() == false ) { + return; + } + uint16_t len = 64; + + rcode = adk.RcvData(&len, msg); + if( rcode & ( rcode != hrNAK )) { + USBTRACE2("Data rcv. :", rcode ); + } + if(len > 0) { + USBTRACE("\r\nData Packet."); + + for( uint8_t i = 0; i < len; i++ ) { + Serial.print((char)msg[i]); + } + /* sending back what was received */ + rcode = adk.SndData( strlen( recv ), (uint8_t *)recv ); + rcode = adk.SndData( strlen(( char * )msg ), msg ); + + }//if( len > 0 )... + + delay( 1000 ); +} + diff --git a/libraries/USB_Host_Shield/examples/adk/term_time/term_time.ino b/libraries/USB_Host_Shield/examples/adk/term_time/term_time.ino new file mode 100755 index 0000000..a79bac4 --- /dev/null +++ b/libraries/USB_Host_Shield/examples/adk/term_time/term_time.ino @@ -0,0 +1,49 @@ +#include +#include +// Satisfy IDE, which only needs to see the include statment in the ino. +#ifdef dobogusinclude +#include +#include +#endif + +USB Usb; + +ADK adk(&Usb,"Circuits@Home, ltd.", + "USB Host Shield", + "Arduino Terminal for Android", + "1.0", + "http://www.circuitsathome.com", + "0000000000000001"); + +void setup() +{ + Serial.begin(115200); +#if !defined(__MIPSEL__) + while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection +#endif + Serial.println("\r\nADK demo start"); + + if (Usb.Init() == -1) { + Serial.println("OSCOKIRQ failed to assert"); + while(1); //halt + }//if (Usb.Init() == -1... +} + +void loop() +{ + uint8_t buf[ 12 ] = { 0 }; //buffer to convert unsigned long to ASCII + const char* sec_ela = " seconds elapsed\r"; + uint8_t rcode; + + Usb.Task(); + if( adk.isReady() == false ) { + return; + } + + ultoa( millis()/1000, (char *)buf, 10 ); + + rcode = adk.SndData( strlen((char *)buf), buf ); + rcode = adk.SndData( strlen( sec_ela), (uint8_t *)sec_ela ); + + delay( 1000 ); +} diff --git a/libraries/USB_Host_Shield/examples/board_qc/board_qc.ino b/libraries/USB_Host_Shield/examples/board_qc/board_qc.ino new file mode 100755 index 0000000..ddd1c5a --- /dev/null +++ b/libraries/USB_Host_Shield/examples/board_qc/board_qc.ino @@ -0,0 +1,258 @@ +/* USB Host Shield 2.0 board quality control routine */ +/* To see the output set your terminal speed to 115200 */ +/* for GPIO test to pass you need to connect GPIN0 to GPOUT7, GPIN1 to GPOUT6, etc. */ +/* otherwise press any key after getting GPIO error to complete the test */ +/**/ +#include +// Satisfy IDE, which only needs to see the include statment in the ino. +#ifdef dobogusinclude +#include +#include <../../../../hardware/pic32/libraries/SPI/SPI.h> // Hack to use the SPI library +#include // Hack to use the SPI library +#endif + +/* variables */ +uint8_t rcode; +uint8_t usbstate; +uint8_t laststate; +//uint8_t buf[sizeof(USB_DEVICE_DESCRIPTOR)]; +USB_DEVICE_DESCRIPTOR buf; + +/* objects */ +USB Usb; +//USBHub hub(&Usb); + +void setup() { + laststate = 0; + Serial.begin(115200); +#if !defined(__MIPSEL__) + while(!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection +#endif + E_Notify(PSTR("\r\nCircuits At Home 2011"), 0x80); + E_Notify(PSTR("\r\nUSB Host Shield Quality Control Routine"), 0x80); + /* SPI quick test - check revision register */ + E_Notify(PSTR("\r\nReading REVISION register... Die revision "), 0x80); + Usb.Init(); // Initializes SPI, we don't care about the return value here + { + uint8_t tmpbyte = Usb.regRd(rREVISION); + switch(tmpbyte) { + case( 0x01): //rev.01 + E_Notify(PSTR("01"), 0x80); + break; + case( 0x12): //rev.02 + E_Notify(PSTR("02"), 0x80); + break; + case( 0x13): //rev.03 + E_Notify(PSTR("03"), 0x80); + break; + default: + E_Notify(PSTR("invalid. Value returned: "), 0x80); + print_hex(tmpbyte, 8); + halt55(); + break; + }//switch( tmpbyte... + }//check revision register + /* SPI long test */ + { + E_Notify(PSTR("\r\nSPI long test. Transfers 1MB of data. Each dot is 64K"), 0x80); + uint8_t sample_wr = 0; + uint8_t sample_rd = 0; + uint8_t gpinpol_copy = Usb.regRd(rGPINPOL); + for(uint8_t i = 0; i < 16; i++) { + for(uint16_t j = 0; j < 65535; j++) { + Usb.regWr(rGPINPOL, sample_wr); + sample_rd = Usb.regRd(rGPINPOL); + if(sample_rd != sample_wr) { + E_Notify(PSTR("\r\nTest failed. "), 0x80); + E_Notify(PSTR("Value written: "), 0x80); + print_hex(sample_wr, 8); + E_Notify(PSTR(" read: "), 0x80); + print_hex(sample_rd, 8); + halt55(); + }//if( sample_rd != sample_wr.. + sample_wr++; + }//for( uint16_t j... + E_Notify(PSTR("."), 0x80); + }//for( uint8_t i... + Usb.regWr(rGPINPOL, gpinpol_copy); + E_Notify(PSTR(" SPI long test passed"), 0x80); + }//SPI long test + /* GPIO test */ + /* in order to simplify board layout, GPIN pins on text fixture are connected to GPOUT */ + /* in reverse order, i.e, GPIN0 is connected to GPOUT7, GPIN1 to GPOUT6, etc. */ + { + uint8_t tmpbyte; + E_Notify(PSTR("\r\nGPIO test. Connect GPIN0 to GPOUT7, GPIN1 to GPOUT6, and so on"), 0x80); + for(uint8_t sample_gpio = 0; sample_gpio < 255; sample_gpio++) { + Usb.gpioWr(sample_gpio); + tmpbyte = Usb.gpioRd(); + /* bit reversing code copied vetbatim from http://graphics.stanford.edu/~seander/bithacks.html#BitReverseObvious */ + tmpbyte = ((tmpbyte * 0x0802LU & 0x22110LU) | (tmpbyte * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16; + if(sample_gpio != tmpbyte) { + E_Notify(PSTR("\r\nTest failed. Value written: "), 0x80); + print_hex(sample_gpio, 8); + E_Notify(PSTR(" Value read: "), 0x80); + print_hex(tmpbyte, 8); + E_Notify(PSTR(" "), 0x80); + press_any_key(); + break; + }//if( sample_gpio != tmpbyte... + }//for( uint8_t sample_gpio... + E_Notify(PSTR("\r\nGPIO test passed."), 0x80); + }//GPIO test + /* PLL test. Stops/starts MAX3421E oscillator several times */ + { + E_Notify(PSTR("\r\nPLL test. 100 chip resets will be performed"), 0x80); + /* check current state of the oscillator */ + if(!(Usb.regRd(rUSBIRQ) & bmOSCOKIRQ)) { //wrong state - should be on + E_Notify(PSTR("\r\nCurrent oscillator state unexpected."), 0x80); + press_any_key(); + } + /* Restart oscillator */ + E_Notify(PSTR("\r\nResetting oscillator\r\n"), 0x80); + for(uint16_t i = 0; i < 100; i++) { + E_Notify(PSTR("\rReset number "), 0x80); + Serial.print(i, DEC); + Usb.regWr(rUSBCTL, bmCHIPRES); //reset + if(Usb.regRd(rUSBIRQ) & bmOSCOKIRQ) { //wrong state - should be off + E_Notify(PSTR("\r\nCurrent oscillator state unexpected."), 0x80); + halt55(); + } + Usb.regWr(rUSBCTL, 0x00); //release from reset + uint16_t j = 0; + for(j = 0; j < 65535; j++) { //tracking off to on time + if(Usb.regRd(rUSBIRQ) & bmOSCOKIRQ) { + E_Notify(PSTR(" Time to stabilize - "), 0x80); + Serial.print(j, DEC); + E_Notify(PSTR(" cycles\r\n"), 0x80); + break; + } + }//for( uint16_t j = 0; j < 65535; j++ + if(j == 0) { + E_Notify(PSTR("PLL failed to stabilize"), 0x80); + press_any_key(); + } + }//for( uint8_t i = 0; i < 255; i++ + + }//PLL test + /* initializing USB stack */ + if(Usb.Init() == -1) { + E_Notify(PSTR("\r\nOSCOKIRQ failed to assert"), 0x80); + halt55(); + } + E_Notify(PSTR("\r\nChecking USB device communication.\r\n"), 0x80); +} + +void loop() { + delay(200); + Usb.Task(); + usbstate = Usb.getUsbTaskState(); + if(usbstate != laststate) { + laststate = usbstate; + /**/ + switch(usbstate) { + case( USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE): + E_Notify(PSTR("\r\nWaiting for device..."), 0x80); + break; + case( USB_ATTACHED_SUBSTATE_RESET_DEVICE): + E_Notify(PSTR("\r\nDevice connected. Resetting..."), 0x80); + break; + case( USB_ATTACHED_SUBSTATE_WAIT_SOF): + E_Notify(PSTR("\r\nReset complete. Waiting for the first SOF..."), 0x80); + break; + case( USB_ATTACHED_SUBSTATE_GET_DEVICE_DESCRIPTOR_SIZE): + E_Notify(PSTR("\r\nSOF generation started. Enumerating device..."), 0x80); + break; + case( USB_STATE_ADDRESSING): + E_Notify(PSTR("\r\nSetting device address..."), 0x80); + break; + case( USB_STATE_RUNNING): + E_Notify(PSTR("\r\nGetting device descriptor"), 0x80); + rcode = Usb.getDevDescr(1, 0, sizeof (USB_DEVICE_DESCRIPTOR), (uint8_t*) & buf); + + if(rcode) { + E_Notify(PSTR("\r\nError reading device descriptor. Error code "), 0x80); + print_hex(rcode, 8); + } else { + /**/ + E_Notify(PSTR("\r\nDescriptor Length:\t"), 0x80); + print_hex(buf.bLength, 8); + E_Notify(PSTR("\r\nDescriptor type:\t"), 0x80); + print_hex(buf.bDescriptorType, 8); + E_Notify(PSTR("\r\nUSB version:\t\t"), 0x80); + print_hex(buf.bcdUSB, 16); + E_Notify(PSTR("\r\nDevice class:\t\t"), 0x80); + print_hex(buf.bDeviceClass, 8); + E_Notify(PSTR("\r\nDevice Subclass:\t"), 0x80); + print_hex(buf.bDeviceSubClass, 8); + E_Notify(PSTR("\r\nDevice Protocol:\t"), 0x80); + print_hex(buf.bDeviceProtocol, 8); + E_Notify(PSTR("\r\nMax.packet size:\t"), 0x80); + print_hex(buf.bMaxPacketSize0, 8); + E_Notify(PSTR("\r\nVendor ID:\t\t"), 0x80); + print_hex(buf.idVendor, 16); + E_Notify(PSTR("\r\nProduct ID:\t\t"), 0x80); + print_hex(buf.idProduct, 16); + E_Notify(PSTR("\r\nRevision ID:\t\t"), 0x80); + print_hex(buf.bcdDevice, 16); + E_Notify(PSTR("\r\nMfg.string index:\t"), 0x80); + print_hex(buf.iManufacturer, 8); + E_Notify(PSTR("\r\nProd.string index:\t"), 0x80); + print_hex(buf.iProduct, 8); + E_Notify(PSTR("\r\nSerial number index:\t"), 0x80); + print_hex(buf.iSerialNumber, 8); + E_Notify(PSTR("\r\nNumber of conf.:\t"), 0x80); + print_hex(buf.bNumConfigurations, 8); + /**/ + E_Notify(PSTR("\r\n\nAll tests passed. Press RESET to restart test"), 0x80); + while(1); + } + break; + case( USB_STATE_ERROR): + E_Notify(PSTR("\r\nUSB state machine reached error state"), 0x80); + break; + + default: + break; + }//switch( usbstate... + } +}//loop()... + +/* constantly transmits 0x55 via SPI to aid probing */ +void halt55() { + + E_Notify(PSTR("\r\nUnrecoverable error - test halted!!"), 0x80); + E_Notify(PSTR("\r\n0x55 pattern is transmitted via SPI"), 0x80); + E_Notify(PSTR("\r\nPress RESET to restart test"), 0x80); + + while(1) { + Usb.regWr(0x55, 0x55); + } +} + +/* prints hex numbers with leading zeroes */ +void print_hex(int v, int num_places) { + int mask = 0, n, num_nibbles, digit; + + for(n = 1; n <= num_places; n++) { + mask = (mask << 1) | 0x0001; + } + v = v & mask; // truncate v to specified number of places + + num_nibbles = num_places / 4; + if((num_places % 4) != 0) { + ++num_nibbles; + } + do { + digit = ((v >> (num_nibbles - 1) * 4)) & 0x0f; + Serial.print(digit, HEX); + } while(--num_nibbles); +} + +/* prints "Press any key" and returns when key is pressed */ +void press_any_key() { + E_Notify(PSTR("\r\nPress any key to continue..."), 0x80); + while(Serial.available() <= 0); //wait for input + Serial.read(); //empty input buffer + return; +} diff --git a/libraries/USB_Host_Shield/examples/cdc_XR21B1411/XR_terminal/XR_terminal.ino b/libraries/USB_Host_Shield/examples/cdc_XR21B1411/XR_terminal/XR_terminal.ino new file mode 100755 index 0000000..0173a08 --- /dev/null +++ b/libraries/USB_Host_Shield/examples/cdc_XR21B1411/XR_terminal/XR_terminal.ino @@ -0,0 +1,83 @@ +#include + +// Satisfy IDE, which only needs to see the include statment in the ino. +#ifdef dobogusinclude +#include +#include +#endif + +class ACMAsyncOper : public CDCAsyncOper +{ +public: + uint8_t OnInit(ACM *pacm); +}; + +uint8_t ACMAsyncOper::OnInit(ACM *pacm) +{ + uint8_t rcode; + // Set DTR = 1 RTS=1 + rcode = pacm->SetControlLineState(3); + + if (rcode) + { + ErrorMessage(PSTR("SetControlLineState"), rcode); + return rcode; + } + + LINE_CODING lc; + lc.dwDTERate = 115200; + lc.bCharFormat = 0; + lc.bParityType = 0; + lc.bDataBits = 8; + + rcode = pacm->SetLineCoding(&lc); + + if (rcode) + ErrorMessage(PSTR("SetLineCoding"), rcode); + + return rcode; +} + +USB Usb; +ACMAsyncOper AsyncOper; +XR21B1411 Acm(&Usb, &AsyncOper); + +void setup() { + Serial.begin( 115200 ); +#if !defined(__MIPSEL__) + while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection +#endif + Serial.println("\r\n\r\nStart"); + + if (Usb.Init() == -1) Serial.println("OSCOKIRQ failed to assert"); +} + +void loop() { + Usb.Task(); + if( Acm.isReady()) { + uint8_t rcode; + uint8_t buf[1]; + uint16_t rcvd = 1; + + /* read keyboard */ + if(Serial.available()) { + uint8_t data = Serial.read(); + /* send */ + rcode = Acm.SndData(1, &data); + if (rcode) + ErrorMessage(PSTR("SndData"), rcode); + } + + /* read XR serial */ + rcode = Acm.RcvData(&rcvd, buf); + if (rcode && rcode != hrNAK) + ErrorMessage(PSTR("Ret"), rcode); + + if( rcvd ) { //more than zero bytes received + for(uint16_t i=0; i < rcvd; i++ ) { + Serial.print((char)buf[i]); + } + } + } +} + diff --git a/libraries/USB_Host_Shield/examples/ftdi/USBFTDILoopback/USBFTDILoopback.ino b/libraries/USB_Host_Shield/examples/ftdi/USBFTDILoopback/USBFTDILoopback.ino new file mode 100755 index 0000000..b1dd1fd --- /dev/null +++ b/libraries/USB_Host_Shield/examples/ftdi/USBFTDILoopback/USBFTDILoopback.ino @@ -0,0 +1,97 @@ +#include +#include + +#include "pgmstrings.h" +// Satisfy IDE, which only needs to see the include statment in the ino. +#ifdef dobogusinclude +#include +#include +#endif + +class FTDIAsync : public FTDIAsyncOper +{ +public: + uint8_t OnInit(FTDI *pftdi); +}; + +uint8_t FTDIAsync::OnInit(FTDI *pftdi) +{ + uint8_t rcode = 0; + + rcode = pftdi->SetBaudRate(115200); + + if (rcode) + { + ErrorMessage(PSTR("SetBaudRate"), rcode); + return rcode; + } + rcode = pftdi->SetFlowControl(FTDI_SIO_DISABLE_FLOW_CTRL); + + if (rcode) + ErrorMessage(PSTR("SetFlowControl"), rcode); + + return rcode; +} + +USB Usb; +//USBHub Hub(&Usb); +FTDIAsync FtdiAsync; +FTDI Ftdi(&Usb, &FtdiAsync); + +uint32_t next_time; + +void setup() +{ + Serial.begin( 115200 ); +#if !defined(__MIPSEL__) + while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection +#endif + Serial.println("Start"); + + if (Usb.Init() == -1) + Serial.println("OSC did not start."); + + delay( 200 ); + + next_time = millis() + 5000; +} + +void loop() +{ + Usb.Task(); + + if( Usb.getUsbTaskState() == USB_STATE_RUNNING ) + { + uint8_t rcode; + char strbuf[] = "DEADBEEF"; + //char strbuf[] = "The quick brown fox jumps over the lazy dog"; + //char strbuf[] = "This string contains 61 character to demonstrate FTDI buffers"; //add one symbol to it to see some garbage + Serial.print("."); + + rcode = Ftdi.SndData(strlen(strbuf), (uint8_t*)strbuf); + + if (rcode) + ErrorMessage(PSTR("SndData"), rcode); + + delay(50); + + uint8_t buf[64]; + + for (uint8_t i=0; i<64; i++) + buf[i] = 0; + + uint16_t rcvd = 64; + rcode = Ftdi.RcvData(&rcvd, buf); + + if (rcode && rcode != hrNAK) + ErrorMessage(PSTR("Ret"), rcode); + + // The device reserves the first two bytes of data + // to contain the current values of the modem and line status registers. + if (rcvd > 2) + Serial.print((char*)(buf+2)); + + delay(10); + } +} + diff --git a/libraries/USB_Host_Shield/examples/ftdi/USBFTDILoopback/pgmstrings.h b/libraries/USB_Host_Shield/examples/ftdi/USBFTDILoopback/pgmstrings.h new file mode 100755 index 0000000..bdb0077 --- /dev/null +++ b/libraries/USB_Host_Shield/examples/ftdi/USBFTDILoopback/pgmstrings.h @@ -0,0 +1,52 @@ +#if !defined(__PGMSTRINGS_H__) +#define __PGMSTRINGS_H__ + +#define LOBYTE(x) ((char*)(&(x)))[0] +#define HIBYTE(x) ((char*)(&(x)))[1] +#define BUFSIZE 256 //buffer size + + +/* Print strings in Program Memory */ +const char Gen_Error_str[] PROGMEM = "\r\nRequest error. Error code:\t"; +const char Dev_Header_str[] PROGMEM ="\r\nDevice descriptor: "; +const char Dev_Length_str[] PROGMEM ="\r\nDescriptor Length:\t"; +const char Dev_Type_str[] PROGMEM ="\r\nDescriptor type:\t"; +const char Dev_Version_str[] PROGMEM ="\r\nUSB version:\t\t"; +const char Dev_Class_str[] PROGMEM ="\r\nDevice class:\t\t"; +const char Dev_Subclass_str[] PROGMEM ="\r\nDevice Subclass:\t"; +const char Dev_Protocol_str[] PROGMEM ="\r\nDevice Protocol:\t"; +const char Dev_Pktsize_str[] PROGMEM ="\r\nMax.packet size:\t"; +const char Dev_Vendor_str[] PROGMEM ="\r\nVendor ID:\t\t"; +const char Dev_Product_str[] PROGMEM ="\r\nProduct ID:\t\t"; +const char Dev_Revision_str[] PROGMEM ="\r\nRevision ID:\t\t"; +const char Dev_Mfg_str[] PROGMEM ="\r\nMfg.string index:\t"; +const char Dev_Prod_str[] PROGMEM ="\r\nProd.string index:\t"; +const char Dev_Serial_str[] PROGMEM ="\r\nSerial number index:\t"; +const char Dev_Nconf_str[] PROGMEM ="\r\nNumber of conf.:\t"; +const char Conf_Trunc_str[] PROGMEM ="Total length truncated to 256 bytes"; +const char Conf_Header_str[] PROGMEM ="\r\nConfiguration descriptor:"; +const char Conf_Totlen_str[] PROGMEM ="\r\nTotal length:\t\t"; +const char Conf_Nint_str[] PROGMEM ="\r\nNum.intf:\t\t"; +const char Conf_Value_str[] PROGMEM ="\r\nConf.value:\t\t"; +const char Conf_String_str[] PROGMEM ="\r\nConf.string:\t\t"; +const char Conf_Attr_str[] PROGMEM ="\r\nAttr.:\t\t\t"; +const char Conf_Pwr_str[] PROGMEM ="\r\nMax.pwr:\t\t"; +const char Int_Header_str[] PROGMEM ="\r\n\r\nInterface descriptor:"; +const char Int_Number_str[] PROGMEM ="\r\nIntf.number:\t\t"; +const char Int_Alt_str[] PROGMEM ="\r\nAlt.:\t\t\t"; +const char Int_Endpoints_str[] PROGMEM ="\r\nEndpoints:\t\t"; +const char Int_Class_str[] PROGMEM ="\r\nIntf. Class:\t\t"; +const char Int_Subclass_str[] PROGMEM ="\r\nIntf. Subclass:\t\t"; +const char Int_Protocol_str[] PROGMEM ="\r\nIntf. Protocol:\t\t"; +const char Int_String_str[] PROGMEM ="\r\nIntf.string:\t\t"; +const char End_Header_str[] PROGMEM ="\r\n\r\nEndpoint descriptor:"; +const char End_Address_str[] PROGMEM ="\r\nEndpoint address:\t"; +const char End_Attr_str[] PROGMEM ="\r\nAttr.:\t\t\t"; +const char End_Pktsize_str[] PROGMEM ="\r\nMax.pkt size:\t\t"; +const char End_Interval_str[] PROGMEM ="\r\nPolling interval:\t"; +const char Unk_Header_str[] PROGMEM = "\r\nUnknown descriptor:"; +const char Unk_Length_str[] PROGMEM ="\r\nLength:\t\t"; +const char Unk_Type_str[] PROGMEM ="\r\nType:\t\t"; +const char Unk_Contents_str[] PROGMEM ="\r\nContents:\t"; + +#endif // __PGMSTRINGS_H__ \ No newline at end of file diff --git a/libraries/USB_Host_Shield/examples/hub_demo/hub_demo.ino b/libraries/USB_Host_Shield/examples/hub_demo/hub_demo.ino new file mode 100755 index 0000000..6f6cbaf --- /dev/null +++ b/libraries/USB_Host_Shield/examples/hub_demo/hub_demo.ino @@ -0,0 +1,344 @@ +#include +#include "pgmstrings.h" +// Satisfy IDE, which only needs to see the include statment in the ino. +#ifdef dobogusinclude +#include +#include +#endif + +USB Usb; +USBHub Hub1(&Usb); +USBHub Hub2(&Usb); +USBHub Hub3(&Usb); +USBHub Hub4(&Usb); + +uint32_t next_time; + +void PrintAllAddresses(UsbDevice *pdev) +{ + UsbDeviceAddress adr; + adr.devAddress = pdev->address.devAddress; + Serial.print("\r\nAddr:"); + Serial.print(adr.devAddress, HEX); + Serial.print("("); + Serial.print(adr.bmHub, HEX); + Serial.print("."); + Serial.print(adr.bmParent, HEX); + Serial.print("."); + Serial.print(adr.bmAddress, HEX); + Serial.println(")"); +} + +void PrintAddress(uint8_t addr) +{ + UsbDeviceAddress adr; + adr.devAddress = addr; + Serial.print("\r\nADDR:\t"); + Serial.println(adr.devAddress,HEX); + Serial.print("DEV:\t"); + Serial.println(adr.bmAddress,HEX); + Serial.print("PRNT:\t"); + Serial.println(adr.bmParent,HEX); + Serial.print("HUB:\t"); + Serial.println(adr.bmHub,HEX); +} + +void setup() +{ + Serial.begin( 115200 ); +#if !defined(__MIPSEL__) + while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection +#endif + Serial.println("Start"); + + if (Usb.Init() == -1) + Serial.println("OSC did not start."); + + delay( 200 ); + + next_time = millis() + 10000; +} + +byte getdevdescr( byte addr, byte &num_conf ); + +void PrintDescriptors(uint8_t addr) +{ + uint8_t rcode = 0; + byte num_conf = 0; + + rcode = getdevdescr( (byte)addr, num_conf ); + if( rcode ) + { + printProgStr(Gen_Error_str); + print_hex( rcode, 8 ); + } + Serial.print("\r\n"); + + for (int i=0; iaddress.devAddress, 8); + Serial.println("\r\n--"); + PrintDescriptors( pdev->address.devAddress ); +} + +void loop() +{ + Usb.Task(); + + if( Usb.getUsbTaskState() == USB_STATE_RUNNING ) + { + if ((millis() - next_time) >= 0L) + { + Usb.ForEachUsbDevice(&PrintAllDescriptors); + Usb.ForEachUsbDevice(&PrintAllAddresses); + + while( 1 ); //stop + } + } +} + +byte getdevdescr( byte addr, byte &num_conf ) +{ + USB_DEVICE_DESCRIPTOR buf; + byte rcode; + rcode = Usb.getDevDescr( addr, 0, 0x12, ( uint8_t *)&buf ); + if( rcode ) { + return( rcode ); + } + printProgStr(Dev_Header_str); + printProgStr(Dev_Length_str); + print_hex( buf.bLength, 8 ); + printProgStr(Dev_Type_str); + print_hex( buf.bDescriptorType, 8 ); + printProgStr(Dev_Version_str); + print_hex( buf.bcdUSB, 16 ); + printProgStr(Dev_Class_str); + print_hex( buf.bDeviceClass, 8 ); + printProgStr(Dev_Subclass_str); + print_hex( buf.bDeviceSubClass, 8 ); + printProgStr(Dev_Protocol_str); + print_hex( buf.bDeviceProtocol, 8 ); + printProgStr(Dev_Pktsize_str); + print_hex( buf.bMaxPacketSize0, 8 ); + printProgStr(Dev_Vendor_str); + print_hex( buf.idVendor, 16 ); + printProgStr(Dev_Product_str); + print_hex( buf.idProduct, 16 ); + printProgStr(Dev_Revision_str); + print_hex( buf.bcdDevice, 16 ); + printProgStr(Dev_Mfg_str); + print_hex( buf.iManufacturer, 8 ); + printProgStr(Dev_Prod_str); + print_hex( buf.iProduct, 8 ); + printProgStr(Dev_Serial_str); + print_hex( buf.iSerialNumber, 8 ); + printProgStr(Dev_Nconf_str); + print_hex( buf.bNumConfigurations, 8 ); + num_conf = buf.bNumConfigurations; + return( 0 ); +} + +void printhubdescr(uint8_t *descrptr, uint8_t addr) +{ + HubDescriptor *pHub = (HubDescriptor*) descrptr; + uint8_t len = *((uint8_t*)descrptr); + + printProgStr(PSTR("\r\n\r\nHub Descriptor:\r\n")); + printProgStr(PSTR("bDescLength:\t\t")); + Serial.println(pHub->bDescLength, HEX); + + printProgStr(PSTR("bDescriptorType:\t")); + Serial.println(pHub->bDescriptorType, HEX); + + printProgStr(PSTR("bNbrPorts:\t\t")); + Serial.println(pHub->bNbrPorts, HEX); + + printProgStr(PSTR("LogPwrSwitchMode:\t")); + Serial.println(pHub->LogPwrSwitchMode, BIN); + + printProgStr(PSTR("CompoundDevice:\t\t")); + Serial.println(pHub->CompoundDevice, BIN); + + printProgStr(PSTR("OverCurrentProtectMode:\t")); + Serial.println(pHub->OverCurrentProtectMode, BIN); + + printProgStr(PSTR("TTThinkTime:\t\t")); + Serial.println(pHub->TTThinkTime, BIN); + + printProgStr(PSTR("PortIndicatorsSupported:")); + Serial.println(pHub->PortIndicatorsSupported, BIN); + + printProgStr(PSTR("Reserved:\t\t")); + Serial.println(pHub->Reserved, HEX); + + printProgStr(PSTR("bPwrOn2PwrGood:\t\t")); + Serial.println(pHub->bPwrOn2PwrGood, HEX); + + printProgStr(PSTR("bHubContrCurrent:\t")); + Serial.println(pHub->bHubContrCurrent, HEX); + + for (uint8_t i=7; ibNbrPorts; i++) + // PrintHubPortStatus(&Usb, addr, i, 1); +} + +byte getconfdescr( byte addr, byte conf ) +{ + uint8_t buf[ BUFSIZE ]; + uint8_t* buf_ptr = buf; + byte rcode; + byte descr_length; + byte descr_type; + unsigned int total_length; + rcode = Usb.getConfDescr( addr, 0, 4, conf, buf ); //get total length + LOBYTE( total_length ) = buf[ 2 ]; + HIBYTE( total_length ) = buf[ 3 ]; + if( total_length > 256 ) { //check if total length is larger than buffer + printProgStr(Conf_Trunc_str); + total_length = 256; + } + rcode = Usb.getConfDescr( addr, 0, total_length, conf, buf ); //get the whole descriptor + while( buf_ptr < buf + total_length ) { //parsing descriptors + descr_length = *( buf_ptr ); + descr_type = *( buf_ptr + 1 ); + switch( descr_type ) { + case( USB_DESCRIPTOR_CONFIGURATION ): + printconfdescr( buf_ptr ); + break; + case( USB_DESCRIPTOR_INTERFACE ): + printintfdescr( buf_ptr ); + break; + case( USB_DESCRIPTOR_ENDPOINT ): + printepdescr( buf_ptr ); + break; + case 0x29: + printhubdescr( buf_ptr, addr ); + break; + default: + printunkdescr( buf_ptr ); + break; + }//switch( descr_type + buf_ptr = ( buf_ptr + descr_length ); //advance buffer pointer + }//while( buf_ptr <=... + return( 0 ); +} +/* prints hex numbers with leading zeroes */ +// copyright, Peter H Anderson, Baltimore, MD, Nov, '07 +// source: http://www.phanderson.com/arduino/arduino_display.html +void print_hex(int v, int num_places) +{ + int mask=0, n, num_nibbles, digit; + + for (n=1; n<=num_places; n++) { + mask = (mask << 1) | 0x0001; + } + v = v & mask; // truncate v to specified number of places + + num_nibbles = num_places / 4; + if ((num_places % 4) != 0) { + ++num_nibbles; + } + do { + digit = ((v >> (num_nibbles-1) * 4)) & 0x0f; + Serial.print(digit, HEX); + } + while(--num_nibbles); +} +/* function to print configuration descriptor */ +void printconfdescr( uint8_t* descr_ptr ) +{ + USB_CONFIGURATION_DESCRIPTOR* conf_ptr = ( USB_CONFIGURATION_DESCRIPTOR* )descr_ptr; + printProgStr(Conf_Header_str); + printProgStr(Conf_Totlen_str); + print_hex( conf_ptr->wTotalLength, 16 ); + printProgStr(Conf_Nint_str); + print_hex( conf_ptr->bNumInterfaces, 8 ); + printProgStr(Conf_Value_str); + print_hex( conf_ptr->bConfigurationValue, 8 ); + printProgStr(Conf_String_str); + print_hex( conf_ptr->iConfiguration, 8 ); + printProgStr(Conf_Attr_str); + print_hex( conf_ptr->bmAttributes, 8 ); + printProgStr(Conf_Pwr_str); + print_hex( conf_ptr->bMaxPower, 8 ); + return; +} +/* function to print interface descriptor */ +void printintfdescr( uint8_t* descr_ptr ) +{ + USB_INTERFACE_DESCRIPTOR* intf_ptr = ( USB_INTERFACE_DESCRIPTOR* )descr_ptr; + printProgStr(Int_Header_str); + printProgStr(Int_Number_str); + print_hex( intf_ptr->bInterfaceNumber, 8 ); + printProgStr(Int_Alt_str); + print_hex( intf_ptr->bAlternateSetting, 8 ); + printProgStr(Int_Endpoints_str); + print_hex( intf_ptr->bNumEndpoints, 8 ); + printProgStr(Int_Class_str); + print_hex( intf_ptr->bInterfaceClass, 8 ); + printProgStr(Int_Subclass_str); + print_hex( intf_ptr->bInterfaceSubClass, 8 ); + printProgStr(Int_Protocol_str); + print_hex( intf_ptr->bInterfaceProtocol, 8 ); + printProgStr(Int_String_str); + print_hex( intf_ptr->iInterface, 8 ); + return; +} +/* function to print endpoint descriptor */ +void printepdescr( uint8_t* descr_ptr ) +{ + USB_ENDPOINT_DESCRIPTOR* ep_ptr = ( USB_ENDPOINT_DESCRIPTOR* )descr_ptr; + printProgStr(End_Header_str); + printProgStr(End_Address_str); + print_hex( ep_ptr->bEndpointAddress, 8 ); + printProgStr(End_Attr_str); + print_hex( ep_ptr->bmAttributes, 8 ); + printProgStr(End_Pktsize_str); + print_hex( ep_ptr->wMaxPacketSize, 16 ); + printProgStr(End_Interval_str); + print_hex( ep_ptr->bInterval, 8 ); + + return; +} +/*function to print unknown descriptor */ +void printunkdescr( uint8_t* descr_ptr ) +{ + byte length = *descr_ptr; + byte i; + printProgStr(Unk_Header_str); + printProgStr(Unk_Length_str); + print_hex( *descr_ptr, 8 ); + printProgStr(Unk_Type_str); + print_hex( *(descr_ptr + 1 ), 8 ); + printProgStr(Unk_Contents_str); + descr_ptr += 2; + for( i = 0; i < length; i++ ) { + print_hex( *descr_ptr, 8 ); + descr_ptr++; + } +} + + +/* Print a string from Program Memory directly to save RAM */ +void printProgStr(prog_char str[]) +{ + char c; + if(!str) return; + while((c = pgm_read_byte(str++))) + Serial.print(c); +} diff --git a/libraries/USB_Host_Shield/examples/hub_demo/pgmstrings.h b/libraries/USB_Host_Shield/examples/hub_demo/pgmstrings.h new file mode 100755 index 0000000..bdb0077 --- /dev/null +++ b/libraries/USB_Host_Shield/examples/hub_demo/pgmstrings.h @@ -0,0 +1,52 @@ +#if !defined(__PGMSTRINGS_H__) +#define __PGMSTRINGS_H__ + +#define LOBYTE(x) ((char*)(&(x)))[0] +#define HIBYTE(x) ((char*)(&(x)))[1] +#define BUFSIZE 256 //buffer size + + +/* Print strings in Program Memory */ +const char Gen_Error_str[] PROGMEM = "\r\nRequest error. Error code:\t"; +const char Dev_Header_str[] PROGMEM ="\r\nDevice descriptor: "; +const char Dev_Length_str[] PROGMEM ="\r\nDescriptor Length:\t"; +const char Dev_Type_str[] PROGMEM ="\r\nDescriptor type:\t"; +const char Dev_Version_str[] PROGMEM ="\r\nUSB version:\t\t"; +const char Dev_Class_str[] PROGMEM ="\r\nDevice class:\t\t"; +const char Dev_Subclass_str[] PROGMEM ="\r\nDevice Subclass:\t"; +const char Dev_Protocol_str[] PROGMEM ="\r\nDevice Protocol:\t"; +const char Dev_Pktsize_str[] PROGMEM ="\r\nMax.packet size:\t"; +const char Dev_Vendor_str[] PROGMEM ="\r\nVendor ID:\t\t"; +const char Dev_Product_str[] PROGMEM ="\r\nProduct ID:\t\t"; +const char Dev_Revision_str[] PROGMEM ="\r\nRevision ID:\t\t"; +const char Dev_Mfg_str[] PROGMEM ="\r\nMfg.string index:\t"; +const char Dev_Prod_str[] PROGMEM ="\r\nProd.string index:\t"; +const char Dev_Serial_str[] PROGMEM ="\r\nSerial number index:\t"; +const char Dev_Nconf_str[] PROGMEM ="\r\nNumber of conf.:\t"; +const char Conf_Trunc_str[] PROGMEM ="Total length truncated to 256 bytes"; +const char Conf_Header_str[] PROGMEM ="\r\nConfiguration descriptor:"; +const char Conf_Totlen_str[] PROGMEM ="\r\nTotal length:\t\t"; +const char Conf_Nint_str[] PROGMEM ="\r\nNum.intf:\t\t"; +const char Conf_Value_str[] PROGMEM ="\r\nConf.value:\t\t"; +const char Conf_String_str[] PROGMEM ="\r\nConf.string:\t\t"; +const char Conf_Attr_str[] PROGMEM ="\r\nAttr.:\t\t\t"; +const char Conf_Pwr_str[] PROGMEM ="\r\nMax.pwr:\t\t"; +const char Int_Header_str[] PROGMEM ="\r\n\r\nInterface descriptor:"; +const char Int_Number_str[] PROGMEM ="\r\nIntf.number:\t\t"; +const char Int_Alt_str[] PROGMEM ="\r\nAlt.:\t\t\t"; +const char Int_Endpoints_str[] PROGMEM ="\r\nEndpoints:\t\t"; +const char Int_Class_str[] PROGMEM ="\r\nIntf. Class:\t\t"; +const char Int_Subclass_str[] PROGMEM ="\r\nIntf. Subclass:\t\t"; +const char Int_Protocol_str[] PROGMEM ="\r\nIntf. Protocol:\t\t"; +const char Int_String_str[] PROGMEM ="\r\nIntf.string:\t\t"; +const char End_Header_str[] PROGMEM ="\r\n\r\nEndpoint descriptor:"; +const char End_Address_str[] PROGMEM ="\r\nEndpoint address:\t"; +const char End_Attr_str[] PROGMEM ="\r\nAttr.:\t\t\t"; +const char End_Pktsize_str[] PROGMEM ="\r\nMax.pkt size:\t\t"; +const char End_Interval_str[] PROGMEM ="\r\nPolling interval:\t"; +const char Unk_Header_str[] PROGMEM = "\r\nUnknown descriptor:"; +const char Unk_Length_str[] PROGMEM ="\r\nLength:\t\t"; +const char Unk_Type_str[] PROGMEM ="\r\nType:\t\t"; +const char Unk_Contents_str[] PROGMEM ="\r\nContents:\t"; + +#endif // __PGMSTRINGS_H__ \ No newline at end of file diff --git a/libraries/USB_Host_Shield/examples/max_LCD/max_LCD.ino b/libraries/USB_Host_Shield/examples/max_LCD/max_LCD.ino new file mode 100755 index 0000000..82a0967 --- /dev/null +++ b/libraries/USB_Host_Shield/examples/max_LCD/max_LCD.ino @@ -0,0 +1,28 @@ +// Just a copy of the HelloWorld example bundled with the LiquidCrystal library in the Arduino IDE + +// HD44780 compatible LCD display via MAX3421E GPOUT support header +// pinout: D[4-7] -> GPOUT[4-7], RS-> GPOUT[2], E ->GPOUT[3] + +#include +// Satisfy IDE, which only needs to see the include statment in the ino. +#ifdef dobogusinclude +#include +#include +#endif + +USB Usb; +Max_LCD lcd(&Usb); + +void setup() { + // Set up the LCD's number of columns and rows: + lcd.begin(16, 2); + // Print a message to the LCD. + lcd.print("Hello, World!"); +} + +void loop() { + // Set the cursor to column 0, line 1 (note: line 1 is the second row, since counting begins with 0): + lcd.setCursor(0, 1); + // Print the number of seconds since reset: + lcd.print(millis() / 1000); +} diff --git a/libraries/USB_Host_Shield/examples/pl2303/pl2303_gprs_terminal/pl2303_gprs_terminal.ino b/libraries/USB_Host_Shield/examples/pl2303/pl2303_gprs_terminal/pl2303_gprs_terminal.ino new file mode 100755 index 0000000..ae94a81 --- /dev/null +++ b/libraries/USB_Host_Shield/examples/pl2303/pl2303_gprs_terminal/pl2303_gprs_terminal.ino @@ -0,0 +1,100 @@ +/* Arduino terminal for PL2303 USB to serial converter and DealeXtreme GPRS modem. */ +/* USB support */ +#include +/* CDC support */ +#include +#include +// Satisfy IDE, which only needs to see the include statment in the ino. +#ifdef dobogusinclude +#include +#include +#endif + +class PLAsyncOper : public CDCAsyncOper +{ +public: + uint8_t OnInit(ACM *pacm); +}; + +uint8_t PLAsyncOper::OnInit(ACM *pacm) +{ + uint8_t rcode; + + // Set DTR = 1 + rcode = pacm->SetControlLineState(1); + + if (rcode) + { + ErrorMessage(PSTR("SetControlLineState"), rcode); + return rcode; + } + + LINE_CODING lc; + //lc.dwDTERate = 9600; + lc.dwDTERate = 115200; + lc.bCharFormat = 0; + lc.bParityType = 0; + lc.bDataBits = 8; + + rcode = pacm->SetLineCoding(&lc); + + if (rcode) + ErrorMessage(PSTR("SetLineCoding"), rcode); + + return rcode; +} +USB Usb; +//USBHub Hub(&Usb); +PLAsyncOper AsyncOper; +PL2303 Pl(&Usb, &AsyncOper); + +void setup() +{ + Serial.begin( 115200 ); +#if !defined(__MIPSEL__) + while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection +#endif + Serial.println("Start"); + + if (Usb.Init() == -1) + Serial.println("OSCOKIRQ failed to assert"); + + delay( 200 ); +} + +void loop() +{ + Usb.Task(); + + if( Usb.getUsbTaskState() == USB_STATE_RUNNING ) + { + uint8_t rcode; + + /* reading the keyboard */ + if(Serial.available()) { + uint8_t data= Serial.read(); + + /* sending to the phone */ + rcode = Pl.SndData(1, &data); + if (rcode) + ErrorMessage(PSTR("SndData"), rcode); + }//if(Serial.available()... + + /* reading the converter */ + /* buffer size must be greater or equal to max.packet size */ + /* it it set to 64 (largest possible max.packet size) here, can be tuned down + for particular endpoint */ + uint8_t buf[64]; + uint16_t rcvd = 64; + rcode = Pl.RcvData(&rcvd, buf); + if (rcode && rcode != hrNAK) + ErrorMessage(PSTR("Ret"), rcode); + + if( rcvd ) { //more than zero bytes received + for(uint16_t i=0; i < rcvd; i++ ) { + Serial.print((char)buf[i]); //printing on the screen + } + }//if( rcvd ... + }//if( Usb.getUsbTaskState() == USB_STATE_RUNNING.. +} + diff --git a/libraries/USB_Host_Shield/examples/pl2303/pl2303_gps/pl2303_gps.ino b/libraries/USB_Host_Shield/examples/pl2303/pl2303_gps/pl2303_gps.ino new file mode 100755 index 0000000..4ca6eea --- /dev/null +++ b/libraries/USB_Host_Shield/examples/pl2303/pl2303_gps/pl2303_gps.ino @@ -0,0 +1,87 @@ +/* USB Host to PL2303-based USB GPS unit interface */ +/* Navibee GM720 receiver - Sirf Star III */ +/* USB support */ +#include +/* CDC support */ +#include +#include +// Satisfy IDE, which only needs to see the include statment in the ino. +#ifdef dobogusinclude +#include +#include +#endif + +class PLAsyncOper : public CDCAsyncOper { +public: + uint8_t OnInit(ACM *pacm); +}; + +uint8_t PLAsyncOper::OnInit(ACM *pacm) { + uint8_t rcode; + + // Set DTR = 1 + rcode = pacm->SetControlLineState(1); + + if(rcode) { + ErrorMessage(PSTR("SetControlLineState"), rcode); + return rcode; + } + + LINE_CODING lc; + lc.dwDTERate = 4800; //default serial speed of GPS unit + lc.bCharFormat = 0; + lc.bParityType = 0; + lc.bDataBits = 8; + + rcode = pacm->SetLineCoding(&lc); + + if(rcode) + ErrorMessage(PSTR("SetLineCoding"), rcode); + + return rcode; +} + +USB Usb; +USBHub Hub(&Usb); +PLAsyncOper AsyncOper; +PL2303 Pl(&Usb, &AsyncOper); +uint32_t read_delay; +#define READ_DELAY 100 + +void setup() { + Serial.begin(115200); +#if !defined(__MIPSEL__) + while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection +#endif + Serial.println("Start"); + + if(Usb.Init() == -1) + Serial.println("OSCOKIRQ failed to assert"); + + delay(200); +} + +void loop() { + uint8_t rcode; + uint8_t buf[64]; //serial buffer equals Max.packet size of bulk-IN endpoint + uint16_t rcvd = 64; + + Usb.Task(); + + if(Pl.isReady()) { + /* reading the GPS */ + if((long)(millis() - read_delay) >= 0L) { + read_delay += READ_DELAY; + rcode = Pl.RcvData(&rcvd, buf); + if(rcode && rcode != hrNAK) + ErrorMessage(PSTR("Ret"), rcode); + if(rcvd) { //more than zero bytes received + for(uint16_t i = 0; i < rcvd; i++) { + Serial.print((char)buf[i]); //printing on the screen + }//for( uint16_t i=0; i < rcvd; i++... + }//if( rcvd + }//if( read_delay > millis()... + }//if( Usb.getUsbTaskState() == USB_STATE_RUNNING.. +} + + diff --git a/libraries/USB_Host_Shield/examples/pl2303/pl2303_tinygps/pl2303_tinygps.ino b/libraries/USB_Host_Shield/examples/pl2303/pl2303_tinygps/pl2303_tinygps.ino new file mode 100755 index 0000000..6184360 --- /dev/null +++ b/libraries/USB_Host_Shield/examples/pl2303/pl2303_tinygps/pl2303_tinygps.ino @@ -0,0 +1,216 @@ +/* USB Host to PL2303-based USB GPS unit interface */ +/* Navibee GM720 receiver - Sirf Star III */ +/* Mikal Hart's TinyGPS library */ +/* test_with_gps_device library example modified for PL2302 access */ + +/* USB support */ +#include + +/* CDC support */ +#include +#include + +#include +// Satisfy IDE, which only needs to see the include statment in the ino. +#ifdef dobogusinclude +#include +#include +#endif + +/* This sample code demonstrates the normal use of a TinyGPS object. + Modified to be used with USB Host Shield Library r2.0 + and USB Host Shield 2.0 +*/ + +class PLAsyncOper : public CDCAsyncOper +{ +public: + uint8_t OnInit(ACM *pacm); +}; + +uint8_t PLAsyncOper::OnInit(ACM *pacm) +{ + uint8_t rcode; + + // Set DTR = 1 + rcode = pacm->SetControlLineState(1); + + if (rcode) { + ErrorMessage(PSTR("SetControlLineState"), rcode); + return rcode; + } + + LINE_CODING lc; + lc.dwDTERate = 4800; //default serial speed of GPS unit + lc.bCharFormat = 0; + lc.bParityType = 0; + lc.bDataBits = 8; + + rcode = pacm->SetLineCoding(&lc); + + if (rcode) { + ErrorMessage(PSTR("SetLineCoding"), rcode); + } + + return rcode; +} + +USB Usb; +//USBHub Hub(&Usb); +PLAsyncOper AsyncOper; +PL2303 Pl(&Usb, &AsyncOper); +TinyGPS gps; + +void gpsdump(TinyGPS &gps); +bool feedgps(); +void printFloat(double f, int digits = 2); + +void setup() +{ + + Serial.begin(115200); +#if !defined(__MIPSEL__) + while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection +#endif + + Serial.print("Testing TinyGPS library v. "); Serial.println(TinyGPS::library_version()); + Serial.println("by Mikal Hart"); + Serial.println(); + Serial.print("Sizeof(gpsobject) = "); Serial.println(sizeof(TinyGPS)); + Serial.println(); + /* USB Initialization */ + if (Usb.Init() == -1) { + Serial.println("OSCOKIRQ failed to assert"); + } + + delay( 200 ); +} + +void loop() +{ + Usb.Task(); + + if( Pl.isReady()) { + + bool newdata = false; + unsigned long start = millis(); + + // Every 5 seconds we print an update + while (millis() - start < 5000) { + if( feedgps()) { + newdata = true; + } + }//while (millis()... + + if (newdata) { + Serial.println("Acquired Data"); + Serial.println("-------------"); + gpsdump(gps); + Serial.println("-------------"); + Serial.println(); + }//if( newdata... + }//if( Usb.getUsbTaskState() == USB_STATE_RUNNING... +} + +void printFloat(double number, int digits) +{ + // Handle negative numbers + if (number < 0.0) + { + Serial.print('-'); + number = -number; + } + + // Round correctly so that print(1.999, 2) prints as "2.00" + double rounding = 0.5; + for (uint8_t i=0; i 0) + Serial.print("."); + + // Extract digits from the remainder one at a time + while (digits-- > 0) + { + remainder *= 10.0; + int toPrint = int(remainder); + Serial.print(toPrint); + remainder -= toPrint; + } +} + +void gpsdump(TinyGPS &gps) +{ + long lat, lon; + float flat, flon; + unsigned long age, date, time, chars; + int year; + byte month, day, hour, minute, second, hundredths; + unsigned short sentences, failed; + + gps.get_position(&lat, &lon, &age); + Serial.print("Lat/Long(10^-5 deg): "); Serial.print(lat); Serial.print(", "); Serial.print(lon); + Serial.print(" Fix age: "); Serial.print(age); Serial.println("ms."); + + feedgps(); // If we don't feed the gps during this long routine, we may drop characters and get checksum errors + + gps.f_get_position(&flat, &flon, &age); + Serial.print("Lat/Long(float): "); printFloat(flat, 5); Serial.print(", "); printFloat(flon, 5); + Serial.print(" Fix age: "); Serial.print(age); Serial.println("ms."); + + feedgps(); + + gps.get_datetime(&date, &time, &age); + Serial.print("Date(ddmmyy): "); Serial.print(date); Serial.print(" Time(hhmmsscc): "); Serial.print(time); + Serial.print(" Fix age: "); Serial.print(age); Serial.println("ms."); + + feedgps(); + + gps.crack_datetime(&year, &month, &day, &hour, &minute, &second, &hundredths, &age); + Serial.print("Date: "); Serial.print(static_cast(month)); Serial.print("/"); Serial.print(static_cast(day)); Serial.print("/"); Serial.print(year); + Serial.print(" Time: "); Serial.print(static_cast(hour)); Serial.print(":"); Serial.print(static_cast(minute)); Serial.print(":"); Serial.print(static_cast(second)); Serial.print("."); Serial.print(static_cast(hundredths)); + Serial.print(" Fix age: "); Serial.print(age); Serial.println("ms."); + + feedgps(); + + Serial.print("Alt(cm): "); Serial.print(gps.altitude()); Serial.print(" Course(10^-2 deg): "); Serial.print(gps.course()); Serial.print(" Speed(10^-2 knots): "); Serial.println(gps.speed()); + Serial.print("Alt(float): "); printFloat(gps.f_altitude()); Serial.print(" Course(float): "); printFloat(gps.f_course()); Serial.println(); + Serial.print("Speed(knots): "); printFloat(gps.f_speed_knots()); Serial.print(" (mph): "); printFloat(gps.f_speed_mph()); + Serial.print(" (mps): "); printFloat(gps.f_speed_mps()); Serial.print(" (kmph): "); printFloat(gps.f_speed_kmph()); Serial.println(); + + feedgps(); + + gps.stats(&chars, &sentences, &failed); + Serial.print("Stats: characters: "); Serial.print(chars); Serial.print(" sentences: "); Serial.print(sentences); Serial.print(" failed checksum: "); Serial.println(failed); +} + +bool feedgps() +{ + uint8_t rcode; + uint8_t buf[64]; //serial buffer equals Max.packet size of bulk-IN endpoint + uint16_t rcvd = 64; + { + /* reading the GPS */ + rcode = Pl.RcvData(&rcvd, buf); + if (rcode && rcode != hrNAK) + ErrorMessage(PSTR("Ret"), rcode); + rcode = false; + if( rcvd ) { //more than zero bytes received + for( uint16_t i=0; i < rcvd; i++ ) { + if( gps.encode((char)buf[i])) { //feed a character to gps object + rcode = true; + }//if( gps.encode(buf[i]... + }//for( uint16_t i=0; i < rcvd; i++... + }//if( rcvd... + } + return( rcode ); +} + diff --git a/libraries/USB_Host_Shield/examples/pl2303/pl2303_xbee_terminal/pl2303_xbee_terminal.ino b/libraries/USB_Host_Shield/examples/pl2303/pl2303_xbee_terminal/pl2303_xbee_terminal.ino new file mode 100755 index 0000000..9a35fc2 --- /dev/null +++ b/libraries/USB_Host_Shield/examples/pl2303/pl2303_xbee_terminal/pl2303_xbee_terminal.ino @@ -0,0 +1,116 @@ +/* Arduino terminal for PL2303 USB to serial converter and XBee radio. */ +/* Inserts linefeed after carriage return in data sent to and received from Xbee */ +/* USB support */ +#include +/* CDC support */ +#include +#include +// Satisfy IDE, which only needs to see the include statment in the ino. +#ifdef dobogusinclude +#include +#include +#endif + +class PLAsyncOper : public CDCAsyncOper +{ +public: + uint8_t OnInit(ACM *pacm); +}; + +uint8_t PLAsyncOper::OnInit(ACM *pacm) +{ + uint8_t rcode; + + // Set DTR = 1 + rcode = pacm->SetControlLineState(1); + + if (rcode) + { + ErrorMessage(PSTR("SetControlLineState"), rcode); + return rcode; + } + + LINE_CODING lc; + lc.dwDTERate = 115200; + lc.bCharFormat = 0; + lc.bParityType = 0; + lc.bDataBits = 8; + + rcode = pacm->SetLineCoding(&lc); + + if (rcode) + ErrorMessage(PSTR("SetLineCoding"), rcode); + + return rcode; +} +USB Usb; +//USBHub Hub(&Usb); +PLAsyncOper AsyncOper; +PL2303 Pl(&Usb, &AsyncOper); + +void setup() +{ + Serial.begin( 115200 ); +#if !defined(__MIPSEL__) + while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection +#endif + Serial.println("Start"); + + if (Usb.Init() == -1) + Serial.println("OSCOKIRQ failed to assert"); + + delay( 200 ); +} + +void loop() +{ + Usb.Task(); + + if( Usb.getUsbTaskState() == USB_STATE_RUNNING ) + { + uint8_t rcode; + + /* reading the keyboard */ + if(Serial.available()) { + uint8_t data= Serial.read(); + + if ( data == '\r' ) { + Serial.print("\r\n"); //insert linefeed + } + else { + Serial.print( data ); //echo back to the screen + } + + /* sending to the phone */ + rcode = Pl.SndData(1, &data); + if (rcode) + ErrorMessage(PSTR("SndData"), rcode); + }//if(Serial.available()... + + delay(50); + + /* reading the converter */ + /* buffer size must be greater or equal to max.packet size */ + /* it it set to 64 (largest possible max.packet size) here, can be tuned down + for particular endpoint */ + uint8_t buf[64]; + uint16_t rcvd = 64; + rcode = Pl.RcvData(&rcvd, buf); + if (rcode && rcode != hrNAK) + ErrorMessage(PSTR("Ret"), rcode); + + if( rcvd ) { //more than zero bytes received + for(uint16_t i=0; i < rcvd; i++ ) { + if( buf[i] =='\r' ) { + Serial.print("\r\n"); //insert linefeed + } + else { + Serial.print((char)buf[i]); //printing on the screen + } + } + } + delay(10); + }//if( Usb.getUsbTaskState() == USB_STATE_RUNNING.. +} + + diff --git a/libraries/USB_Host_Shield/examples/testusbhostFAT/Makefile b/libraries/USB_Host_Shield/examples/testusbhostFAT/Makefile new file mode 100755 index 0000000..232d338 --- /dev/null +++ b/libraries/USB_Host_Shield/examples/testusbhostFAT/Makefile @@ -0,0 +1,58 @@ +# +# These are set for a mega 1280 + quadram plus my serial patch. +# If you lack quadram, or want to disable LFN, just change _FS_TINY=1 _USE_LFN=0 +# +# If your board is a mega 2560 uncomment the following two lines +# BOARD = mega2560 +# PROGRAMMER = wiring +# ...and then comment out the following two lines +BOARD = mega +PROGRAMMER = arduino + +#BOARD = teensypp2 +#BOARD = teensy3 +#BOARD = teensy31 + +# set your Arduino tty port here +PORT = /dev/ttyUSB0 + +EXTRA_FLAGS = -D _USE_LFN=3 + +# change to 0 if you have quadram to take advantage of caching FAT +EXTRA_FLAGS += -D _FS_TINY=1 + + +EXTRA_FLAGS += -D _MAX_SS=512 + + +# Don't worry if you don't have external RAM, xmem2 detects this situation. +# You *WILL* be wanting to get some kind of external ram on your mega in order to +# do anything that is intense. +EXTRA_FLAGS += -D EXT_RAM_STACK=1 +EXTRA_FLAGS += -D EXT_RAM_HEAP=1 + + +# These are no longer needed for the demo to work. +# In the event you need more ram, uncomment these 3 lines. +#EXTRA_FLAGS += -D DISABLE_SERIAL1 +#EXTRA_FLAGS += -D DISABLE_SERIAL2 +#EXTRA_FLAGS += -D DISABLE_SERIAL3 + +# +# Advanced debug on Serial3 +# + +# uncomment the next two to enable debug on Serial3 +EXTRA_FLAGS += -D USB_HOST_SERIAL=Serial3 +#EXTRA_FLAGS += -D DEBUG_USB_HOST + +# The following are the libraries used. +LIB_DIRS += ../../ +LIB_DIRS += ../testusbhostFAT/xmem2 +LIB_DIRS += ../testusbhostFAT/generic_storage +LIB_DIRS += ../testusbhostFAT/RTClib +LIB_DIRS += $(ARD_HOME)/libraries/Wire +LIB_DIRS += $(ARD_HOME)/libraries/Wire/utility + +# And finally, the part that brings everything together for you. +include Arduino_Makefile_master/_Makefile.master diff --git a/libraries/USB_Host_Shield/examples/testusbhostFAT/README.md b/libraries/USB_Host_Shield/examples/testusbhostFAT/README.md new file mode 100755 index 0000000..d8b4296 --- /dev/null +++ b/libraries/USB_Host_Shield/examples/testusbhostFAT/README.md @@ -0,0 +1,29 @@ +This small sketch tests the USB host shield mass storage library. + +__Note:__ This will not run a Arduino Uno due to the limited ram available in the ATmega328p. + +The Arduino Mega (ATmega1280) and the Arduino Mega 2560 (ATmega2560) are confirmed to work with this test code. + +To compile this example you will need the following libraries as well: + +* [xmem2](https://github.com/xxxajk/xmem2) +* [generic_storage FATfs](https://github.com/xxxajk/generic_storage) +* [RTClib](https://github.com/xxxajk/RTClib) + +The following shield is recommended for larger projects: . + +You may use the bundled [Makefile](Makefile) to compile the code instead of the Arduino IDE if you have problems or want a smaller binary. The master makefile is bundled as a submodule, but can also be downloaded manually at the following link: . + +To download the USB Host library and all the needed libraries for this test. + +Run the following command in a terminal application: + +``` +git clone --recursive https://github.com/felis/USB_Host_Shield_2.0 +``` + +If you want to update all the submodules run: + +``` +git submodule foreach --recursive git pull origin master +``` diff --git a/libraries/USB_Host_Shield/examples/testusbhostFAT/testusbhostFAT.ino b/libraries/USB_Host_Shield/examples/testusbhostFAT/testusbhostFAT.ino new file mode 100755 index 0000000..3854b74 --- /dev/null +++ b/libraries/USB_Host_Shield/examples/testusbhostFAT/testusbhostFAT.ino @@ -0,0 +1,721 @@ +/* + * Mega + USB storage + optional DS1307 + optional expansion RAM + funky status LED, + * Includes interactive debug level setting, and supports hot-plug. + * + * IMPORTANT! PLEASE USE Arduino 1.0.5 or better! + * Older versions HAVE MAJOR BUGS AND WILL NOT WORK AT ALL! + * Use of gcc-avr and lib-c that is newer than the Arduino version is even better. + * If you experience random crashes, use make. + * The options that the IDE use can generate bad code and cause the AVR to crash. + * + * This sketch requires the following libraries: + * https://github.com/felis/USB_Host_Shield_2.0 Install as 'USB_Host_Shield_2_0' + * https://github.com/xxxajk/xmem2 Install as 'xmem', provides memory services. + * https://github.com/xxxajk/generic_storage provides access to FAT file system. + * https://github.com/xxxajk/RTClib provides access to DS1307, or fake clock. + * + * Optional, to use the Makefile (Recommended! See above!): + * https://github.com/xxxajk/Arduino_Makefile_master + * + */ + +///////////////////////////////////////////////////////////// +// Please Note: // +// This section is for info with the Arduino IDE ONLY. // +// Unfortunately due to short sightedness of the Arduino // +// code team, that you must set the following in the // +// respective libraries. // +// Changing them here will have _NO_ effect! // +///////////////////////////////////////////////////////////// + +// Uncomment to enable debugging +//#define DEBUG_USB_HOST +// This is where stderr/USB debugging goes to +//#define USB_HOST_SERIAL Serial3 + +// If you have external memory, setting this to 0 enables FAT table caches. +// The 0 setting is recommended only if you have external memory. +//#define _FS_TINY 1 + +//#define _USE_LFN 3 +//#define EXT_RAM_STACK 1 +//#define EXT_RAM_HEAP 1 +//#define _MAX_SS 512 +///////////////////////////////////////////////////////////// +// End of Arduino IDE specific information // +///////////////////////////////////////////////////////////// + +// You can set this to 0 if you are not using a USB hub. +// It will save a little bit of flash and RAM. +// Set to 1 if you want to use a hub. +#define WANT_HUB_TEST 0 + + +#if defined(__AVR__) +#include +#else +#include +#endif +#if WANT_HUB_TEST +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(__AVR__) +static FILE tty_stdio; +static FILE tty_stderr; +volatile uint32_t LEDnext_time; // fade timeout +volatile uint32_t HEAPnext_time; // when to print out next heap report +volatile int brightness = 0; // how bright the LED is +volatile int fadeAmount = 80; // how many points to fade the LED by +#endif + +USB Usb; + +volatile uint8_t current_state = 1; +volatile uint8_t last_state = 0; +volatile bool fatready = false; +volatile bool partsready = false; +volatile bool notified = false; +volatile bool runtest = false; +volatile bool usbon = false; +volatile uint32_t usbon_time; +volatile bool change = false; +volatile bool reportlvl = false; +int cpart = 0; +PCPartition *PT; + +#if WANT_HUB_TEST +#define MAX_HUBS 1 +USBHub *Hubs[MAX_HUBS]; +#endif + +static PFAT *Fats[_VOLUMES]; +static part_t parts[_VOLUMES]; +static storage_t sto[_VOLUMES]; + +/*make sure this is a power of two. */ +#define mbxs 128 +static uint8_t My_Buff_x[mbxs]; /* File read buffer */ + +#if defined(__AVR__) + +#define prescale1 ((1 << WGM12) | (1 << CS10)) +#define prescale8 ((1 << WGM12) | (1 << CS11)) +#define prescale64 ((1 << WGM12) | (1 << CS10) | (1 << CS11)) +#define prescale256 ((1 << WGM12) | (1 << CS12)) +#define prescale1024 ((1 << WGM12) | (1 << CS12) | (1 << CS10)) + +extern "C" unsigned int freeHeap(); + +static int tty_stderr_putc(char c, FILE *t) { + USB_HOST_SERIAL.write(c); + return 0; +} + +static int tty_stderr_flush(FILE *t) { + USB_HOST_SERIAL.flush(); + return 0; +} + +static int tty_std_putc(char c, FILE *t) { + Serial.write(c); + return 0; +} + +static int tty_std_getc(FILE *t) { + while(!Serial.available()); + return Serial.read(); +} + +static int tty_std_flush(FILE *t) { + Serial.flush(); + return 0; +} + +#else +extern "C" { + + int _write(int fd, const char *ptr, int len) { + int j; + for(j = 0; j < len; j++) { + if(fd == 1) + Serial.write(*ptr++); + else if(fd == 2) + USB_HOST_SERIAL.write(*ptr++); + } + return len; + } + + int _read(int fd, char *ptr, int len) { + if(len > 0 && fd == 0) { + while(!Serial.available()); + *ptr = Serial.read(); + return 1; + } + return 0; + } + +#include + + int _fstat(int fd, struct stat *st) { + memset(st, 0, sizeof (*st)); + st->st_mode = S_IFCHR; + st->st_blksize = 1024; + return 0; + } + + int _isatty(int fd) { + return (fd < 3) ? 1 : 0; + } +} +#endif + +void setup() { + bool serr = false; + for(int i = 0; i < _VOLUMES; i++) { + Fats[i] = NULL; + sto[i].private_data = new pvt_t; + ((pvt_t *)sto[i].private_data)->B = 255; // impossible + } + // Set this to higher values to enable more debug information + // minimum 0x00, maximum 0xff + UsbDEBUGlvl = 0x51; + +#if !defined(CORE_TEENSY) && defined(__AVR__) + // make LED pin as an output: + pinMode(LED_BUILTIN, OUTPUT); + pinMode(2, OUTPUT); + // Ensure TX is off + _SFR_BYTE(UCSR0B) &= ~_BV(TXEN0); + // Initialize 'debug' serial port + USB_HOST_SERIAL.begin(115200); + // Do not start primary Serial port if already started. + if(bit_is_clear(UCSR0B, TXEN0)) { + Serial.begin(115200); + serr = true; + } + + + // Blink LED + delay(500); + analogWrite(LED_BUILTIN, 255); + delay(500); + analogWrite(LED_BUILTIN, 0); + delay(500); +#else + while(!Serial); + Serial.begin(115200); // On the Teensy 3.x we get a delay at least! +#endif +#if defined(__AVR__) + // Set up stdio/stderr + tty_stdio.put = tty_std_putc; + tty_stdio.get = tty_std_getc; + tty_stdio.flags = _FDEV_SETUP_RW; + tty_stdio.udata = 0; + + tty_stderr.put = tty_stderr_putc; + tty_stderr.get = NULL; + tty_stderr.flags = _FDEV_SETUP_WRITE; + tty_stderr.udata = 0; + + stdout = &tty_stdio; + stdin = &tty_stdio; + stderr = &tty_stderr; +#endif + printf_P(PSTR("\r\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nStart\r\n")); + printf_P(PSTR("Current UsbDEBUGlvl %02x\r\n"), UsbDEBUGlvl); + printf_P(PSTR("'+' and '-' increase/decrease by 0x01\r\n")); + printf_P(PSTR("'.' and ',' increase/decrease by 0x10\r\n")); + printf_P(PSTR("'t' will run a 10MB write/read test and print out the time it took.\r\n")); + printf_P(PSTR("'e' will toggle vbus off for a few moments.\r\n\r\n")); + printf_P(PSTR("Long filename support: " +#if _USE_LFN + "Enabled" +#else + "Disabled" +#endif + "\r\n")); + if(serr) { + fprintf_P(stderr, PSTR("\r\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nStart\r\n")); + fprintf_P(stderr, PSTR("Current UsbDEBUGlvl %02x\r\n"), UsbDEBUGlvl); + fprintf_P(stderr, PSTR("Long filename support: " +#if _USE_LFN + "Enabled" +#else + "Disabled" +#endif + "\r\n")); + } + +#if !defined(CORE_TEENSY) && defined(__AVR__) + analogWrite(LED_BUILTIN, 255); + delay(500); + analogWrite(LED_BUILTIN, 0); + delay(500); + analogWrite(LED_BUILTIN, 255); + delay(500); + analogWrite(LED_BUILTIN, 0); + delay(500); + analogWrite(LED_BUILTIN, 255); + delay(500); + analogWrite(LED_BUILTIN, 0); + delay(500); + + LEDnext_time = millis() + 1; +#if EXT_RAM + printf_P(PSTR("Total EXT RAM banks %i\r\n"), xmem::getTotalBanks()); +#endif + printf_P(PSTR("Available heap: %u Bytes\r\n"), freeHeap()); + printf_P(PSTR("SP %x\r\n"), (uint8_t *)(SP)); +#endif + + // Even though I'm not going to actually be deleting, + // I want to be able to have slightly more control. + // Besides, it is easier to initialize stuff... +#if WANT_HUB_TEST + for(int i = 0; i < MAX_HUBS; i++) { + Hubs[i] = new USBHub(&Usb); +#if defined(__AVR__) + printf_P(PSTR("Available heap: %u Bytes\r\n"), freeHeap()); +#endif + } +#endif + // Initialize generic storage. This must be done before USB starts. + Init_Generic_Storage(); + + while(Usb.Init(1000) == -1) { + printf_P(PSTR("No USB HOST Shield?\r\n")); + Notify(PSTR("OSC did not start."), 0x40); + } + +#if !defined(CORE_TEENSY) && defined(__AVR__) + cli(); + TCCR3A = 0; + TCCR3B = 0; + // (0.01/(1/((16 *(10^6)) / 8))) - 1 = 19999 + OCR3A = 19999; + TCCR3B |= prescale8; + TIMSK3 |= (1 << OCIE1A); + sei(); + + HEAPnext_time = millis() + 10000; +#endif +#if defined(__AVR__) + HEAPnext_time = millis() + 10000; +#endif +} + +void serialEvent() { + // Adjust UsbDEBUGlvl level on-the-fly. + // + to increase, - to decrease, * to display current level. + // . to increase by 16, , to decrease by 16 + // e to flick VBUS + // * to report debug level + if(Serial.available()) { + int inByte = Serial.read(); + switch(inByte) { + case '+': + if(UsbDEBUGlvl < 0xff) UsbDEBUGlvl++; + reportlvl = true; + break; + case '-': + if(UsbDEBUGlvl > 0x00) UsbDEBUGlvl--; + reportlvl = true; + break; + case '.': + if(UsbDEBUGlvl < 0xf0) UsbDEBUGlvl += 16; + reportlvl = true; + break; + case ',': + if(UsbDEBUGlvl > 0x0f) UsbDEBUGlvl -= 16; + reportlvl = true; + break; + case '*': + reportlvl = true; + break; + case 't': + runtest = true; + break; + case 'e': + change = true; + usbon = false; + break; + } + } +} + +#if !defined(CORE_TEENSY) && defined(__AVR__) +// ALL teensy versions LACK PWM ON LED + +ISR(TIMER3_COMPA_vect) { + if((long)(millis() - LEDnext_time) >= 0L) { + LEDnext_time = millis() + 30; + + // set the brightness of LED + analogWrite(LED_BUILTIN, brightness); + + // change the brightness for next time through the loop: + brightness = brightness + fadeAmount; + + // reverse the direction of the fading at the ends of the fade: + if(brightness <= 0) { + brightness = 0; + fadeAmount = -fadeAmount; + } + if(brightness >= 255) { + brightness = 255; + fadeAmount = -fadeAmount; + } + } +} +#endif + +bool isfat(uint8_t t) { + return (t == 0x01 || t == 0x04 || t == 0x06 || t == 0x0b || t == 0x0c || t == 0x0e || t == 0x1); +} + +void die(FRESULT rc) { + printf_P(PSTR("Failed with rc=%u.\r\n"), rc); + //for (;;); +} + +void loop() { + FIL My_File_Object_x; /* File object */ + +#if defined(__AVR__) + // Print a heap status report about every 10 seconds. + if((long)(millis() - HEAPnext_time) >= 0L) { + if(UsbDEBUGlvl > 0x50) { + printf_P(PSTR("Available heap: %u Bytes\r\n"), freeHeap()); + } + HEAPnext_time = millis() + 10000; + } + TCCR3B = 0; +#endif +#if defined(CORE_TEENSY) + // Teensy suffers here, oh well... + serialEvent(); +#endif + // Horrid! This sort of thing really belongs in an ISR, not here! + // We also will be needing to test each hub port, we don't do this yet! + if(!change && !usbon && (long)(millis() - usbon_time) >= 0L) { + change = true; + usbon = true; + } + + if(change) { + change = false; + if(usbon) { + Usb.vbusPower(vbus_on); + printf_P(PSTR("VBUS on\r\n")); + } else { + Usb.vbusPower(vbus_off); + usbon_time = millis() + 2000; + } + } + Usb.Task(); + current_state = Usb.getUsbTaskState(); + if(current_state != last_state) { + if(UsbDEBUGlvl > 0x50) + printf_P(PSTR("USB state = %x\r\n"), current_state); +#if !defined(CORE_TEENSY) && defined(__AVR__) + if(current_state == USB_STATE_RUNNING) { + fadeAmount = 30; + } +#endif + if(current_state == USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE) { +#if !defined(CORE_TEENSY) && defined(__AVR__) + fadeAmount = 80; +#endif + partsready = false; + for(int i = 0; i < cpart; i++) { + if(Fats[i] != NULL) + delete Fats[i]; + Fats[i] = NULL; + } + fatready = false; + notified = false; + cpart = 0; + } + last_state = current_state; + } + + // only do any of this if usb is on + if(usbon) { + if(partsready && !fatready) { + if(cpart > 0) fatready = true; + } + // This is horrible, and needs to be moved elsewhere! + for(int B = 0; B < MAX_USB_MS_DRIVERS; B++) { + if(!partsready && (UHS_USB_Storage[B]->GetAddress() != NULL)) { + + // Build a list. + int ML = UHS_USB_Storage[B]->GetbMaxLUN(); + //printf("MAXLUN = %i\r\n", ML); + ML++; + for(int i = 0; i < ML; i++) { + if(UHS_USB_Storage[B]->LUNIsGood(i)) { + partsready = true; + ((pvt_t *)(sto[i].private_data))->lun = i; + ((pvt_t *)(sto[i].private_data))->B = B; + sto[i].Reads = *UHS_USB_BulkOnly_Read; + sto[i].Writes = *UHS_USB_BulkOnly_Write; + sto[i].Status = *UHS_USB_BulkOnly_Status; + sto[i].Initialize = *UHS_USB_BulkOnly_Initialize; + sto[i].Commit = *UHS_USB_BulkOnly_Commit; + sto[i].TotalSectors = UHS_USB_Storage[B]->GetCapacity(i); + sto[i].SectorSize = UHS_USB_Storage[B]->GetSectorSize(i); + printf_P(PSTR("LUN:\t\t%u\r\n"), i); + printf_P(PSTR("Total Sectors:\t%08lx\t%lu\r\n"), sto[i].TotalSectors, sto[i].TotalSectors); + printf_P(PSTR("Sector Size:\t%04x\t\t%u\r\n"), sto[i].SectorSize, sto[i].SectorSize); + // get the partition data... + PT = new PCPartition; + + if(!PT->Init(&sto[i])) { + part_t *apart; + for(int j = 0; j < 4; j++) { + apart = PT->GetPart(j); + if(apart != NULL && apart->type != 0x00) { + memcpy(&(parts[cpart]), apart, sizeof (part_t)); + printf_P(PSTR("Partition %u type %#02x\r\n"), j, parts[cpart].type); + // for now + if(isfat(parts[cpart].type)) { + Fats[cpart] = new PFAT(&sto[i], cpart, parts[cpart].firstSector); + //int r = Fats[cpart]->Good(); + if(Fats[cpart]->MountStatus()) { + delete Fats[cpart]; + Fats[cpart] = NULL; + } else cpart++; + } + } + } + } else { + // try superblock + Fats[cpart] = new PFAT(&sto[i], cpart, 0); + //int r = Fats[cpart]->Good(); + if(Fats[cpart]->MountStatus()) { + //printf_P(PSTR("Superblock error %x\r\n"), r); + delete Fats[cpart]; + Fats[cpart] = NULL; + } else cpart++; + + } + delete PT; + } else { + sto[i].Writes = NULL; + sto[i].Reads = NULL; + sto[i].Initialize = NULL; + sto[i].TotalSectors = 0UL; + sto[i].SectorSize = 0; + } + } + + } + } + + if(fatready) { + if(Fats[0] != NULL) { + struct Pvt * p; + p = ((struct Pvt *)(Fats[0]->storage->private_data)); + if(!UHS_USB_Storage[p->B]->LUNIsGood(p->lun)) { + // media change +#if !defined(CORE_TEENSY) && defined(__AVR__) + fadeAmount = 80; +#endif + partsready = false; + for(int i = 0; i < cpart; i++) { + if(Fats[i] != NULL) + delete Fats[i]; + Fats[cpart] = NULL; + } + fatready = false; + notified = false; + cpart = 0; + } + + } + } + if(fatready) { + FRESULT rc; /* Result code */ + UINT bw, br, i; + if(!notified) { +#if !defined(CORE_TEENSY) && defined(__AVR__) + fadeAmount = 5; +#endif + notified = true; + FATFS *fs = NULL; + for(int zz = 0; zz < _VOLUMES; zz++) { + if(Fats[zz]->volmap == 0) fs = Fats[zz]->ffs; + } + printf_P(PSTR("\r\nOpen an existing file (message.txt).\r\n")); + rc = f_open(&My_File_Object_x, "0:/MESSAGE.TXT", FA_READ); + if(rc) printf_P(PSTR("Error %i, message.txt not found.\r\n"), rc); + else { + printf_P(PSTR("\r\nType the file content.\r\n")); + for(;;) { + rc = f_read(&My_File_Object_x, My_Buff_x, mbxs, &br); /* Read a chunk of file */ + if(rc || !br) break; /* Error or end of file */ + for(i = 0; i < br; i++) { + /* Type the data */ + if(My_Buff_x[i] == '\n') + Serial.write('\r'); + if(My_Buff_x[i] != '\r') + Serial.write(My_Buff_x[i]); + Serial.flush(); + } + } + if(rc) { + f_close(&My_File_Object_x); + goto out; + } + + printf_P(PSTR("\r\nClose the file.\r\n")); + rc = f_close(&My_File_Object_x); + if(rc) goto out; + } + printf_P(PSTR("\r\nCreate a new file (hello.txt).\r\n")); + rc = f_open(&My_File_Object_x, "0:/Hello.TxT", FA_WRITE | FA_CREATE_ALWAYS); + if(rc) { + die(rc); + goto outdir; + } + printf_P(PSTR("\r\nWrite a text data. (Hello world!)\r\n")); + rc = f_write(&My_File_Object_x, "Hello world!\r\n", 14, &bw); + if(rc) { + goto out; + } + printf_P(PSTR("%u bytes written.\r\n"), bw); + + printf_P(PSTR("\r\nClose the file.\r\n")); + rc = f_close(&My_File_Object_x); + if(rc) { + die(rc); + goto out; + } +outdir:{ +#if _USE_LFN + char lfn[_MAX_LFN + 1]; + FILINFO My_File_Info_Object_x; /* File information object */ + My_File_Info_Object_x.lfname = lfn; +#endif + DIR My_Dir_Object_x; /* Directory object */ + printf_P(PSTR("\r\nOpen root directory.\r\n")); + rc = f_opendir(&My_Dir_Object_x, "0:/"); + if(rc) { + die(rc); + goto out; + } + + printf_P(PSTR("\r\nDirectory listing...\r\n")); +#if defined(__AVR__) + printf_P(PSTR("Available heap: %u Bytes\r\n"), freeHeap()); +#endif + for(;;) { +#if _USE_LFN + My_File_Info_Object_x.lfsize = _MAX_LFN; +#endif + + rc = f_readdir(&My_Dir_Object_x, &My_File_Info_Object_x); /* Read a directory item */ + if(rc || !My_File_Info_Object_x.fname[0]) break; /* Error or end of dir */ + + if(My_File_Info_Object_x.fattrib & AM_DIR) { + Serial.write('d'); + } else { + Serial.write('-'); + } + Serial.write('r'); + + if(My_File_Info_Object_x.fattrib & AM_RDO) { + Serial.write('-'); + } else { + Serial.write('w'); + } + if(My_File_Info_Object_x.fattrib & AM_HID) { + Serial.write('h'); + } else { + Serial.write('-'); + } + + if(My_File_Info_Object_x.fattrib & AM_SYS) { + Serial.write('s'); + } else { + Serial.write('-'); + } + + if(My_File_Info_Object_x.fattrib & AM_ARC) { + Serial.write('a'); + } else { + Serial.write('-'); + } + +#if _USE_LFN + if(*My_File_Info_Object_x.lfname) + printf_P(PSTR(" %8lu %s (%s)\r\n"), My_File_Info_Object_x.fsize, My_File_Info_Object_x.fname, My_File_Info_Object_x.lfname); + else +#endif + printf_P(PSTR(" %8lu %s\r\n"), My_File_Info_Object_x.fsize, &(My_File_Info_Object_x.fname[0])); + } + } +out: + if(rc) die(rc); + + DISK_IOCTL(fs->drv, CTRL_COMMIT, 0); + printf_P(PSTR("\r\nTest completed.\r\n")); + + } + + if(runtest) { + ULONG ii, wt, rt, start, end; + FATFS *fs = NULL; + for(int zz = 0; zz < _VOLUMES; zz++) { + if(Fats[zz]->volmap == 0) fs = Fats[zz]->ffs; + } + runtest = false; + f_unlink("0:/10MB.bin"); + printf_P(PSTR("\r\nCreate a new 10MB test file (10MB.bin).\r\n")); + rc = f_open(&My_File_Object_x, "0:/10MB.bin", FA_WRITE | FA_CREATE_ALWAYS); + if(rc) goto failed; + for(bw = 0; bw < mbxs; bw++) My_Buff_x[bw] = bw & 0xff; + fflush(stdout); + start = millis(); + while(start == millis()); + for(ii = 10485760LU / mbxs; ii > 0LU; ii--) { + rc = f_write(&My_File_Object_x, My_Buff_x, mbxs, &bw); + if(rc || !bw) goto failed; + } + rc = f_close(&My_File_Object_x); + if(rc) goto failed; + end = millis(); + wt = (end - start) - 1; + printf_P(PSTR("Time to write 10485760 bytes: %lu ms (%lu sec) \r\n"), wt, (500 + wt) / 1000UL); + rc = f_open(&My_File_Object_x, "0:/10MB.bin", FA_READ); + fflush(stdout); + start = millis(); + while(start == millis()); + if(rc) goto failed; + for(;;) { + rc = f_read(&My_File_Object_x, My_Buff_x, mbxs, &bw); /* Read a chunk of file */ + if(rc || !bw) break; /* Error or end of file */ + } + end = millis(); + if(rc) goto failed; + rc = f_close(&My_File_Object_x); + if(rc) goto failed; + rt = (end - start) - 1; + printf_P(PSTR("Time to read 10485760 bytes: %lu ms (%lu sec)\r\nDelete test file\r\n"), rt, (500 + rt) / 1000UL); +failed: + if(rc) die(rc); + DISK_IOCTL(fs->drv, CTRL_COMMIT, 0); + printf_P(PSTR("10MB timing test finished.\r\n")); + } + } + } +} + diff --git a/libraries/USB_Host_Shield/gpl2.txt b/libraries/USB_Host_Shield/gpl2.txt new file mode 100755 index 0000000..5b6e7c6 --- /dev/null +++ b/libraries/USB_Host_Shield/gpl2.txt @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/libraries/USB_Host_Shield/hexdump.h b/libraries/USB_Host_Shield/hexdump.h new file mode 100755 index 0000000..ffa7248 --- /dev/null +++ b/libraries/USB_Host_Shield/hexdump.h @@ -0,0 +1,61 @@ +/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved. + +This software may be distributed and modified under the terms of the GNU +General Public License version 2 (GPL2) as published by the Free Software +Foundation and appearing in the file GPL2.TXT included in the packaging of +this file. Please note that GPL2 Section 2[b] requires that all works based +on this software must also be made publicly available under the terms of +the GPL2 ("Copyleft"). + +Contact information +------------------- + +Circuits At Home, LTD +Web : http://www.circuitsathome.com +e-mail : support@circuitsathome.com + */ +#if !defined(_usb_h_) || defined(__HEXDUMP_H__) +#error "Never include hexdump.h directly; include Usb.h instead" +#else +#define __HEXDUMP_H__ + +extern int UsbDEBUGlvl; + +template +class HexDumper : public BASE_CLASS { + uint8_t byteCount; + OFFSET_TYPE byteTotal; + +public: + + HexDumper() : byteCount(0), byteTotal(0) { + }; + + void Initialize() { + byteCount = 0; + byteTotal = 0; + }; + + void Parse(const LEN_TYPE len, const uint8_t *pbuf, const OFFSET_TYPE &offset); +}; + +template +void HexDumper::Parse(const LEN_TYPE len, const uint8_t *pbuf, const OFFSET_TYPE &offset) { + if(UsbDEBUGlvl >= 0x80) { // Fully bypass this block of code if we do not debug. + for(LEN_TYPE j = 0; j < len; j++, byteCount++, byteTotal++) { + if(!byteCount) { + PrintHex (byteTotal, 0x80); + E_Notify(PSTR(": "), 0x80); + } + PrintHex (pbuf[j], 0x80); + E_Notify(PSTR(" "), 0x80); + + if(byteCount == 15) { + E_Notify(PSTR("\r\n"), 0x80); + byteCount = 0xFF; + } + } + } +} + +#endif // __HEXDUMP_H__ diff --git a/libraries/USB_Host_Shield/hid.cpp b/libraries/USB_Host_Shield/hid.cpp new file mode 100755 index 0000000..e4c7721 --- /dev/null +++ b/libraries/USB_Host_Shield/hid.cpp @@ -0,0 +1,112 @@ +/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved. + +This software may be distributed and modified under the terms of the GNU +General Public License version 2 (GPL2) as published by the Free Software +Foundation and appearing in the file GPL2.TXT included in the packaging of +this file. Please note that GPL2 Section 2[b] requires that all works based +on this software must also be made publicly available under the terms of +the GPL2 ("Copyleft"). + +Contact information +------------------- + +Circuits At Home, LTD +Web : http://www.circuitsathome.com +e-mail : support@circuitsathome.com + */ + +#include "hid.h" + +//get HID report descriptor + +/* WRONG! Endpoint is _ALWAYS_ ZERO for HID! We want the _INTERFACE_ value here! +uint8_t HID::GetReportDescr(uint8_t ep, USBReadParser *parser) { + const uint8_t constBufLen = 64; + uint8_t buf[constBufLen]; + + uint8_t rcode = pUsb->ctrlReq(bAddress, ep, bmREQ_HID_REPORT, USB_REQUEST_GET_DESCRIPTOR, 0x00, + HID_DESCRIPTOR_REPORT, 0x0000, 128, constBufLen, buf, (USBReadParser*)parser); + + //return ((rcode != hrSTALL) ? rcode : 0); + return rcode; +} + */ +uint8_t HID::GetReportDescr(uint16_t wIndex, USBReadParser *parser) { + const uint8_t constBufLen = 64; + uint8_t buf[constBufLen]; + + uint8_t rcode = pUsb->ctrlReq(bAddress, 0x00, bmREQ_HID_REPORT, USB_REQUEST_GET_DESCRIPTOR, 0x00, + HID_DESCRIPTOR_REPORT, wIndex, 128, constBufLen, buf, (USBReadParser*)parser); + + //return ((rcode != hrSTALL) ? rcode : 0); + return rcode; +} + +//uint8_t HID::getHidDescr( uint8_t ep, uint16_t nbytes, uint8_t* dataptr ) +//{ +// return( pUsb->ctrlReq( bAddress, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, 0x00, HID_DESCRIPTOR_HID, 0x0000, nbytes, dataptr )); +//} + +uint8_t HID::SetReport(uint8_t ep, uint8_t iface, uint8_t report_type, uint8_t report_id, uint16_t nbytes, uint8_t* dataptr) { + return ( pUsb->ctrlReq(bAddress, ep, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, report_id, report_type, iface, nbytes, nbytes, dataptr, NULL)); +} + +uint8_t HID::GetReport(uint8_t ep, uint8_t iface, uint8_t report_type, uint8_t report_id, uint16_t nbytes, uint8_t* dataptr) { + return ( pUsb->ctrlReq(bAddress, ep, bmREQ_HID_IN, HID_REQUEST_GET_REPORT, report_id, report_type, iface, nbytes, nbytes, dataptr, NULL)); +} + +uint8_t HID::GetIdle(uint8_t iface, uint8_t reportID, uint8_t* dataptr) { + return ( pUsb->ctrlReq(bAddress, 0, bmREQ_HID_IN, HID_REQUEST_GET_IDLE, reportID, 0, iface, 0x0001, 0x0001, dataptr, NULL)); +} + +uint8_t HID::SetIdle(uint8_t iface, uint8_t reportID, uint8_t duration) { + return ( pUsb->ctrlReq(bAddress, 0, bmREQ_HID_OUT, HID_REQUEST_SET_IDLE, reportID, duration, iface, 0x0000, 0x0000, NULL, NULL)); +} + +uint8_t HID::SetProtocol(uint8_t iface, uint8_t protocol) { + return ( pUsb->ctrlReq(bAddress, 0, bmREQ_HID_OUT, HID_REQUEST_SET_PROTOCOL, protocol, 0x00, iface, 0x0000, 0x0000, NULL, NULL)); +} + +uint8_t HID::GetProtocol(uint8_t iface, uint8_t* dataptr) { + return ( pUsb->ctrlReq(bAddress, 0, bmREQ_HID_IN, HID_REQUEST_GET_PROTOCOL, 0x00, 0x00, iface, 0x0001, 0x0001, dataptr, NULL)); +} + +void HID::PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr) { + Notify(PSTR("Endpoint descriptor:"), 0x80); + Notify(PSTR("\r\nLength:\t\t"), 0x80); + D_PrintHex (ep_ptr->bLength, 0x80); + Notify(PSTR("\r\nType:\t\t"), 0x80); + D_PrintHex (ep_ptr->bDescriptorType, 0x80); + Notify(PSTR("\r\nAddress:\t"), 0x80); + D_PrintHex (ep_ptr->bEndpointAddress, 0x80); + Notify(PSTR("\r\nAttributes:\t"), 0x80); + D_PrintHex (ep_ptr->bmAttributes, 0x80); + Notify(PSTR("\r\nMaxPktSize:\t"), 0x80); + D_PrintHex (ep_ptr->wMaxPacketSize, 0x80); + Notify(PSTR("\r\nPoll Intrv:\t"), 0x80); + D_PrintHex (ep_ptr->bInterval, 0x80); +} + +void HID::PrintHidDescriptor(const USB_HID_DESCRIPTOR *pDesc) { + Notify(PSTR("\r\n\r\nHID Descriptor:\r\n"), 0x80); + Notify(PSTR("bDescLength:\t\t"), 0x80); + D_PrintHex (pDesc->bLength, 0x80); + + Notify(PSTR("\r\nbDescriptorType:\t"), 0x80); + D_PrintHex (pDesc->bDescriptorType, 0x80); + + Notify(PSTR("\r\nbcdHID:\t\t\t"), 0x80); + D_PrintHex (pDesc->bcdHID, 0x80); + + Notify(PSTR("\r\nbCountryCode:\t\t"), 0x80); + D_PrintHex (pDesc->bCountryCode, 0x80); + + Notify(PSTR("\r\nbNumDescriptors:\t"), 0x80); + D_PrintHex (pDesc->bNumDescriptors, 0x80); + + Notify(PSTR("\r\nbDescrType:\t\t"), 0x80); + D_PrintHex (pDesc->bDescrType, 0x80); + + Notify(PSTR("\r\nwDescriptorLength:\t"), 0x80); + D_PrintHex (pDesc->wDescriptorLength, 0x80); +} diff --git a/libraries/USB_Host_Shield/hid.h b/libraries/USB_Host_Shield/hid.h new file mode 100755 index 0000000..1450718 --- /dev/null +++ b/libraries/USB_Host_Shield/hid.h @@ -0,0 +1,184 @@ +/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved. + +This software may be distributed and modified under the terms of the GNU +General Public License version 2 (GPL2) as published by the Free Software +Foundation and appearing in the file GPL2.TXT included in the packaging of +this file. Please note that GPL2 Section 2[b] requires that all works based +on this software must also be made publicly available under the terms of +the GPL2 ("Copyleft"). + +Contact information +------------------- + +Circuits At Home, LTD +Web : http://www.circuitsathome.com +e-mail : support@circuitsathome.com + */ +#if !defined(__HID_H__) +#define __HID_H__ + +#include "Usb.h" +#include "hidusagestr.h" + +#define MAX_REPORT_PARSERS 2 +#define HID_MAX_HID_CLASS_DESCRIPTORS 5 + +#define DATA_SIZE_MASK 0x03 +#define TYPE_MASK 0x0C +#define TAG_MASK 0xF0 + +#define DATA_SIZE_0 0x00 +#define DATA_SIZE_1 0x01 +#define DATA_SIZE_2 0x02 +#define DATA_SIZE_4 0x03 + +#define TYPE_MAIN 0x00 +#define TYPE_GLOBAL 0x04 +#define TYPE_LOCAL 0x08 + +#define TAG_MAIN_INPUT 0x80 +#define TAG_MAIN_OUTPUT 0x90 +#define TAG_MAIN_COLLECTION 0xA0 +#define TAG_MAIN_FEATURE 0xB0 +#define TAG_MAIN_ENDCOLLECTION 0xC0 + +#define TAG_GLOBAL_USAGEPAGE 0x00 +#define TAG_GLOBAL_LOGICALMIN 0x10 +#define TAG_GLOBAL_LOGICALMAX 0x20 +#define TAG_GLOBAL_PHYSMIN 0x30 +#define TAG_GLOBAL_PHYSMAX 0x40 +#define TAG_GLOBAL_UNITEXP 0x50 +#define TAG_GLOBAL_UNIT 0x60 +#define TAG_GLOBAL_REPORTSIZE 0x70 +#define TAG_GLOBAL_REPORTID 0x80 +#define TAG_GLOBAL_REPORTCOUNT 0x90 +#define TAG_GLOBAL_PUSH 0xA0 +#define TAG_GLOBAL_POP 0xB0 + +#define TAG_LOCAL_USAGE 0x00 +#define TAG_LOCAL_USAGEMIN 0x10 +#define TAG_LOCAL_USAGEMAX 0x20 + +/* HID requests */ +#define bmREQ_HID_OUT USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_INTERFACE +#define bmREQ_HID_IN USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_INTERFACE +#define bmREQ_HID_REPORT USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_STANDARD|USB_SETUP_RECIPIENT_INTERFACE + +/* HID constants. Not part of chapter 9 */ +/* Class-Specific Requests */ +#define HID_REQUEST_GET_REPORT 0x01 +#define HID_REQUEST_GET_IDLE 0x02 +#define HID_REQUEST_GET_PROTOCOL 0x03 +#define HID_REQUEST_SET_REPORT 0x09 +#define HID_REQUEST_SET_IDLE 0x0A +#define HID_REQUEST_SET_PROTOCOL 0x0B + +/* Class Descriptor Types */ +#define HID_DESCRIPTOR_HID 0x21 +#define HID_DESCRIPTOR_REPORT 0x22 +#define HID_DESRIPTOR_PHY 0x23 + +/* Protocol Selection */ +#define HID_BOOT_PROTOCOL 0x00 +#define HID_RPT_PROTOCOL 0x01 + +/* HID Interface Class Code */ +#define HID_INTF 0x03 + +/* HID Interface Class SubClass Codes */ +#define HID_BOOT_INTF_SUBCLASS 0x01 + +/* HID Interface Class Protocol Codes */ +#define HID_PROTOCOL_NONE 0x00 +#define HID_PROTOCOL_KEYBOARD 0x01 +#define HID_PROTOCOL_MOUSE 0x02 + +#define HID_ITEM_TYPE_MAIN 0 +#define HID_ITEM_TYPE_GLOBAL 1 +#define HID_ITEM_TYPE_LOCAL 2 +#define HID_ITEM_TYPE_RESERVED 3 + +#define HID_LONG_ITEM_PREFIX 0xfe // Long item prefix value + +#define bmHID_MAIN_ITEM_TAG 0xfc // Main item tag mask + +#define bmHID_MAIN_ITEM_INPUT 0x80 // Main item Input tag value +#define bmHID_MAIN_ITEM_OUTPUT 0x90 // Main item Output tag value +#define bmHID_MAIN_ITEM_FEATURE 0xb0 // Main item Feature tag value +#define bmHID_MAIN_ITEM_COLLECTION 0xa0 // Main item Collection tag value +#define bmHID_MAIN_ITEM_END_COLLECTION 0xce // Main item End Collection tag value + +#define HID_MAIN_ITEM_COLLECTION_PHYSICAL 0 +#define HID_MAIN_ITEM_COLLECTION_APPLICATION 1 +#define HID_MAIN_ITEM_COLLECTION_LOGICAL 2 +#define HID_MAIN_ITEM_COLLECTION_REPORT 3 +#define HID_MAIN_ITEM_COLLECTION_NAMED_ARRAY 4 +#define HID_MAIN_ITEM_COLLECTION_USAGE_SWITCH 5 +#define HID_MAIN_ITEM_COLLECTION_USAGE_MODIFIER 6 + +struct HidItemPrefix { + uint8_t bSize : 2; + uint8_t bType : 2; + uint8_t bTag : 4; +}; + +struct MainItemIOFeature { + uint8_t bmIsConstantOrData : 1; + uint8_t bmIsArrayOrVariable : 1; + uint8_t bmIsRelativeOrAbsolute : 1; + uint8_t bmIsWrapOrNoWrap : 1; + uint8_t bmIsNonLonearOrLinear : 1; + uint8_t bmIsNoPreferedOrPrefered : 1; + uint8_t bmIsNullOrNoNull : 1; + uint8_t bmIsVolatileOrNonVolatile : 1; +}; + +class HID; + +class HIDReportParser { +public: + virtual void Parse(HID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf) = 0; +}; + +class HID : public USBDeviceConfig, public UsbConfigXtracter { +protected: + USB *pUsb; // USB class instance pointer + uint8_t bAddress; // address + +protected: + static const uint8_t epInterruptInIndex = 1; // InterruptIN endpoint index + static const uint8_t epInterruptOutIndex = 2; // InterruptOUT endpoint index + + static const uint8_t maxHidInterfaces = 3; + static const uint8_t maxEpPerInterface = 2; + static const uint8_t totalEndpoints = (maxHidInterfaces * maxEpPerInterface + 1); + + void PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr); + void PrintHidDescriptor(const USB_HID_DESCRIPTOR *pDesc); + + virtual HIDReportParser* GetReportParser(uint8_t id) {}; + +public: + + HID(USB *pusb) : pUsb(pusb) { + }; + + const USB* GetUsb() { + return pUsb; + }; + + virtual bool SetReportParser(uint8_t id, HIDReportParser *prs) {}; + + uint8_t SetProtocol(uint8_t iface, uint8_t protocol); + uint8_t GetProtocol(uint8_t iface, uint8_t* dataptr); + uint8_t GetIdle(uint8_t iface, uint8_t reportID, uint8_t* dataptr); + uint8_t SetIdle(uint8_t iface, uint8_t reportID, uint8_t duration); + + uint8_t GetReportDescr(uint16_t wIndex, USBReadParser *parser = NULL); + + uint8_t GetHidDescr(uint8_t ep, uint16_t nbytes, uint8_t* dataptr); + uint8_t GetReport(uint8_t ep, uint8_t iface, uint8_t report_type, uint8_t report_id, uint16_t nbytes, uint8_t* dataptr); + uint8_t SetReport(uint8_t ep, uint8_t iface, uint8_t report_type, uint8_t report_id, uint16_t nbytes, uint8_t* dataptr); +}; + +#endif // __HID_H__ diff --git a/libraries/USB_Host_Shield/hidboot.cpp b/libraries/USB_Host_Shield/hidboot.cpp new file mode 100755 index 0000000..280b2f9 --- /dev/null +++ b/libraries/USB_Host_Shield/hidboot.cpp @@ -0,0 +1,201 @@ +/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved. + +This software may be distributed and modified under the terms of the GNU +General Public License version 2 (GPL2) as published by the Free Software +Foundation and appearing in the file GPL2.TXT included in the packaging of +this file. Please note that GPL2 Section 2[b] requires that all works based +on this software must also be made publicly available under the terms of +the GPL2 ("Copyleft"). + +Contact information +------------------- + +Circuits At Home, LTD +Web : http://www.circuitsathome.com +e-mail : support@circuitsathome.com + */ +#include "hidboot.h" + +void MouseReportParser::Parse(HID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf) { + MOUSEINFO *pmi = (MOUSEINFO*)buf; + // Future: + // bool event; + +#if 0 + if (prevState.mouseInfo.bmLeftButton == 0 && pmi->bmLeftButton == 1) + OnLeftButtonDown(pmi); + + if (prevState.mouseInfo.bmLeftButton == 1 && pmi->bmLeftButton == 0) + OnLeftButtonUp(pmi); + + if (prevState.mouseInfo.bmRightButton == 0 && pmi->bmRightButton == 1) + OnRightButtonDown(pmi); + + if (prevState.mouseInfo.bmRightButton == 1 && pmi->bmRightButton == 0) + OnRightButtonUp(pmi); + + if (prevState.mouseInfo.bmMiddleButton == 0 && pmi->bmMiddleButton == 1) + OnMiddleButtonDown(pmi); + + if (prevState.mouseInfo.bmMiddleButton == 1 && pmi->bmMiddleButton == 0) + OnMiddleButtonUp(pmi); + + if (prevState.mouseInfo.dX != pmi->dX || prevState.mouseInfo.dY != pmi->dY) + OnMouseMove(pmi); + + if (len > sizeof (MOUSEINFO)) + for (uint8_t i = 0; ibmLeftButton ) { + if(pmi->bmLeftButton) { + OnLeftButtonDown(pmi); + } else { + OnLeftButtonUp(pmi); + } + // Future: + // event = true; + } + + if(prevState.mouseInfo.bmRightButton != pmi->bmRightButton) { + if(pmi->bmRightButton) { + OnRightButtonDown(pmi); + } else { + OnRightButtonUp(pmi); + } + // Future: + // event = true; + } + + if(prevState.mouseInfo.bmMiddleButton != pmi->bmMiddleButton) { + if(pmi->bmMiddleButton) { + OnMiddleButtonDown(pmi); + } else { + OnMiddleButtonUp(pmi); + } + // Future: + // event = true; + } + + // + // Scroll wheel(s), are not part of the spec, but we could support it. + // Logitech wireless keyboard and mouse combo reports scroll wheel in byte 4 + // We wouldn't even need to save this information. + //if(len > 3) { + //} + // + + // Mice only report motion when they actually move! + // Why not just pass the x/y values to simplify things?? + if(pmi->dX || pmi->dY) { + OnMouseMove(pmi); + // Future: + // event = true; + } + + // + // Future: + // Provide a callback that operates on the gathered events from above. + // + // if(event) OnMouse(); + // + + // Only the first byte matters (buttons). We do NOT need to save position info. + prevState.bInfo[0] = buf[0]; +#endif + +}; + +void KeyboardReportParser::Parse(HID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf) { + // On error - return + if (buf[2] == 1) + return; + + //KBDINFO *pki = (KBDINFO*)buf; + + // provide event for changed control key state + if (prevState.bInfo[0x00] != buf[0x00]) { + OnControlKeysChanged(prevState.bInfo[0x00], buf[0x00]); + } + + for (uint8_t i = 2; i < 8; i++) { + bool down = false; + bool up = false; + + for (uint8_t j = 2; j < 8; j++) { + if (buf[i] == prevState.bInfo[j] && buf[i] != 1) + down = true; + if (buf[j] == prevState.bInfo[i] && prevState.bInfo[i] != 1) + up = true; + } + if (!down) { + HandleLockingKeys(hid, buf[i]); + OnKeyDown(*buf, buf[i]); + } + if (!up) + OnKeyUp(prevState.bInfo[0], prevState.bInfo[i]); + } + for (uint8_t i = 0; i < 8; i++) + prevState.bInfo[i] = buf[i]; +}; + +const uint8_t KeyboardReportParser::numKeys[10] PROGMEM = {'!', '@', '#', '$', '%', '^', '&', '*', '(', ')'}; +const uint8_t KeyboardReportParser::symKeysUp[12] PROGMEM = {'_', '+', '{', '}', '|', '~', ':', '"', '~', '<', '>', '?'}; +const uint8_t KeyboardReportParser::symKeysLo[12] PROGMEM = {'-', '=', '[', ']', '\\', ' ', ';', '\'', '`', ',', '.', '/'}; +const uint8_t KeyboardReportParser::padKeys[5] PROGMEM = {'/', '*', '-', '+', 0x13}; + +uint8_t KeyboardReportParser::OemToAscii(uint8_t mod, uint8_t key) { + uint8_t shift = (mod & 0x22); + + // [a-z] + if (VALUE_WITHIN(key, 0x04, 0x1d)) { + // Upper case letters + if ((kbdLockingKeys.kbdLeds.bmCapsLock == 0 && shift) || + (kbdLockingKeys.kbdLeds.bmCapsLock == 1 && shift == 0)) + return (key - 4 + 'A'); + + // Lower case letters + else + return (key - 4 + 'a'); + }// Numbers + else if (VALUE_WITHIN(key, 0x1e, 0x27)) { + if (shift) + return ((uint8_t)pgm_read_byte(&getNumKeys()[key - 0x1e])); + else + return ((key == UHS_HID_BOOT_KEY_ZERO) ? '0' : key - 0x1e + '1'); + }// Keypad Numbers + else if(VALUE_WITHIN(key, 0x59, 0x61)) { + if(kbdLockingKeys.kbdLeds.bmNumLock == 1) + return (key - 0x59 + '1'); + } else if(VALUE_WITHIN(key, 0x2d, 0x38)) + return ((shift) ? (uint8_t)pgm_read_byte(&getSymKeysUp()[key - 0x2d]) : (uint8_t)pgm_read_byte(&getSymKeysLo()[key - 0x2d])); + else if(VALUE_WITHIN(key, 0x54, 0x58)) + return (uint8_t)pgm_read_byte(&getPadKeys()[key - 0x54]); + else { + switch(key) { + case UHS_HID_BOOT_KEY_SPACE: return (0x20); + case UHS_HID_BOOT_KEY_ENTER: return (0x13); + case UHS_HID_BOOT_KEY_ZERO2: return ((kbdLockingKeys.kbdLeds.bmNumLock == 1) ? '0': 0); + case UHS_HID_BOOT_KEY_PERIOD: return ((kbdLockingKeys.kbdLeds.bmNumLock == 1) ? '.': 0); + } + } + return ( 0); +} diff --git a/libraries/USB_Host_Shield/hidboot.h b/libraries/USB_Host_Shield/hidboot.h new file mode 100755 index 0000000..fb63ec5 --- /dev/null +++ b/libraries/USB_Host_Shield/hidboot.h @@ -0,0 +1,618 @@ +/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved. + +This software may be distributed and modified under the terms of the GNU +General Public License version 2 (GPL2) as published by the Free Software +Foundation and appearing in the file GPL2.TXT included in the packaging of +this file. Please note that GPL2 Section 2[b] requires that all works based +on this software must also be made publicly available under the terms of +the GPL2 ("Copyleft"). + +Contact information +------------------- + +Circuits At Home, LTD +Web : http://www.circuitsathome.com +e-mail : support@circuitsathome.com + */ +#if !defined(__HIDBOOT_H__) +#define __HIDBOOT_H__ + +#include "hid.h" + +#define UHS_HID_BOOT_KEY_ZERO 0x27 +#define UHS_HID_BOOT_KEY_ENTER 0x28 +#define UHS_HID_BOOT_KEY_SPACE 0x2c +#define UHS_HID_BOOT_KEY_CAPS_LOCK 0x39 +#define UHS_HID_BOOT_KEY_SCROLL_LOCK 0x47 +#define UHS_HID_BOOT_KEY_NUM_LOCK 0x53 +#define UHS_HID_BOOT_KEY_ZERO2 0x62 +#define UHS_HID_BOOT_KEY_PERIOD 0x63 + +// Don't worry, GCC will optimize the result to a final value. +#define bitsEndpoints(p) ((((p) & HID_PROTOCOL_KEYBOARD)? 2 : 0) | (((p) & HID_PROTOCOL_MOUSE)? 1 : 0)) +#define totalEndpoints(p) ((bitsEndpoints(p) == 3) ? 3 : 2) +#define epMUL(p) ((((p) & HID_PROTOCOL_KEYBOARD)? 1 : 0) + (((p) & HID_PROTOCOL_MOUSE)? 1 : 0)) + +// Already defined in hid.h +// #define HID_MAX_HID_CLASS_DESCRIPTORS 5 + +struct MOUSEINFO { + + struct { + uint8_t bmLeftButton : 1; + uint8_t bmRightButton : 1; + uint8_t bmMiddleButton : 1; + uint8_t bmDummy : 5; + }; + int8_t dX; + int8_t dY; +}; + +class MouseReportParser : public HIDReportParser { + + union { + MOUSEINFO mouseInfo; + uint8_t bInfo[sizeof (MOUSEINFO)]; + } prevState; + +public: + void Parse(HID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf); + +protected: + + virtual void OnMouseMove(MOUSEINFO *mi) { + }; + + virtual void OnLeftButtonUp(MOUSEINFO *mi) { + }; + + virtual void OnLeftButtonDown(MOUSEINFO *mi) { + }; + + virtual void OnRightButtonUp(MOUSEINFO *mi) { + }; + + virtual void OnRightButtonDown(MOUSEINFO *mi) { + }; + + virtual void OnMiddleButtonUp(MOUSEINFO *mi) { + }; + + virtual void OnMiddleButtonDown(MOUSEINFO *mi) { + }; +}; + +struct MODIFIERKEYS { + uint8_t bmLeftCtrl : 1; + uint8_t bmLeftShift : 1; + uint8_t bmLeftAlt : 1; + uint8_t bmLeftGUI : 1; + uint8_t bmRightCtrl : 1; + uint8_t bmRightShift : 1; + uint8_t bmRightAlt : 1; + uint8_t bmRightGUI : 1; +}; + +struct KBDINFO { + + struct { + uint8_t bmLeftCtrl : 1; + uint8_t bmLeftShift : 1; + uint8_t bmLeftAlt : 1; + uint8_t bmLeftGUI : 1; + uint8_t bmRightCtrl : 1; + uint8_t bmRightShift : 1; + uint8_t bmRightAlt : 1; + uint8_t bmRightGUI : 1; + }; + uint8_t bReserved; + uint8_t Keys[6]; +}; + +struct KBDLEDS { + uint8_t bmNumLock : 1; + uint8_t bmCapsLock : 1; + uint8_t bmScrollLock : 1; + uint8_t bmCompose : 1; + uint8_t bmKana : 1; + uint8_t bmReserved : 3; +}; + +class KeyboardReportParser : public HIDReportParser { + static const uint8_t numKeys[10]; + static const uint8_t symKeysUp[12]; + static const uint8_t symKeysLo[12]; + static const uint8_t padKeys[5]; + +protected: + + union { + KBDINFO kbdInfo; + uint8_t bInfo[sizeof (KBDINFO)]; + } prevState; + + union { + KBDLEDS kbdLeds; + uint8_t bLeds; + } kbdLockingKeys; + + uint8_t OemToAscii(uint8_t mod, uint8_t key); + +public: + + KeyboardReportParser() { + kbdLockingKeys.bLeds = 0; + }; + + void Parse(HID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf); + +protected: + + virtual uint8_t HandleLockingKeys(HID* hid, uint8_t key) { + uint8_t old_keys = kbdLockingKeys.bLeds; + + switch(key) { + case UHS_HID_BOOT_KEY_NUM_LOCK: + kbdLockingKeys.kbdLeds.bmNumLock = ~kbdLockingKeys.kbdLeds.bmNumLock; + break; + case UHS_HID_BOOT_KEY_CAPS_LOCK: + kbdLockingKeys.kbdLeds.bmCapsLock = ~kbdLockingKeys.kbdLeds.bmCapsLock; + break; + case UHS_HID_BOOT_KEY_SCROLL_LOCK: + kbdLockingKeys.kbdLeds.bmScrollLock = ~kbdLockingKeys.kbdLeds.bmScrollLock; + break; + } + + if(old_keys != kbdLockingKeys.bLeds && hid) + return (hid->SetReport(0, 0/*hid->GetIface()*/, 2, 0, 1, &kbdLockingKeys.bLeds)); + + return 0; + }; + + virtual void OnControlKeysChanged(uint8_t before, uint8_t after) { + }; + + virtual void OnKeyDown(uint8_t mod, uint8_t key) { + }; + + virtual void OnKeyUp(uint8_t mod, uint8_t key) { + }; + + virtual const uint8_t *getNumKeys() { + return numKeys; + }; + + virtual const uint8_t *getSymKeysUp() { + return symKeysUp; + }; + + virtual const uint8_t *getSymKeysLo() { + return symKeysLo; + }; + + virtual const uint8_t *getPadKeys() { + return padKeys; + }; +}; + +template +class HIDBoot : public HID //public USBDeviceConfig, public UsbConfigXtracter +{ + EpInfo epInfo[totalEndpoints(BOOT_PROTOCOL)]; + HIDReportParser *pRptParser[epMUL(BOOT_PROTOCOL)]; + + uint8_t bConfNum; // configuration number + uint8_t bIfaceNum; // Interface Number + uint8_t bNumIface; // number of interfaces in the configuration + uint8_t bNumEP; // total number of EP in the configuration + uint32_t qNextPollTime; // next poll time + bool bPollEnable; // poll enable flag + uint8_t bInterval; // largest interval + + void Initialize(); + + virtual HIDReportParser* GetReportParser(uint8_t id) { + return pRptParser[id]; + }; + +public: + HIDBoot(USB *p); + + virtual bool SetReportParser(uint8_t id, HIDReportParser *prs) { + pRptParser[id] = prs; + return true; + }; + + // USBDeviceConfig implementation + uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed); + uint8_t Release(); + uint8_t Poll(); + + virtual uint8_t GetAddress() { + return bAddress; + }; + + virtual bool isReady() { + return bPollEnable; + }; + + // UsbConfigXtracter implementation + // Method should be defined here if virtual. + virtual void EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *ep); + + virtual bool DEVCLASSOK(uint8_t klass) { + return (klass == USB_CLASS_HID); + } + + virtual bool DEVSUBCLASSOK(uint8_t subklass) { + return (subklass == BOOT_PROTOCOL); + } +}; + +template +HIDBoot::HIDBoot(USB *p) : +HID(p), +qNextPollTime(0), +bPollEnable(false) { + Initialize(); + + for(int i = 0; i < epMUL(BOOT_PROTOCOL); i++) { + pRptParser[i] = NULL; + } + if(pUsb) + pUsb->RegisterDeviceClass(this); +} + +template +void HIDBoot::Initialize() { + for(int i = 0; i < totalEndpoints(BOOT_PROTOCOL); i++) { + epInfo[i].epAddr = 0; + epInfo[i].maxPktSize = (i) ? 0 : 8; + epInfo[i].epAttribs = 0; + epInfo[i].bmNakPower = (i) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER; + } + bNumEP = 1; + bNumIface = 0; + bConfNum = 0; +} + +template +uint8_t HIDBoot::Init(uint8_t parent, uint8_t port, bool lowspeed) { + const uint8_t constBufSize = sizeof (USB_DEVICE_DESCRIPTOR); + + uint8_t buf[constBufSize]; + uint8_t rcode; + UsbDevice *p = NULL; + EpInfo *oldep_ptr = NULL; + uint8_t len = 0; + //uint16_t cd_len = 0; + + uint8_t num_of_conf; // number of configurations + //uint8_t num_of_intf; // number of interfaces + + AddressPool &addrPool = pUsb->GetAddressPool(); + + USBTRACE("BM Init\r\n"); + //USBTRACE2("totalEndpoints:", (uint8_t) (totalEndpoints(BOOT_PROTOCOL))); + //USBTRACE2("epMUL:", epMUL(BOOT_PROTOCOL)); + + if(bAddress) + return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE; + + bInterval = 0; + // Get pointer to pseudo device with address 0 assigned + p = addrPool.GetUsbDevicePtr(0); + + if(!p) + return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; + + if(!p->epinfo) { + USBTRACE("epinfo\r\n"); + return USB_ERROR_EPINFO_IS_NULL; + } + + // Save old pointer to EP_RECORD of address 0 + oldep_ptr = p->epinfo; + + // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence + p->epinfo = epInfo; + + p->lowspeed = lowspeed; + + // Get device descriptor + rcode = pUsb->getDevDescr(0, 0, 8, (uint8_t*)buf); + + if(!rcode) + len = (buf[0] > constBufSize) ? constBufSize : buf[0]; + + if(rcode) { + // Restore p->epinfo + p->epinfo = oldep_ptr; + + goto FailGetDevDescr; + } + + // Restore p->epinfo + p->epinfo = oldep_ptr; + + // Allocate new address according to device class + bAddress = addrPool.AllocAddress(parent, false, port); + + if(!bAddress) + return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL; + + // Extract Max Packet Size from the device descriptor + epInfo[0].maxPktSize = (uint8_t)((USB_DEVICE_DESCRIPTOR*)buf)->bMaxPacketSize0; + + // Assign new address to the device + rcode = pUsb->setAddr(0, 0, bAddress); + + if(rcode) { + p->lowspeed = false; + addrPool.FreeAddress(bAddress); + bAddress = 0; + USBTRACE2("setAddr:", rcode); + return rcode; + } + //delay(2); //per USB 2.0 sect.9.2.6.3 + + USBTRACE2("Addr:", bAddress); + + p->lowspeed = false; + + p = addrPool.GetUsbDevicePtr(bAddress); + + if(!p) + return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; + + p->lowspeed = lowspeed; + + if(len) + rcode = pUsb->getDevDescr(bAddress, 0, len, (uint8_t*)buf); + + if(rcode) + goto FailGetDevDescr; + + num_of_conf = ((USB_DEVICE_DESCRIPTOR*)buf)->bNumConfigurations; + + USBTRACE2("NC:", num_of_conf); + + // GCC will optimize unused stuff away. + if((BOOT_PROTOCOL & (HID_PROTOCOL_KEYBOARD | HID_PROTOCOL_MOUSE)) == (HID_PROTOCOL_KEYBOARD | HID_PROTOCOL_MOUSE)) { + USBTRACE("HID_PROTOCOL_KEYBOARD AND MOUSE\r\n"); + ConfigDescParser< + USB_CLASS_HID, + HID_BOOT_INTF_SUBCLASS, + HID_PROTOCOL_KEYBOARD | HID_PROTOCOL_MOUSE, + CP_MASK_COMPARE_ALL > confDescrParser(this); + confDescrParser.SetOR(); // Use the OR variant. + for(uint8_t i = 0; i < num_of_conf; i++) { + pUsb->getConfDescr(bAddress, 0, i, &confDescrParser); + if(bNumEP == (uint8_t)(totalEndpoints(BOOT_PROTOCOL))) + break; + } + } else { + // GCC will optimize unused stuff away. + if(BOOT_PROTOCOL & HID_PROTOCOL_KEYBOARD) { + USBTRACE("HID_PROTOCOL_KEYBOARD\r\n"); + for(uint8_t i = 0; i < num_of_conf; i++) { + ConfigDescParser< + USB_CLASS_HID, + HID_BOOT_INTF_SUBCLASS, + HID_PROTOCOL_KEYBOARD, + CP_MASK_COMPARE_ALL> confDescrParserA(this); + + pUsb->getConfDescr(bAddress, 0, i, &confDescrParserA); + if(bNumEP == (uint8_t)(totalEndpoints(BOOT_PROTOCOL))) + break; + } + } + + // GCC will optimize unused stuff away. + if(BOOT_PROTOCOL & HID_PROTOCOL_MOUSE) { + USBTRACE("HID_PROTOCOL_MOUSE\r\n"); + for(uint8_t i = 0; i < num_of_conf; i++) { + ConfigDescParser< + USB_CLASS_HID, + HID_BOOT_INTF_SUBCLASS, + HID_PROTOCOL_MOUSE, + CP_MASK_COMPARE_ALL> confDescrParserB(this); + + pUsb->getConfDescr(bAddress, 0, i, &confDescrParserB); + if(bNumEP == ((uint8_t)(totalEndpoints(BOOT_PROTOCOL)))) + break; + + } + } + } + USBTRACE2("bNumEP:", bNumEP); + + if(bNumEP != (uint8_t)(totalEndpoints(BOOT_PROTOCOL))) { + rcode = USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED; + goto Fail; + } + + // Assign epInfo to epinfo pointer + rcode = pUsb->setEpInfoEntry(bAddress, bNumEP, epInfo); + //USBTRACE2("setEpInfoEntry returned ", rcode); + USBTRACE2("Cnf:", bConfNum); + + delay(1000); + + // Set Configuration Value + rcode = pUsb->setConf(bAddress, 0, bConfNum); + + if(rcode) + goto FailSetConfDescr; + + delay(1000); + + USBTRACE2("bIfaceNum:", bIfaceNum); + USBTRACE2("bNumIface:", bNumIface); + + // Yes, mouse wants SetProtocol and SetIdle too! + for(uint8_t i = 0; i < epMUL(BOOT_PROTOCOL); i++) { + USBTRACE2("\r\nInterface:", i); + rcode = SetProtocol(i, HID_BOOT_PROTOCOL); + if(rcode) goto FailSetProtocol; + USBTRACE2("PROTOCOL SET HID_BOOT rcode:", rcode); + rcode = SetIdle(i, 0, 0); + USBTRACE2("SET_IDLE rcode:", rcode); + // if(rcode) goto FailSetIdle; This can fail. + // Get the RPIPE and just throw it away. + SinkParser sink; + rcode = GetReportDescr(i, &sink); + USBTRACE2("RPIPE rcode:", rcode); + } + + // Get RPIPE and throw it away. + + if(BOOT_PROTOCOL & HID_PROTOCOL_KEYBOARD) { + // Wake keyboard interface by twinkling up to 5 LEDs that are in the spec. + // kana, compose, scroll, caps, num + rcode = 0x20; // Reuse rcode. + while(rcode) { + rcode >>= 1; + // Ignore any error returned, we don't care if LED is not supported + SetReport(0, 0, 2, 0, 1, &rcode); // Eventually becomes zero (All off) + delay(25); + } + } + USBTRACE("BM configured\r\n"); + + bPollEnable = true; + return 0; + +FailGetDevDescr: +#ifdef DEBUG_USB_HOST + NotifyFailGetDevDescr(); + goto Fail; +#endif + + //FailSetDevTblEntry: + //#ifdef DEBUG_USB_HOST + // NotifyFailSetDevTblEntry(); + // goto Fail; + //#endif + + //FailGetConfDescr: + //#ifdef DEBUG_USB_HOST + // NotifyFailGetConfDescr(); + // goto Fail; + //#endif + +FailSetConfDescr: +#ifdef DEBUG_USB_HOST + NotifyFailSetConfDescr(); + goto Fail; +#endif + +FailSetProtocol: +#ifdef DEBUG_USB_HOST + USBTRACE("SetProto:"); + goto Fail; +#endif + + //FailSetIdle: + //#ifdef DEBUG_USB_HOST + // USBTRACE("SetIdle:"); + //#endif + +Fail: +#ifdef DEBUG_USB_HOST + NotifyFail(rcode); +#endif + Release(); + + return rcode; +} + +template +void HIDBoot::EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *pep) { + + // If the first configuration satisfies, the others are not considered. + //if(bNumEP > 1 && conf != bConfNum) + if(bNumEP == totalEndpoints(BOOT_PROTOCOL)) + return; + + bConfNum = conf; + bIfaceNum = iface; + + if((pep->bmAttributes & 0x03) == 3 && (pep->bEndpointAddress & 0x80) == 0x80) { + if(pep->bInterval > bInterval) bInterval = pep->bInterval; + + // Fill in the endpoint info structure + epInfo[bNumEP].epAddr = (pep->bEndpointAddress & 0x0F); + epInfo[bNumEP].maxPktSize = (uint8_t)pep->wMaxPacketSize; + epInfo[bNumEP].epAttribs = 0; + epInfo[bNumEP].bmNakPower = USB_NAK_NOWAIT; + bNumEP++; + + } +} + +template +uint8_t HIDBoot::Release() { + pUsb->GetAddressPool().FreeAddress(bAddress); + + bConfNum = 0; + bIfaceNum = 0; + bNumEP = 1; + bAddress = 0; + qNextPollTime = 0; + bPollEnable = false; + + return 0; +} + +template +uint8_t HIDBoot::Poll() { + uint8_t rcode = 0; + + if(bPollEnable && ((long)(millis() - qNextPollTime) >= 0L)) { + + // To-do: optimize manually, using the for loop only if needed. + for(int i = 0; i < epMUL(BOOT_PROTOCOL); i++) { + const uint16_t const_buff_len = 16; + uint8_t buf[const_buff_len]; + + USBTRACE3("(hidboot.h) i=", i, 0x81); + USBTRACE3("(hidboot.h) epInfo[epInterruptInIndex + i].epAddr=", epInfo[epInterruptInIndex + i].epAddr, 0x81); + USBTRACE3("(hidboot.h) epInfo[epInterruptInIndex + i].maxPktSize=", epInfo[epInterruptInIndex + i].maxPktSize, 0x81); + uint16_t read = (uint16_t)epInfo[epInterruptInIndex + i].maxPktSize; + + rcode = pUsb->inTransfer(bAddress, epInfo[epInterruptInIndex + i].epAddr, &read, buf); + // SOME buggy dongles report extra keys (like sleep) using a 2 byte packet on the wrong endpoint. + // Since keyboard and mice must report at least 3 bytes, we ignore the extra data. + if(!rcode && read > 2) { + if(pRptParser[i]) + pRptParser[i]->Parse((HID*)this, 0, (uint8_t)read, buf); +#ifdef DEBUG_USB_HOST + // We really don't care about errors and anomalies unless we are debugging. + } else { + if(rcode != hrNAK) { + USBTRACE3("(hidboot.h) Poll:", rcode, 0x81); + } + if(!rcode && read) { + USBTRACE3("(hidboot.h) Strange read count: ", read, 0x80); + USBTRACE3("(hidboot.h) Interface:", i, 0x80); + } + } + + if(!rcode && read && (UsbDEBUGlvl > 0x7f)) { + for(uint8_t i = 0; i < read; i++) { + PrintHex (buf[i], 0x80); + USBTRACE1(" ", 0x80); + } + if(read) + USBTRACE1("\r\n", 0x80); +#endif + } + + } + qNextPollTime = millis() + bInterval; + } + return rcode; +} + +#endif // __HIDBOOTMOUSE_H__ diff --git a/libraries/USB_Host_Shield/hidescriptorparser.cpp b/libraries/USB_Host_Shield/hidescriptorparser.cpp new file mode 100755 index 0000000..e4491b4 --- /dev/null +++ b/libraries/USB_Host_Shield/hidescriptorparser.cpp @@ -0,0 +1,1588 @@ +/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved. + +This software may be distributed and modified under the terms of the GNU +General Public License version 2 (GPL2) as published by the Free Software +Foundation and appearing in the file GPL2.TXT included in the packaging of +this file. Please note that GPL2 Section 2[b] requires that all works based +on this software must also be made publicly available under the terms of +the GPL2 ("Copyleft"). + +Contact information +------------------- + +Circuits At Home, LTD +Web : http://www.circuitsathome.com +e-mail : support@circuitsathome.com + */ + +#include "hidescriptorparser.h" + +const char * const ReportDescParserBase::usagePageTitles0[] PROGMEM = { + pstrUsagePageGenericDesktopControls, + pstrUsagePageSimulationControls, + pstrUsagePageVRControls, + pstrUsagePageSportControls, + pstrUsagePageGameControls, + pstrUsagePageGenericDeviceControls, + pstrUsagePageKeyboardKeypad, + pstrUsagePageLEDs, + pstrUsagePageButton, + pstrUsagePageOrdinal, + pstrUsagePageTelephone, + pstrUsagePageConsumer, + pstrUsagePageDigitizer, + pstrUsagePagePID, + pstrUsagePageUnicode +}; + +const char * const ReportDescParserBase::usagePageTitles1[] PROGMEM = { + pstrUsagePageBarCodeScanner, + pstrUsagePageScale, + pstrUsagePageMSRDevices, + pstrUsagePagePointOfSale, + pstrUsagePageCameraControl, + pstrUsagePageArcade +}; +const char * const ReportDescParserBase::genDesktopTitles0[] PROGMEM = { + pstrUsagePointer, + pstrUsageMouse, + pstrUsageJoystick, + pstrUsageGamePad, + pstrUsageKeyboard, + pstrUsageKeypad, + pstrUsageMultiAxisController, + pstrUsageTabletPCSystemControls + +}; +const char * const ReportDescParserBase::genDesktopTitles1[] PROGMEM = { + pstrUsageX, + pstrUsageY, + pstrUsageZ, + pstrUsageRx, + pstrUsageRy, + pstrUsageRz, + pstrUsageSlider, + pstrUsageDial, + pstrUsageWheel, + pstrUsageHatSwitch, + pstrUsageCountedBuffer, + pstrUsageByteCount, + pstrUsageMotionWakeup, + pstrUsageStart, + pstrUsageSelect, + pstrUsagePageReserved, + pstrUsageVx, + pstrUsageVy, + pstrUsageVz, + pstrUsageVbrx, + pstrUsageVbry, + pstrUsageVbrz, + pstrUsageVno, + pstrUsageFeatureNotification, + pstrUsageResolutionMultiplier +}; +const char * const ReportDescParserBase::genDesktopTitles2[] PROGMEM = { + pstrUsageSystemControl, + pstrUsageSystemPowerDown, + pstrUsageSystemSleep, + pstrUsageSystemWakeup, + pstrUsageSystemContextMenu, + pstrUsageSystemMainMenu, + pstrUsageSystemAppMenu, + pstrUsageSystemMenuHelp, + pstrUsageSystemMenuExit, + pstrUsageSystemMenuSelect, + pstrUsageSystemMenuRight, + pstrUsageSystemMenuLeft, + pstrUsageSystemMenuUp, + pstrUsageSystemMenuDown, + pstrUsageSystemColdRestart, + pstrUsageSystemWarmRestart, + pstrUsageDPadUp, + pstrUsageDPadDown, + pstrUsageDPadRight, + pstrUsageDPadLeft +}; +const char * const ReportDescParserBase::genDesktopTitles3[] PROGMEM = { + pstrUsageSystemDock, + pstrUsageSystemUndock, + pstrUsageSystemSetup, + pstrUsageSystemBreak, + pstrUsageSystemDebuggerBreak, + pstrUsageApplicationBreak, + pstrUsageApplicationDebuggerBreak, + pstrUsageSystemSpeakerMute, + pstrUsageSystemHibernate +}; +const char * const ReportDescParserBase::genDesktopTitles4[] PROGMEM = { + pstrUsageSystemDisplayInvert, + pstrUsageSystemDisplayInternal, + pstrUsageSystemDisplayExternal, + pstrUsageSystemDisplayBoth, + pstrUsageSystemDisplayDual, + pstrUsageSystemDisplayToggleIntExt, + pstrUsageSystemDisplaySwapPriSec, + pstrUsageSystemDisplayLCDAutoscale +}; +const char * const ReportDescParserBase::simuTitles0[] PROGMEM = { + pstrUsageFlightSimulationDevice, + pstrUsageAutomobileSimulationDevice, + pstrUsageTankSimulationDevice, + pstrUsageSpaceshipSimulationDevice, + pstrUsageSubmarineSimulationDevice, + pstrUsageSailingSimulationDevice, + pstrUsageMotocicleSimulationDevice, + pstrUsageSportsSimulationDevice, + pstrUsageAirplaneSimulationDevice, + pstrUsageHelicopterSimulationDevice, + pstrUsageMagicCarpetSimulationDevice, + pstrUsageBicycleSimulationDevice +}; +const char * const ReportDescParserBase::simuTitles1[] PROGMEM = { + pstrUsageFlightControlStick, + pstrUsageFlightStick, + pstrUsageCyclicControl, + pstrUsageCyclicTrim, + pstrUsageFlightYoke, + pstrUsageTrackControl +}; +const char * const ReportDescParserBase::simuTitles2[] PROGMEM = { + pstrUsageAileron, + pstrUsageAileronTrim, + pstrUsageAntiTorqueControl, + pstrUsageAutopilotEnable, + pstrUsageChaffRelease, + pstrUsageCollectiveControl, + pstrUsageDiveBrake, + pstrUsageElectronicCountermeasures, + pstrUsageElevator, + pstrUsageElevatorTrim, + pstrUsageRudder, + pstrUsageThrottle, + pstrUsageFlightCommunications, + pstrUsageFlareRelease, + pstrUsageLandingGear, + pstrUsageToeBrake, + pstrUsageTrigger, + pstrUsageWeaponsArm, + pstrUsageWeaponsSelect, + pstrUsageWingFlaps, + pstrUsageAccelerator, + pstrUsageBrake, + pstrUsageClutch, + pstrUsageShifter, + pstrUsageSteering, + pstrUsageTurretDirection, + pstrUsageBarrelElevation, + pstrUsageDivePlane, + pstrUsageBallast, + pstrUsageBicycleCrank, + pstrUsageHandleBars, + pstrUsageFrontBrake, + pstrUsageRearBrake +}; +const char * const ReportDescParserBase::vrTitles0[] PROGMEM = { + pstrUsageBelt, + pstrUsageBodySuit, + pstrUsageFlexor, + pstrUsageGlove, + pstrUsageHeadTracker, + pstrUsageHeadMountedDisplay, + pstrUsageHandTracker, + pstrUsageOculometer, + pstrUsageVest, + pstrUsageAnimatronicDevice +}; +const char * const ReportDescParserBase::vrTitles1[] PROGMEM = { + pstrUsageStereoEnable, + pstrUsageDisplayEnable +}; +const char * const ReportDescParserBase::sportsCtrlTitles0[] PROGMEM = { + pstrUsageBaseballBat, + pstrUsageGolfClub, + pstrUsageRowingMachine, + pstrUsageTreadmill +}; +const char * const ReportDescParserBase::sportsCtrlTitles1[] PROGMEM = { + pstrUsageOar, + pstrUsageSlope, + pstrUsageRate, + pstrUsageStickSpeed, + pstrUsageStickFaceAngle, + pstrUsageStickHeelToe, + pstrUsageStickFollowThough, + pstrUsageStickTempo, + pstrUsageStickType, + pstrUsageStickHeight +}; +const char * const ReportDescParserBase::sportsCtrlTitles2[] PROGMEM = { + pstrUsagePutter, + pstrUsage1Iron, + pstrUsage2Iron, + pstrUsage3Iron, + pstrUsage4Iron, + pstrUsage5Iron, + pstrUsage6Iron, + pstrUsage7Iron, + pstrUsage8Iron, + pstrUsage9Iron, + pstrUsage10Iron, + pstrUsage11Iron, + pstrUsageSandWedge, + pstrUsageLoftWedge, + pstrUsagePowerWedge, + pstrUsage1Wood, + pstrUsage3Wood, + pstrUsage5Wood, + pstrUsage7Wood, + pstrUsage9Wood +}; +const char * const ReportDescParserBase::gameTitles0[] PROGMEM = { + pstrUsage3DGameController, + pstrUsagePinballDevice, + pstrUsageGunDevice +}; +const char * const ReportDescParserBase::gameTitles1[] PROGMEM = { + pstrUsagePointOfView, + pstrUsageTurnRightLeft, + pstrUsagePitchForwardBackward, + pstrUsageRollRightLeft, + pstrUsageMoveRightLeft, + pstrUsageMoveForwardBackward, + pstrUsageMoveUpDown, + pstrUsageLeanRightLeft, + pstrUsageLeanForwardBackward, + pstrUsageHeightOfPOV, + pstrUsageFlipper, + pstrUsageSecondaryFlipper, + pstrUsageBump, + pstrUsageNewGame, + pstrUsageShootBall, + pstrUsagePlayer, + pstrUsageGunBolt, + pstrUsageGunClip, + pstrUsageGunSelector, + pstrUsageGunSingleShot, + pstrUsageGunBurst, + pstrUsageGunAutomatic, + pstrUsageGunSafety, + pstrUsageGamepadFireJump, + pstrUsageGamepadTrigger +}; +const char * const ReportDescParserBase::genDevCtrlTitles[] PROGMEM = { + pstrUsageBatteryStrength, + pstrUsageWirelessChannel, + pstrUsageWirelessID, + pstrUsageDiscoverWirelessControl, + pstrUsageSecurityCodeCharEntered, + pstrUsageSecurityCodeCharErased, + pstrUsageSecurityCodeCleared +}; +const char * const ReportDescParserBase::ledTitles[] PROGMEM = { + pstrUsageNumLock, + pstrUsageCapsLock, + pstrUsageScrollLock, + pstrUsageCompose, + pstrUsageKana, + pstrUsagePower, + pstrUsageShift, + pstrUsageDoNotDisturb, + pstrUsageMute, + pstrUsageToneEnable, + pstrUsageHighCutFilter, + pstrUsageLowCutFilter, + pstrUsageEqualizerEnable, + pstrUsageSoundFieldOn, + pstrUsageSurroundOn, + pstrUsageRepeat, + pstrUsageStereo, + pstrUsageSamplingRateDetect, + pstrUsageSpinning, + pstrUsageCAV, + pstrUsageCLV, + pstrUsageRecordingFormatDetect, + pstrUsageOffHook, + pstrUsageRing, + pstrUsageMessageWaiting, + pstrUsageDataMode, + pstrUsageBatteryOperation, + pstrUsageBatteryOK, + pstrUsageBatteryLow, + pstrUsageSpeaker, + pstrUsageHeadSet, + pstrUsageHold, + pstrUsageMicrophone, + pstrUsageCoverage, + pstrUsageNightMode, + pstrUsageSendCalls, + pstrUsageCallPickup, + pstrUsageConference, + pstrUsageStandBy, + pstrUsageCameraOn, + pstrUsageCameraOff, + pstrUsageOnLine, + pstrUsageOffLine, + pstrUsageBusy, + pstrUsageReady, + pstrUsagePaperOut, + pstrUsagePaperJam, + pstrUsageRemote, + pstrUsageForward, + pstrUsageReverse, + pstrUsageStop, + pstrUsageRewind, + pstrUsageFastForward, + pstrUsagePlay, + pstrUsagePause, + pstrUsageRecord, + pstrUsageError, + pstrUsageSelectedIndicator, + pstrUsageInUseIndicator, + pstrUsageMultiModeIndicator, + pstrUsageIndicatorOn, + pstrUsageIndicatorFlash, + pstrUsageIndicatorSlowBlink, + pstrUsageIndicatorFastBlink, + pstrUsageIndicatorOff, + pstrUsageFlashOnTime, + pstrUsageSlowBlinkOnTime, + pstrUsageSlowBlinkOffTime, + pstrUsageFastBlinkOnTime, + pstrUsageFastBlinkOffTime, + pstrUsageIndicatorColor, + pstrUsageIndicatorRed, + pstrUsageIndicatorGreen, + pstrUsageIndicatorAmber, + pstrUsageGenericIndicator, + pstrUsageSystemSuspend, + pstrUsageExternalPowerConnected +}; +const char * const ReportDescParserBase::telTitles0 [] PROGMEM = { + pstrUsagePhone, + pstrUsageAnsweringMachine, + pstrUsageMessageControls, + pstrUsageHandset, + pstrUsageHeadset, + pstrUsageTelephonyKeyPad, + pstrUsageProgrammableButton +}; +const char * const ReportDescParserBase::telTitles1 [] PROGMEM = { + pstrUsageHookSwitch, + pstrUsageFlash, + pstrUsageFeature, + pstrUsageHold, + pstrUsageRedial, + pstrUsageTransfer, + pstrUsageDrop, + pstrUsagePark, + pstrUsageForwardCalls, + pstrUsageAlternateFunction, + pstrUsageLine, + pstrUsageSpeakerPhone, + pstrUsageConference, + pstrUsageRingEnable, + pstrUsageRingSelect, + pstrUsagePhoneMute, + pstrUsageCallerID, + pstrUsageSend +}; +const char * const ReportDescParserBase::telTitles2 [] PROGMEM = { + pstrUsageSpeedDial, + pstrUsageStoreNumber, + pstrUsageRecallNumber, + pstrUsagePhoneDirectory +}; +const char * const ReportDescParserBase::telTitles3 [] PROGMEM = { + pstrUsageVoiceMail, + pstrUsageScreenCalls, + pstrUsageDoNotDisturb, + pstrUsageMessage, + pstrUsageAnswerOnOff +}; +const char * const ReportDescParserBase::telTitles4 [] PROGMEM = { + pstrUsageInsideDialTone, + pstrUsageOutsideDialTone, + pstrUsageInsideRingTone, + pstrUsageOutsideRingTone, + pstrUsagePriorityRingTone, + pstrUsageInsideRingback, + pstrUsagePriorityRingback, + pstrUsageLineBusyTone, + pstrUsageReorderTone, + pstrUsageCallWaitingTone, + pstrUsageConfirmationTone1, + pstrUsageConfirmationTone2, + pstrUsageTonesOff, + pstrUsageOutsideRingback, + pstrUsageRinger +}; +const char * const ReportDescParserBase::telTitles5 [] PROGMEM = { + pstrUsagePhoneKey0, + pstrUsagePhoneKey1, + pstrUsagePhoneKey2, + pstrUsagePhoneKey3, + pstrUsagePhoneKey4, + pstrUsagePhoneKey5, + pstrUsagePhoneKey6, + pstrUsagePhoneKey7, + pstrUsagePhoneKey8, + pstrUsagePhoneKey9, + pstrUsagePhoneKeyStar, + pstrUsagePhoneKeyPound, + pstrUsagePhoneKeyA, + pstrUsagePhoneKeyB, + pstrUsagePhoneKeyC, + pstrUsagePhoneKeyD +}; +const char * const ReportDescParserBase::consTitles0[] PROGMEM = { + pstrUsageConsumerControl, + pstrUsageNumericKeyPad, + pstrUsageProgrammableButton, + pstrUsageMicrophone, + pstrUsageHeadphone, + pstrUsageGraphicEqualizer +}; +const char * const ReportDescParserBase::consTitles1[] PROGMEM = { + pstrUsagePlus10, + pstrUsagePlus100, + pstrUsageAMPM +}; +const char * const ReportDescParserBase::consTitles2[] PROGMEM = { + pstrUsagePower, + pstrUsageReset, + pstrUsageSleep, + pstrUsageSleepAfter, + pstrUsageSleepMode, + pstrUsageIllumination, + pstrUsageFunctionButtons + +}; +const char * const ReportDescParserBase::consTitles3[] PROGMEM = { + pstrUsageMenu, + pstrUsageMenuPick, + pstrUsageMenuUp, + pstrUsageMenuDown, + pstrUsageMenuLeft, + pstrUsageMenuRight, + pstrUsageMenuEscape, + pstrUsageMenuValueIncrease, + pstrUsageMenuValueDecrease +}; +const char * const ReportDescParserBase::consTitles4[] PROGMEM = { + pstrUsageDataOnScreen, + pstrUsageClosedCaption, + pstrUsageClosedCaptionSelect, + pstrUsageVCRTV, + pstrUsageBroadcastMode, + pstrUsageSnapshot, + pstrUsageStill +}; +const char * const ReportDescParserBase::consTitles5[] PROGMEM = { + pstrUsageSelection, + pstrUsageAssignSelection, + pstrUsageModeStep, + pstrUsageRecallLast, + pstrUsageEnterChannel, + pstrUsageOrderMovie, + pstrUsageChannel, + pstrUsageMediaSelection, + pstrUsageMediaSelectComputer, + pstrUsageMediaSelectTV, + pstrUsageMediaSelectWWW, + pstrUsageMediaSelectDVD, + pstrUsageMediaSelectTelephone, + pstrUsageMediaSelectProgramGuide, + pstrUsageMediaSelectVideoPhone, + pstrUsageMediaSelectGames, + pstrUsageMediaSelectMessages, + pstrUsageMediaSelectCD, + pstrUsageMediaSelectVCR, + pstrUsageMediaSelectTuner, + pstrUsageQuit, + pstrUsageHelp, + pstrUsageMediaSelectTape, + pstrUsageMediaSelectCable, + pstrUsageMediaSelectSatellite, + pstrUsageMediaSelectSecurity, + pstrUsageMediaSelectHome, + pstrUsageMediaSelectCall, + pstrUsageChannelIncrement, + pstrUsageChannelDecrement, + pstrUsageMediaSelectSAP, + pstrUsagePageReserved, + pstrUsageVCRPlus, + pstrUsageOnce, + pstrUsageDaily, + pstrUsageWeekly, + pstrUsageMonthly +}; +const char * const ReportDescParserBase::consTitles6[] PROGMEM = { + pstrUsagePlay, + pstrUsagePause, + pstrUsageRecord, + pstrUsageFastForward, + pstrUsageRewind, + pstrUsageScanNextTrack, + pstrUsageScanPreviousTrack, + pstrUsageStop, + pstrUsageEject, + pstrUsageRandomPlay, + pstrUsageSelectDisk, + pstrUsageEnterDisk, + pstrUsageRepeat, + pstrUsageTracking, + pstrUsageTrackNormal, + pstrUsageSlowTracking, + pstrUsageFrameForward, + pstrUsageFrameBackwards, + pstrUsageMark, + pstrUsageClearMark, + pstrUsageRepeatFromMark, + pstrUsageReturnToMark, + pstrUsageSearchMarkForward, + pstrUsageSearchMarkBackwards, + pstrUsageCounterReset, + pstrUsageShowCounter, + pstrUsageTrackingIncrement, + pstrUsageTrackingDecrement, + pstrUsageStopEject, + pstrUsagePlayPause, + pstrUsagePlaySkip +}; +const char * const ReportDescParserBase::consTitles7[] PROGMEM = { + pstrUsageVolume, + pstrUsageBalance, + pstrUsageMute, + pstrUsageBass, + pstrUsageTreble, + pstrUsageBassBoost, + pstrUsageSurroundMode, + pstrUsageLoudness, + pstrUsageMPX, + pstrUsageVolumeIncrement, + pstrUsageVolumeDecrement +}; +const char * const ReportDescParserBase::consTitles8[] PROGMEM = { + pstrUsageSpeedSelect, + pstrUsagePlaybackSpeed, + pstrUsageStandardPlay, + pstrUsageLongPlay, + pstrUsageExtendedPlay, + pstrUsageSlow +}; +const char * const ReportDescParserBase::consTitles9[] PROGMEM = { + pstrUsageFanEnable, + pstrUsageFanSpeed, + pstrUsageLightEnable, + pstrUsageLightIlluminationLevel, + pstrUsageClimateControlEnable, + pstrUsageRoomTemperature, + pstrUsageSecurityEnable, + pstrUsageFireAlarm, + pstrUsagePoliceAlarm, + pstrUsageProximity, + pstrUsageMotion, + pstrUsageDuresAlarm, + pstrUsageHoldupAlarm, + pstrUsageMedicalAlarm +}; +const char * const ReportDescParserBase::consTitlesA[] PROGMEM = { + pstrUsageBalanceRight, + pstrUsageBalanceLeft, + pstrUsageBassIncrement, + pstrUsageBassDecrement, + pstrUsageTrebleIncrement, + pstrUsageTrebleDecrement +}; +const char * const ReportDescParserBase::consTitlesB[] PROGMEM = { + pstrUsageSpeakerSystem, + pstrUsageChannelLeft, + pstrUsageChannelRight, + pstrUsageChannelCenter, + pstrUsageChannelFront, + pstrUsageChannelCenterFront, + pstrUsageChannelSide, + pstrUsageChannelSurround, + pstrUsageChannelLowFreqEnhancement, + pstrUsageChannelTop, + pstrUsageChannelUnknown +}; +const char * const ReportDescParserBase::consTitlesC[] PROGMEM = { + pstrUsageSubChannel, + pstrUsageSubChannelIncrement, + pstrUsageSubChannelDecrement, + pstrUsageAlternateAudioIncrement, + pstrUsageAlternateAudioDecrement +}; +const char * const ReportDescParserBase::consTitlesD[] PROGMEM = { + pstrUsageApplicationLaunchButtons, + pstrUsageALLaunchButtonConfigTool, + pstrUsageALProgrammableButton, + pstrUsageALConsumerControlConfig, + pstrUsageALWordProcessor, + pstrUsageALTextEditor, + pstrUsageALSpreadsheet, + pstrUsageALGraphicsEditor, + pstrUsageALPresentationApp, + pstrUsageALDatabaseApp, + pstrUsageALEmailReader, + pstrUsageALNewsreader, + pstrUsageALVoicemail, + pstrUsageALContactsAddressBook, + pstrUsageALCalendarSchedule, + pstrUsageALTaskProjectManager, + pstrUsageALLogJournalTimecard, + pstrUsageALCheckbookFinance, + pstrUsageALCalculator, + pstrUsageALAVCapturePlayback, + pstrUsageALLocalMachineBrowser, + pstrUsageALLANWANBrow, + pstrUsageALInternetBrowser, + pstrUsageALRemoteNetISPConnect, + pstrUsageALNetworkConference, + pstrUsageALNetworkChat, + pstrUsageALTelephonyDialer, + pstrUsageALLogon, + pstrUsageALLogoff, + pstrUsageALLogonLogoff, + pstrUsageALTermLockScrSav, + pstrUsageALControlPannel, + pstrUsageALCommandLineProcessorRun, + pstrUsageALProcessTaskManager, + pstrUsageALSelectTaskApplication, + pstrUsageALNextTaskApplication, + pstrUsageALPreviousTaskApplication, + pstrUsageALPreemptiveHaltTaskApp, + pstrUsageALIntegratedHelpCenter, + pstrUsageALDocuments, + pstrUsageALThesaurus, + pstrUsageALDictionary, + pstrUsageALDesktop, + pstrUsageALSpellCheck, + pstrUsageALGrammarCheck, + pstrUsageALWirelessStatus, + pstrUsageALKeyboardLayout, + pstrUsageALVirusProtection, + pstrUsageALEncryption, + pstrUsageALScreenSaver, + pstrUsageALAlarms, + pstrUsageALClock, + pstrUsageALFileBrowser, + pstrUsageALPowerStatus, + pstrUsageALImageBrowser, + pstrUsageALAudioBrowser, + pstrUsageALMovieBrowser, + pstrUsageALDigitalRightsManager, + pstrUsageALDigitalWallet, + pstrUsagePageReserved, + pstrUsageALInstantMessaging, + pstrUsageALOEMFeaturesBrowser, + pstrUsageALOEMHelp, + pstrUsageALOnlineCommunity, + pstrUsageALEntertainmentContentBrow, + pstrUsageALOnlineShoppingBrowser, + pstrUsageALSmartCardInfoHelp, + pstrUsageALMarketMonitorFinBrowser, + pstrUsageALCustomCorpNewsBrowser, + pstrUsageALOnlineActivityBrowser, + pstrUsageALResearchSearchBrowser, + pstrUsageALAudioPlayer +}; +const char * const ReportDescParserBase::consTitlesE[] PROGMEM = { + pstrUsageGenericGUIAppControls, + pstrUsageACNew, + pstrUsageACOpen, + pstrUsageACClose, + pstrUsageACExit, + pstrUsageACMaximize, + pstrUsageACMinimize, + pstrUsageACSave, + pstrUsageACPrint, + pstrUsageACProperties, + pstrUsageACUndo, + pstrUsageACCopy, + pstrUsageACCut, + pstrUsageACPaste, + pstrUsageACSelectAll, + pstrUsageACFind, + pstrUsageACFindAndReplace, + pstrUsageACSearch, + pstrUsageACGoto, + pstrUsageACHome, + pstrUsageACBack, + pstrUsageACForward, + pstrUsageACStop, + pstrUsageACRefresh, + pstrUsageACPreviousLink, + pstrUsageACNextLink, + pstrUsageACBookmarks, + pstrUsageACHistory, + pstrUsageACSubscriptions, + pstrUsageACZoomIn, + pstrUsageACZoomOut, + pstrUsageACZoom, + pstrUsageACFullScreenView, + pstrUsageACNormalView, + pstrUsageACViewToggle, + pstrUsageACScrollUp, + pstrUsageACScrollDown, + pstrUsageACScroll, + pstrUsageACPanLeft, + pstrUsageACPanRight, + pstrUsageACPan, + pstrUsageACNewWindow, + pstrUsageACTileHoriz, + pstrUsageACTileVert, + pstrUsageACFormat, + pstrUsageACEdit, + pstrUsageACBold, + pstrUsageACItalics, + pstrUsageACUnderline, + pstrUsageACStrikethrough, + pstrUsageACSubscript, + pstrUsageACSuperscript, + pstrUsageACAllCaps, + pstrUsageACRotate, + pstrUsageACResize, + pstrUsageACFlipHorizontal, + pstrUsageACFlipVertical, + pstrUsageACMirrorHorizontal, + pstrUsageACMirrorVertical, + pstrUsageACFontSelect, + pstrUsageACFontColor, + pstrUsageACFontSize, + pstrUsageACJustifyLeft, + pstrUsageACJustifyCenterH, + pstrUsageACJustifyRight, + pstrUsageACJustifyBlockH, + pstrUsageACJustifyTop, + pstrUsageACJustifyCenterV, + pstrUsageACJustifyBottom, + pstrUsageACJustifyBlockV, + pstrUsageACIndentDecrease, + pstrUsageACIndentIncrease, + pstrUsageACNumberedList, + pstrUsageACRestartNumbering, + pstrUsageACBulletedList, + pstrUsageACPromote, + pstrUsageACDemote, + pstrUsageACYes, + pstrUsageACNo, + pstrUsageACCancel, + pstrUsageACCatalog, + pstrUsageACBuyChkout, + pstrUsageACAddToCart, + pstrUsageACExpand, + pstrUsageACExpandAll, + pstrUsageACCollapse, + pstrUsageACCollapseAll, + pstrUsageACPrintPreview, + pstrUsageACPasteSpecial, + pstrUsageACInsertMode, + pstrUsageACDelete, + pstrUsageACLock, + pstrUsageACUnlock, + pstrUsageACProtect, + pstrUsageACUnprotect, + pstrUsageACAttachComment, + pstrUsageACDeleteComment, + pstrUsageACViewComment, + pstrUsageACSelectWord, + pstrUsageACSelectSentence, + pstrUsageACSelectParagraph, + pstrUsageACSelectColumn, + pstrUsageACSelectRow, + pstrUsageACSelectTable, + pstrUsageACSelectObject, + pstrUsageACRedoRepeat, + pstrUsageACSort, + pstrUsageACSortAscending, + pstrUsageACSortDescending, + pstrUsageACFilter, + pstrUsageACSetClock, + pstrUsageACViewClock, + pstrUsageACSelectTimeZone, + pstrUsageACEditTimeZone, + pstrUsageACSetAlarm, + pstrUsageACClearAlarm, + pstrUsageACSnoozeAlarm, + pstrUsageACResetAlarm, + pstrUsageACSyncronize, + pstrUsageACSendReceive, + pstrUsageACSendTo, + pstrUsageACReply, + pstrUsageACReplyAll, + pstrUsageACForwardMessage, + pstrUsageACSend, + pstrUsageACAttachFile, + pstrUsageACUpload, + pstrUsageACDownload, + pstrUsageACSetBorders, + pstrUsageACInsertRow, + pstrUsageACInsertColumn, + pstrUsageACInsertFile, + pstrUsageACInsertPicture, + pstrUsageACInsertObject, + pstrUsageACInsertSymbol, + pstrUsageACSaveAndClose, + pstrUsageACRename, + pstrUsageACMerge, + pstrUsageACSplit, + pstrUsageACDistributeHorizontaly, + pstrUsageACDistributeVerticaly +}; +const char * const ReportDescParserBase::digitTitles0[] PROGMEM = { + pstrUsageDigitizer, + pstrUsagePen, + pstrUsageLightPen, + pstrUsageTouchScreen, + pstrUsageTouchPad, + pstrUsageWhiteBoard, + pstrUsageCoordinateMeasuringMachine, + pstrUsage3DDigitizer, + pstrUsageStereoPlotter, + pstrUsageArticulatedArm, + pstrUsageArmature, + pstrUsageMultiplePointDigitizer, + pstrUsageFreeSpaceWand +}; +const char * const ReportDescParserBase::digitTitles1[] PROGMEM = { + pstrUsageStylus, + pstrUsagePuck, + pstrUsageFinger + +}; +const char * const ReportDescParserBase::digitTitles2[] PROGMEM = { + pstrUsageTipPressure, + pstrUsageBarrelPressure, + pstrUsageInRange, + pstrUsageTouch, + pstrUsageUntouch, + pstrUsageTap, + pstrUsageQuality, + pstrUsageDataValid, + pstrUsageTransducerIndex, + pstrUsageTabletFunctionKeys, + pstrUsageProgramChangeKeys, + pstrUsageBatteryStrength, + pstrUsageInvert, + pstrUsageXTilt, + pstrUsageYTilt, + pstrUsageAzimuth, + pstrUsageAltitude, + pstrUsageTwist, + pstrUsageTipSwitch, + pstrUsageSecondaryTipSwitch, + pstrUsageBarrelSwitch, + pstrUsageEraser, + pstrUsageTabletPick +}; +const char * const ReportDescParserBase::aplphanumTitles0[] PROGMEM = { + pstrUsageAlphanumericDisplay, + pstrUsageBitmappedDisplay +}; +const char * const ReportDescParserBase::aplphanumTitles1[] PROGMEM = { + pstrUsageDisplayAttributesReport, + pstrUsageASCIICharacterSet, + pstrUsageDataReadBack, + pstrUsageFontReadBack, + pstrUsageDisplayControlReport, + pstrUsageClearDisplay, + pstrUsageDisplayEnable, + pstrUsageScreenSaverDelay, + pstrUsageScreenSaverEnable, + pstrUsageVerticalScroll, + pstrUsageHorizontalScroll, + pstrUsageCharacterReport, + pstrUsageDisplayData, + pstrUsageDisplayStatus, + pstrUsageStatusNotReady, + pstrUsageStatusReady, + pstrUsageErrorNotALoadableCharacter, + pstrUsageErrorFotDataCanNotBeRead, + pstrUsageCursorPositionReport, + pstrUsageRow, + pstrUsageColumn, + pstrUsageRows, + pstrUsageColumns, + pstrUsageCursorPixelPosition, + pstrUsageCursorMode, + pstrUsageCursorEnable, + pstrUsageCursorBlink, + pstrUsageFontReport, + pstrUsageFontData, + pstrUsageCharacterWidth, + pstrUsageCharacterHeight, + pstrUsageCharacterSpacingHorizontal, + pstrUsageCharacterSpacingVertical, + pstrUsageUnicodeCharset, + pstrUsageFont7Segment, + pstrUsage7SegmentDirectMap, + pstrUsageFont14Segment, + pstrUsage14SegmentDirectMap, + pstrUsageDisplayBrightness, + pstrUsageDisplayContrast, + pstrUsageCharacterAttribute, + pstrUsageAttributeReadback, + pstrUsageAttributeData, + pstrUsageCharAttributeEnhance, + pstrUsageCharAttributeUnderline, + pstrUsageCharAttributeBlink +}; +const char * const ReportDescParserBase::aplphanumTitles2[] PROGMEM = { + pstrUsageBitmapSizeX, + pstrUsageBitmapSizeY, + pstrUsagePageReserved, + pstrUsageBitDepthFormat, + pstrUsageDisplayOrientation, + pstrUsagePaletteReport, + pstrUsagePaletteDataSize, + pstrUsagePaletteDataOffset, + pstrUsagePaletteData, + pstrUsageBlitReport, + pstrUsageBlitRectangleX1, + pstrUsageBlitRectangleY1, + pstrUsageBlitRectangleX2, + pstrUsageBlitRectangleY2, + pstrUsageBlitData, + pstrUsageSoftButton, + pstrUsageSoftButtonID, + pstrUsageSoftButtonSide, + pstrUsageSoftButtonOffset1, + pstrUsageSoftButtonOffset2, + pstrUsageSoftButtonReport +}; +const char * const ReportDescParserBase::medInstrTitles0[] PROGMEM = { + pstrUsageVCRAcquisition, + pstrUsageFreezeThaw, + pstrUsageClipStore, + pstrUsageUpdate, + pstrUsageNext, + pstrUsageSave, + pstrUsagePrint, + pstrUsageMicrophoneEnable +}; +const char * const ReportDescParserBase::medInstrTitles1[] PROGMEM = { + pstrUsageCine, + pstrUsageTransmitPower, + pstrUsageVolume, + pstrUsageFocus, + pstrUsageDepth +}; +const char * const ReportDescParserBase::medInstrTitles2[] PROGMEM = { + pstrUsageSoftStepPrimary, + pstrUsageSoftStepSecondary +}; +const char * const ReportDescParserBase::medInstrTitles3[] PROGMEM = { + pstrUsageZoomSelect, + pstrUsageZoomAdjust, + pstrUsageSpectralDopplerModeSelect, + pstrUsageSpectralDopplerModeAdjust, + pstrUsageColorDopplerModeSelect, + pstrUsageColorDopplerModeAdjust, + pstrUsageMotionModeSelect, + pstrUsageMotionModeAdjust, + pstrUsage2DModeSelect, + pstrUsage2DModeAdjust +}; +const char * const ReportDescParserBase::medInstrTitles4[] PROGMEM = { + pstrUsageSoftControlSelect, + pstrUsageSoftControlAdjust +}; + +void ReportDescParserBase::Parse(const uint16_t len, const uint8_t *pbuf, const uint16_t &offset) { + uint16_t cntdn = (uint16_t)len; + uint8_t *p = (uint8_t*)pbuf; + + + totalSize = 0; + + while(cntdn) { + //USB_HOST_SERIAL.println(""); + //PrintHex(offset + len - cntdn); + //USB_HOST_SERIAL.print(":"); + + ParseItem(&p, &cntdn); + + //if (ParseItem(&p, &cntdn)) + // return; + } + //USBTRACE2("Total:", totalSize); +} + +void ReportDescParserBase::PrintValue(uint8_t *p, uint8_t len) { + E_Notify(PSTR("("), 0x80); + for(; len; p++, len--) + PrintHex (*p, 0x80); + E_Notify(PSTR(")"), 0x80); +} + +void ReportDescParserBase::PrintByteValue(uint8_t data) { + E_Notify(PSTR("("), 0x80); + PrintHex (data, 0x80); + E_Notify(PSTR(")"), 0x80); +} + +void ReportDescParserBase::PrintItemTitle(uint8_t prefix) { + switch(prefix & (TYPE_MASK | TAG_MASK)) { + case (TYPE_GLOBAL | TAG_GLOBAL_PUSH): + E_Notify(PSTR("\r\nPush"), 0x80); + break; + case (TYPE_GLOBAL | TAG_GLOBAL_POP): + E_Notify(PSTR("\r\nPop"), 0x80); + break; + case (TYPE_GLOBAL | TAG_GLOBAL_USAGEPAGE): + E_Notify(PSTR("\r\nUsage Page"), 0x80); + break; + case (TYPE_GLOBAL | TAG_GLOBAL_LOGICALMIN): + E_Notify(PSTR("\r\nLogical Min"), 0x80); + break; + case (TYPE_GLOBAL | TAG_GLOBAL_LOGICALMAX): + E_Notify(PSTR("\r\nLogical Max"), 0x80); + break; + case (TYPE_GLOBAL | TAG_GLOBAL_PHYSMIN): + E_Notify(PSTR("\r\nPhysical Min"), 0x80); + break; + case (TYPE_GLOBAL | TAG_GLOBAL_PHYSMAX): + E_Notify(PSTR("\r\nPhysical Max"), 0x80); + break; + case (TYPE_GLOBAL | TAG_GLOBAL_UNITEXP): + E_Notify(PSTR("\r\nUnit Exp"), 0x80); + break; + case (TYPE_GLOBAL | TAG_GLOBAL_UNIT): + E_Notify(PSTR("\r\nUnit"), 0x80); + break; + case (TYPE_GLOBAL | TAG_GLOBAL_REPORTSIZE): + E_Notify(PSTR("\r\nReport Size"), 0x80); + break; + case (TYPE_GLOBAL | TAG_GLOBAL_REPORTCOUNT): + E_Notify(PSTR("\r\nReport Count"), 0x80); + break; + case (TYPE_GLOBAL | TAG_GLOBAL_REPORTID): + E_Notify(PSTR("\r\nReport Id"), 0x80); + break; + case (TYPE_LOCAL | TAG_LOCAL_USAGE): + E_Notify(PSTR("\r\nUsage"), 0x80); + break; + case (TYPE_LOCAL | TAG_LOCAL_USAGEMIN): + E_Notify(PSTR("\r\nUsage Min"), 0x80); + break; + case (TYPE_LOCAL | TAG_LOCAL_USAGEMAX): + E_Notify(PSTR("\r\nUsage Max"), 0x80); + break; + case (TYPE_MAIN | TAG_MAIN_COLLECTION): + E_Notify(PSTR("\r\nCollection"), 0x80); + break; + case (TYPE_MAIN | TAG_MAIN_ENDCOLLECTION): + E_Notify(PSTR("\r\nEnd Collection"), 0x80); + break; + case (TYPE_MAIN | TAG_MAIN_INPUT): + E_Notify(PSTR("\r\nInput"), 0x80); + break; + case (TYPE_MAIN | TAG_MAIN_OUTPUT): + E_Notify(PSTR("\r\nOutput"), 0x80); + break; + case (TYPE_MAIN | TAG_MAIN_FEATURE): + E_Notify(PSTR("\r\nFeature"), 0x80); + break; + } // switch (**pp & (TYPE_MASK | TAG_MASK)) +} + +uint8_t ReportDescParserBase::ParseItem(uint8_t **pp, uint16_t *pcntdn) { + //uint8_t ret = enErrorSuccess; + //reinterpret_cast<>(varBuffer); + switch(itemParseState) { + case 0: + if(**pp == HID_LONG_ITEM_PREFIX) + USBTRACE("\r\nLONG\r\n"); + else { + uint8_t size = ((**pp) & DATA_SIZE_MASK); + + itemPrefix = (**pp); + itemSize = 1 + ((size == DATA_SIZE_4) ? 4 : size); + + PrintItemTitle(itemPrefix); + } + (*pp)++; + (*pcntdn)--; + itemSize--; + itemParseState = 1; + + if(!itemSize) + break; + + if(!pcntdn) + return enErrorIncomplete; + case 1: + //USBTRACE2("\r\niSz:",itemSize); + + theBuffer.valueSize = itemSize; + valParser.Initialize(&theBuffer); + itemParseState = 2; + case 2: + if(!valParser.Parse(pp, pcntdn)) + return enErrorIncomplete; + itemParseState = 3; + case 3: + { + uint8_t data = *((uint8_t*)varBuffer); + + switch(itemPrefix & (TYPE_MASK | TAG_MASK)) { + case (TYPE_LOCAL | TAG_LOCAL_USAGE): + if(pfUsage) { + if(theBuffer.valueSize > 1) { + uint16_t* ui16 = reinterpret_cast(varBuffer); + pfUsage(*ui16); + } else + pfUsage(data); + } + break; + case (TYPE_GLOBAL | TAG_GLOBAL_REPORTSIZE): + rptSize = data; + PrintByteValue(data); + break; + case (TYPE_GLOBAL | TAG_GLOBAL_REPORTCOUNT): + rptCount = data; + PrintByteValue(data); + break; + case (TYPE_GLOBAL | TAG_GLOBAL_LOGICALMIN): + case (TYPE_GLOBAL | TAG_GLOBAL_LOGICALMAX): + case (TYPE_GLOBAL | TAG_GLOBAL_PHYSMIN): + case (TYPE_GLOBAL | TAG_GLOBAL_PHYSMAX): + case (TYPE_GLOBAL | TAG_GLOBAL_REPORTID): + case (TYPE_LOCAL | TAG_LOCAL_USAGEMIN): + case (TYPE_LOCAL | TAG_LOCAL_USAGEMAX): + case (TYPE_GLOBAL | TAG_GLOBAL_UNITEXP): + case (TYPE_GLOBAL | TAG_GLOBAL_UNIT): + PrintValue(varBuffer, theBuffer.valueSize); + break; + case (TYPE_GLOBAL | TAG_GLOBAL_PUSH): + case (TYPE_GLOBAL | TAG_GLOBAL_POP): + break; + case (TYPE_GLOBAL | TAG_GLOBAL_USAGEPAGE): + SetUsagePage(data); + PrintUsagePage(data); + PrintByteValue(data); + break; + case (TYPE_MAIN | TAG_MAIN_COLLECTION): + case (TYPE_MAIN | TAG_MAIN_ENDCOLLECTION): + switch(data) { + case 0x00: + E_Notify(PSTR(" Physical"), 0x80); + break; + case 0x01: + E_Notify(PSTR(" Application"), 0x80); + break; + case 0x02: + E_Notify(PSTR(" Logical"), 0x80); + break; + case 0x03: + E_Notify(PSTR(" Report"), 0x80); + break; + case 0x04: + E_Notify(PSTR(" Named Array"), 0x80); + break; + case 0x05: + E_Notify(PSTR(" Usage Switch"), 0x80); + break; + case 0x06: + E_Notify(PSTR(" Usage Modifier"), 0x80); + break; + default: + E_Notify(PSTR(" Vendor Defined("), 0x80); + PrintHex (data, 0x80); + E_Notify(PSTR(")"), 0x80); + } + break; + case (TYPE_MAIN | TAG_MAIN_INPUT): + case (TYPE_MAIN | TAG_MAIN_OUTPUT): + case (TYPE_MAIN | TAG_MAIN_FEATURE): + totalSize += (uint16_t)rptSize * (uint16_t)rptCount; + rptSize = 0; + rptCount = 0; + E_Notify(PSTR("("), 0x80); + PrintBin (data, 0x80); + E_Notify(PSTR(")"), 0x80); + break; + } // switch (**pp & (TYPE_MASK | TAG_MASK)) + } + } // switch (itemParseState) + itemParseState = 0; + return enErrorSuccess; +} + +ReportDescParserBase::UsagePageFunc ReportDescParserBase::usagePageFunctions[] /*PROGMEM*/ = { + &ReportDescParserBase::PrintGenericDesktopPageUsage, + &ReportDescParserBase::PrintSimulationControlsPageUsage, + &ReportDescParserBase::PrintVRControlsPageUsage, + &ReportDescParserBase::PrintSportsControlsPageUsage, + &ReportDescParserBase::PrintGameControlsPageUsage, + &ReportDescParserBase::PrintGenericDeviceControlsPageUsage, + NULL, // Keyboard/Keypad + &ReportDescParserBase::PrintLEDPageUsage, + &ReportDescParserBase::PrintButtonPageUsage, + &ReportDescParserBase::PrintOrdinalPageUsage, + &ReportDescParserBase::PrintTelephonyPageUsage, + &ReportDescParserBase::PrintConsumerPageUsage, + &ReportDescParserBase::PrintDigitizerPageUsage, + NULL, // Reserved + NULL, // PID + NULL // Unicode +}; + +void ReportDescParserBase::SetUsagePage(uint16_t page) { + pfUsage = NULL; + + if(VALUE_BETWEEN(page, 0x00, 0x11)) { + pfUsage = (usagePageFunctions[page - 1]); + + } else { + switch(page) { + case 0x14: + pfUsage = &ReportDescParserBase::PrintAlphanumDisplayPageUsage; + break; + case 0x40: + pfUsage = &ReportDescParserBase::PrintMedicalInstrumentPageUsage; + break; + } + } +} + +void ReportDescParserBase::PrintUsagePage(uint16_t page) { + const char * const * w; + E_Notify(pstrSpace, 0x80); + + output_if_between(page, 0x00, 0x11, w, E_Notify, usagePageTitles0, 0x80) + else output_if_between(page, 0x8b, 0x92, w, E_Notify, usagePageTitles1, 0x80) + else if(VALUE_BETWEEN(page, 0x7f, 0x84)) + E_Notify(pstrUsagePageMonitor, 0x80); + else if(VALUE_BETWEEN(page, 0x83, 0x8c)) + E_Notify(pstrUsagePagePower, 0x80); + else if(page > 0xfeff /* && page <= 0xffff */) + E_Notify(pstrUsagePageVendorDefined, 0x80); + else + switch(page) { + case 0x14: + E_Notify(pstrUsagePageAlphaNumericDisplay, 0x80); + break; + case 0x40: + E_Notify(pstrUsagePageMedicalInstruments, 0x80); + break; + default: + E_Notify(pstrUsagePageUndefined, 0x80); + } +} + +void ReportDescParserBase::PrintButtonPageUsage(uint16_t usage) { + E_Notify(pstrSpace, 0x80); + E_Notify(PSTR("Btn"), 0x80); + PrintHex (usage, 0x80); + E_Notify(PSTR("\r\n"), 0x80); + //USB_HOST_SERIAL.print(usage, HEX); +} + +void ReportDescParserBase::PrintOrdinalPageUsage(uint16_t usage) { + E_Notify(pstrSpace, 0x80); + E_Notify(PSTR("Inst"), 0x80); + // Sorry, HEX for now... + PrintHex (usage, 0x80); + E_Notify(PSTR("\r\n"), 0x80); + //USB_HOST_SERIAL.print(usage, DEC); +} + +void ReportDescParserBase::PrintGenericDesktopPageUsage(uint16_t usage) { + const char * const * w; + E_Notify(pstrSpace, 0x80); + + output_if_between(usage, 0x00, 0x0a, w, E_Notify, genDesktopTitles0, 0x80) + else output_if_between(usage, 0x2f, 0x49, w, E_Notify, genDesktopTitles1, 0x80) + else output_if_between(usage, 0x7f, 0x94, w, E_Notify, genDesktopTitles2, 0x80) + else output_if_between(usage, 0x9f, 0xa9, w, E_Notify, genDesktopTitles3, 0x80) + else output_if_between(usage, 0xaf, 0xb8, w, E_Notify, genDesktopTitles4, 0x80) + else E_Notify(pstrUsagePageUndefined, 0x80); +} + +void ReportDescParserBase::PrintSimulationControlsPageUsage(uint16_t usage) { + const char * const * w; + E_Notify(pstrSpace, 0x80); + + output_if_between(usage, 0x00, 0x0d, w, E_Notify, simuTitles0, 0x80) + else output_if_between(usage, 0x1f, 0x26, w, E_Notify, simuTitles1, 0x80) + else output_if_between(usage, 0xaf, 0xd1, w, E_Notify, simuTitles2, 0x80) + else E_Notify(pstrUsagePageUndefined, 0x80); +} + +void ReportDescParserBase::PrintVRControlsPageUsage(uint16_t usage) { + const char * const * w; + E_Notify(pstrSpace, 0x80); + + output_if_between(usage, 0x00, 0x0b, w, E_Notify, vrTitles0, 0x80) + else output_if_between(usage, 0x1f, 0x22, w, E_Notify, vrTitles1, 0x80) + else E_Notify(pstrUsagePageUndefined, 0x80); +} + +void ReportDescParserBase::PrintSportsControlsPageUsage(uint16_t usage) { + const char * const * w; + E_Notify(pstrSpace, 0x80); + + output_if_between(usage, 0x00, 0x05, w, E_Notify, sportsCtrlTitles0, 0x80) + else output_if_between(usage, 0x2f, 0x3a, w, E_Notify, sportsCtrlTitles1, 0x80) + else output_if_between(usage, 0x4f, 0x64, w, E_Notify, sportsCtrlTitles2, 0x80) + else E_Notify(pstrUsagePageUndefined, 0x80); +} + +void ReportDescParserBase::PrintGameControlsPageUsage(uint16_t usage) { + const char * const * w; + E_Notify(pstrSpace, 0x80); + + output_if_between(usage, 0x00, 0x04, w, E_Notify, gameTitles0, 0x80) + else output_if_between(usage, 0x1f, 0x3a, w, E_Notify, gameTitles1, 0x80) + else E_Notify(pstrUsagePageUndefined, 0x80); +} + +void ReportDescParserBase::PrintGenericDeviceControlsPageUsage(uint16_t usage) { + const char * const * w; + E_Notify(pstrSpace, 0x80); + + output_if_between(usage, 0x1f, 0x27, w, E_Notify, genDevCtrlTitles, 0x80) + else E_Notify(pstrUsagePageUndefined, 0x80); +} + +void ReportDescParserBase::PrintLEDPageUsage(uint16_t usage) { + const char * const * w; + E_Notify(pstrSpace, 0x80); + + output_if_between(usage, 0x00, 0x4e, w, E_Notify, ledTitles, 0x80) + else E_Notify(pstrUsagePageUndefined, 0x80); +} + +void ReportDescParserBase::PrintTelephonyPageUsage(uint16_t usage) { + const char * const * w; + E_Notify(pstrSpace, 0x80); + + output_if_between(usage, 0x00, 0x08, w, E_Notify, telTitles0, 0x80) + else output_if_between(usage, 0x1f, 0x32, w, E_Notify, telTitles1, 0x80) + else output_if_between(usage, 0x4f, 0x54, w, E_Notify, telTitles2, 0x80) + else output_if_between(usage, 0x6f, 0x75, w, E_Notify, telTitles3, 0x80) + else output_if_between(usage, 0x8f, 0x9f, w, E_Notify, telTitles4, 0x80) + else output_if_between(usage, 0xaf, 0xc0, w, E_Notify, telTitles5, 0x80) + else E_Notify(pstrUsagePageUndefined, 0x80); +} + +void ReportDescParserBase::PrintConsumerPageUsage(uint16_t usage) { + const char * const * w; + E_Notify(pstrSpace, 0x80); + + output_if_between(usage, 0x00, 0x07, w, E_Notify, consTitles0, 0x80) + else output_if_between(usage, 0x1f, 0x23, w, E_Notify, consTitles1, 0x80) + else output_if_between(usage, 0x2f, 0x37, w, E_Notify, consTitles2, 0x80) + else output_if_between(usage, 0x3f, 0x49, w, E_Notify, consTitles3, 0x80) + else output_if_between(usage, 0x5f, 0x67, w, E_Notify, consTitles4, 0x80) + else output_if_between(usage, 0x7f, 0xa5, w, E_Notify, consTitles5, 0x80) + else output_if_between(usage, 0xaf, 0xcf, w, E_Notify, consTitles6, 0x80) + else output_if_between(usage, 0xdf, 0xeb, w, E_Notify, consTitles7, 0x80) + else output_if_between(usage, 0xef, 0xf6, w, E_Notify, consTitles8, 0x80) + else output_if_between(usage, 0xff, 0x10e, w, E_Notify, consTitles9, 0x80) + else output_if_between(usage, 0x14f, 0x156, w, E_Notify, consTitlesA, 0x80) + else output_if_between(usage, 0x15f, 0x16b, w, E_Notify, consTitlesB, 0x80) + else output_if_between(usage, 0x16f, 0x175, w, E_Notify, consTitlesC, 0x80) + else output_if_between(usage, 0x17f, 0x1c8, w, E_Notify, consTitlesD, 0x80) + else output_if_between(usage, 0x1ff, 0x29d, w, E_Notify, consTitlesE, 0x80) + else E_Notify(pstrUsagePageUndefined, 0x80); +} + +void ReportDescParserBase::PrintDigitizerPageUsage(uint16_t usage) { + const char * const * w; + E_Notify(pstrSpace, 0x80); + + output_if_between(usage, 0x00, 0x0e, w, E_Notify, digitTitles0, 0x80) + else output_if_between(usage, 0x1f, 0x23, w, E_Notify, digitTitles1, 0x80) + else output_if_between(usage, 0x2f, 0x47, w, E_Notify, digitTitles2, 0x80) + else E_Notify(pstrUsagePageUndefined, 0x80); +} + +void ReportDescParserBase::PrintAlphanumDisplayPageUsage(uint16_t usage) { + const char * const * w; + E_Notify(pstrSpace, 0x80); + + output_if_between(usage, 0x00, 0x03, w, E_Notify, aplphanumTitles0, 0x80) + else output_if_between(usage, 0x1f, 0x4e, w, E_Notify, aplphanumTitles1, 0x80) + else output_if_between(usage, 0x7f, 0x96, w, E_Notify, digitTitles2, 0x80) + else E_Notify(pstrUsagePageUndefined, 0x80); +} + +void ReportDescParserBase::PrintMedicalInstrumentPageUsage(uint16_t usage) { + const char * const * w; + E_Notify(pstrSpace, 0x80); + + if(usage == 1) E_Notify(pstrUsageMedicalUltrasound, 0x80); + else if(usage == 0x70) + E_Notify(pstrUsageDepthGainCompensation, 0x80); + else output_if_between(usage, 0x1f, 0x28, w, E_Notify, medInstrTitles0, 0x80) + else output_if_between(usage, 0x3f, 0x45, w, E_Notify, medInstrTitles1, 0x80) + else output_if_between(usage, 0x5f, 0x62, w, E_Notify, medInstrTitles2, 0x80) + else output_if_between(usage, 0x7f, 0x8a, w, E_Notify, medInstrTitles3, 0x80) + else output_if_between(usage, 0x9f, 0xa2, w, E_Notify, medInstrTitles4, 0x80) + else E_Notify(pstrUsagePageUndefined, 0x80); +} + +uint8_t ReportDescParser2::ParseItem(uint8_t **pp, uint16_t *pcntdn) { + //uint8_t ret = enErrorSuccess; + + switch(itemParseState) { + case 0: + if(**pp == HID_LONG_ITEM_PREFIX) + USBTRACE("\r\nLONG\r\n"); + else { + uint8_t size = ((**pp) & DATA_SIZE_MASK); + itemPrefix = (**pp); + itemSize = 1 + ((size == DATA_SIZE_4) ? 4 : size); + } + (*pp)++; + (*pcntdn)--; + itemSize--; + itemParseState = 1; + + if(!itemSize) + break; + + if(!pcntdn) + return enErrorIncomplete; + case 1: + theBuffer.valueSize = itemSize; + valParser.Initialize(&theBuffer); + itemParseState = 2; + case 2: + if(!valParser.Parse(pp, pcntdn)) + return enErrorIncomplete; + itemParseState = 3; + case 3: + { + uint8_t data = *((uint8_t*)varBuffer); + + switch(itemPrefix & (TYPE_MASK | TAG_MASK)) { + case (TYPE_LOCAL | TAG_LOCAL_USAGE): + if(pfUsage) { + if(theBuffer.valueSize > 1) { + uint16_t* ui16 = reinterpret_cast(varBuffer); + pfUsage(*ui16); + } else + pfUsage(data); + } + break; + case (TYPE_GLOBAL | TAG_GLOBAL_REPORTSIZE): + rptSize = data; + break; + case (TYPE_GLOBAL | TAG_GLOBAL_REPORTCOUNT): + rptCount = data; + break; + case (TYPE_GLOBAL | TAG_GLOBAL_REPORTID): + rptId = data; + break; + case (TYPE_LOCAL | TAG_LOCAL_USAGEMIN): + useMin = data; + break; + case (TYPE_LOCAL | TAG_LOCAL_USAGEMAX): + useMax = data; + break; + case (TYPE_GLOBAL | TAG_GLOBAL_USAGEPAGE): + SetUsagePage(data); + break; + case (TYPE_MAIN | TAG_MAIN_OUTPUT): + case (TYPE_MAIN | TAG_MAIN_FEATURE): + rptSize = 0; + rptCount = 0; + useMin = 0; + useMax = 0; + break; + case (TYPE_MAIN | TAG_MAIN_INPUT): + OnInputItem(data); + + totalSize += (uint16_t)rptSize * (uint16_t)rptCount; + + rptSize = 0; + rptCount = 0; + useMin = 0; + useMax = 0; + break; + } // switch (**pp & (TYPE_MASK | TAG_MASK)) + } + } // switch (itemParseState) + itemParseState = 0; + return enErrorSuccess; +} + +void ReportDescParser2::OnInputItem(uint8_t itm) { + uint8_t byte_offset = (totalSize >> 3); // calculate offset to the next unhandled byte i = (int)(totalCount / 8); + uint32_t tmp = (byte_offset << 3); + uint8_t bit_offset = totalSize - tmp; // number of bits in the current byte already handled + uint8_t *p = pBuf + byte_offset; // current byte pointer + + if(bit_offset) + *p >>= bit_offset; + + uint8_t usage = useMin; + + bool print_usemin_usemax = ((useMin < useMax) && ((itm & 3) == 2) && pfUsage) ? true : false; + + uint8_t bits_of_byte = 8; + + // for each field in field array defined by rptCount + for(uint8_t field = 0; field < rptCount; field++, usage++) { + + union { + uint8_t bResult[4]; + uint16_t wResult[2]; + uint32_t dwResult; + } result; + + result.dwResult = 0; + uint8_t mask = 0; + + if(print_usemin_usemax) + pfUsage(usage); + + // bits_left - number of bits in the field(array of fields, depending on Report Count) left to process + // bits_of_byte - number of bits in current byte left to process + // bits_to_copy - number of bits to copy to result buffer + + // for each bit in a field + for(uint8_t bits_left = rptSize, bits_to_copy = 0; bits_left; + bits_left -= bits_to_copy) { + bits_to_copy = (bits_left > bits_of_byte) ? bits_of_byte : bits_left; + + result.dwResult <<= bits_to_copy; // Result buffer is shifted by the number of bits to be copied into it + + uint8_t val = *p; + + val >>= (8 - bits_of_byte); // Shift by the number of bits already processed + + mask = 0; + + for(uint8_t j = bits_to_copy; j; j--) { + mask <<= 1; + mask |= 1; + } + + result.bResult[0] = (result.bResult[0] | (val & mask)); + + bits_of_byte -= bits_to_copy; + + if(bits_of_byte < 1) { + bits_of_byte = 8; + p++; + } + } + PrintByteValue(result.dwResult); + } + E_Notify(PSTR("\r\n"), 0x80); +} + +void UniversalReportParser::Parse(HID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf) { + ReportDescParser2 prs(len, buf); + + uint8_t ret = hid->GetReportDescr(0, &prs); + + if(ret) + ErrorMessage (PSTR("GetReportDescr-2"), ret); +} diff --git a/libraries/USB_Host_Shield/hidescriptorparser.h b/libraries/USB_Host_Shield/hidescriptorparser.h new file mode 100755 index 0000000..f3b496f --- /dev/null +++ b/libraries/USB_Host_Shield/hidescriptorparser.h @@ -0,0 +1,176 @@ +/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved. + +This software may be distributed and modified under the terms of the GNU +General Public License version 2 (GPL2) as published by the Free Software +Foundation and appearing in the file GPL2.TXT included in the packaging of +this file. Please note that GPL2 Section 2[b] requires that all works based +on this software must also be made publicly available under the terms of +the GPL2 ("Copyleft"). + +Contact information +------------------- + +Circuits At Home, LTD +Web : http://www.circuitsathome.com +e-mail : support@circuitsathome.com + */ +#if !defined(__HIDDESCRIPTORPARSER_H__) +#define __HIDDESCRIPTORPARSER_H__ + +#include "hid.h" + +class ReportDescParserBase : public USBReadParser { +public: + typedef void (*UsagePageFunc)(uint16_t usage); + + static void PrintGenericDesktopPageUsage(uint16_t usage); + static void PrintSimulationControlsPageUsage(uint16_t usage); + static void PrintVRControlsPageUsage(uint16_t usage); + static void PrintSportsControlsPageUsage(uint16_t usage); + static void PrintGameControlsPageUsage(uint16_t usage); + static void PrintGenericDeviceControlsPageUsage(uint16_t usage); + static void PrintLEDPageUsage(uint16_t usage); + static void PrintButtonPageUsage(uint16_t usage); + static void PrintOrdinalPageUsage(uint16_t usage); + static void PrintTelephonyPageUsage(uint16_t usage); + static void PrintConsumerPageUsage(uint16_t usage); + static void PrintDigitizerPageUsage(uint16_t usage); + static void PrintAlphanumDisplayPageUsage(uint16_t usage); + static void PrintMedicalInstrumentPageUsage(uint16_t usage); + + static void PrintValue(uint8_t *p, uint8_t len); + static void PrintByteValue(uint8_t data); + + static void PrintItemTitle(uint8_t prefix); + + static const char * const usagePageTitles0[]; + static const char * const usagePageTitles1[]; + static const char * const genDesktopTitles0[]; + static const char * const genDesktopTitles1[]; + static const char * const genDesktopTitles2[]; + static const char * const genDesktopTitles3[]; + static const char * const genDesktopTitles4[]; + static const char * const simuTitles0[]; + static const char * const simuTitles1[]; + static const char * const simuTitles2[]; + static const char * const vrTitles0[]; + static const char * const vrTitles1[]; + static const char * const sportsCtrlTitles0[]; + static const char * const sportsCtrlTitles1[]; + static const char * const sportsCtrlTitles2[]; + static const char * const gameTitles0[]; + static const char * const gameTitles1[]; + static const char * const genDevCtrlTitles[]; + static const char * const ledTitles[]; + static const char * const telTitles0[]; + static const char * const telTitles1[]; + static const char * const telTitles2[]; + static const char * const telTitles3[]; + static const char * const telTitles4[]; + static const char * const telTitles5[]; + static const char * const consTitles0[]; + static const char * const consTitles1[]; + static const char * const consTitles2[]; + static const char * const consTitles3[]; + static const char * const consTitles4[]; + static const char * const consTitles5[]; + static const char * const consTitles6[]; + static const char * const consTitles7[]; + static const char * const consTitles8[]; + static const char * const consTitles9[]; + static const char * const consTitlesA[]; + static const char * const consTitlesB[]; + static const char * const consTitlesC[]; + static const char * const consTitlesD[]; + static const char * const consTitlesE[]; + static const char * const digitTitles0[]; + static const char * const digitTitles1[]; + static const char * const digitTitles2[]; + static const char * const aplphanumTitles0[]; + static const char * const aplphanumTitles1[]; + static const char * const aplphanumTitles2[]; + static const char * const medInstrTitles0[]; + static const char * const medInstrTitles1[]; + static const char * const medInstrTitles2[]; + static const char * const medInstrTitles3[]; + static const char * const medInstrTitles4[]; + +protected: + static UsagePageFunc usagePageFunctions[]; + + MultiValueBuffer theBuffer; + MultiByteValueParser valParser; + ByteSkipper theSkipper; + uint8_t varBuffer[sizeof (USB_CONFIGURATION_DESCRIPTOR)]; + + uint8_t itemParseState; // Item parser state variable + uint8_t itemSize; // Item size + uint8_t itemPrefix; // Item prefix (first byte) + uint8_t rptSize; // Report Size + uint8_t rptCount; // Report Count + + uint16_t totalSize; // Report size in bits + + // Method should be defined here if virtual. + virtual uint8_t ParseItem(uint8_t **pp, uint16_t *pcntdn); + + UsagePageFunc pfUsage; + + static void PrintUsagePage(uint16_t page); + void SetUsagePage(uint16_t page); + +public: + + ReportDescParserBase() : + itemParseState(0), + itemSize(0), + itemPrefix(0), + rptSize(0), + rptCount(0), + pfUsage(NULL) { + theBuffer.pValue = varBuffer; + valParser.Initialize(&theBuffer); + theSkipper.Initialize(&theBuffer); + }; + + void Parse(const uint16_t len, const uint8_t *pbuf, const uint16_t &offset); + + enum { + enErrorSuccess = 0 + , enErrorIncomplete // value or record is partialy read in buffer + , enErrorBufferTooSmall + }; +}; + +class ReportDescParser : public ReportDescParserBase { +}; + +class ReportDescParser2 : public ReportDescParserBase { + uint8_t rptId; // Report ID + uint8_t useMin; // Usage Minimum + uint8_t useMax; // Usage Maximum + uint8_t fieldCount; // Number of field being currently processed + + void OnInputItem(uint8_t itm); // Method which is called every time Input item is found + + uint8_t *pBuf; // Report buffer pointer + uint8_t bLen; // Report length + +protected: + // Method should be defined here if virtual. + virtual uint8_t ParseItem(uint8_t **pp, uint16_t *pcntdn); + +public: + + ReportDescParser2(uint16_t len, uint8_t *pbuf) : + ReportDescParserBase(), rptId(0), useMin(0), useMax(0), fieldCount(0), pBuf(pbuf), bLen(len) { + }; +}; + +class UniversalReportParser : public HIDReportParser { +public: + // Method should be defined here if virtual. + virtual void Parse(HID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf); +}; + +#endif // __HIDDESCRIPTORPARSER_H__ diff --git a/libraries/USB_Host_Shield/hiduniversal.cpp b/libraries/USB_Host_Shield/hiduniversal.cpp new file mode 100755 index 0000000..395aa69 --- /dev/null +++ b/libraries/USB_Host_Shield/hiduniversal.cpp @@ -0,0 +1,425 @@ +/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved. + +This software may be distributed and modified under the terms of the GNU +General Public License version 2 (GPL2) as published by the Free Software +Foundation and appearing in the file GPL2.TXT included in the packaging of +this file. Please note that GPL2 Section 2[b] requires that all works based +on this software must also be made publicly available under the terms of +the GPL2 ("Copyleft"). + +Contact information +------------------- + +Circuits At Home, LTD +Web : http://www.circuitsathome.com +e-mail : support@circuitsathome.com + */ + +#include "hiduniversal.h" + +HIDUniversal::HIDUniversal(USB *p) : +HID(p), +qNextPollTime(0), +pollInterval(0), +bPollEnable(false), +bHasReportId(false) { + Initialize(); + + if(pUsb) + pUsb->RegisterDeviceClass(this); +} + +uint16_t HIDUniversal::GetHidClassDescrLen(uint8_t type, uint8_t num) { + for(uint8_t i = 0, n = 0; i < HID_MAX_HID_CLASS_DESCRIPTORS; i++) { + if(descrInfo[i].bDescrType == type) { + if(n == num) + return descrInfo[i].wDescriptorLength; + n++; + } + } + return 0; +} + +void HIDUniversal::Initialize() { + for(uint8_t i = 0; i < MAX_REPORT_PARSERS; i++) { + rptParsers[i].rptId = 0; + rptParsers[i].rptParser = NULL; + } + for(uint8_t i = 0; i < HID_MAX_HID_CLASS_DESCRIPTORS; i++) { + descrInfo[i].bDescrType = 0; + descrInfo[i].wDescriptorLength = 0; + } + for(uint8_t i = 0; i < maxHidInterfaces; i++) { + hidInterfaces[i].bmInterface = 0; + hidInterfaces[i].bmProtocol = 0; + + for(uint8_t j = 0; j < maxEpPerInterface; j++) + hidInterfaces[i].epIndex[j] = 0; + } + for(uint8_t i = 0; i < totalEndpoints; i++) { + epInfo[i].epAddr = 0; + epInfo[i].maxPktSize = (i) ? 0 : 8; + epInfo[i].epAttribs = 0; + epInfo[i].bmNakPower = (i) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER; + } + bNumEP = 1; + bNumIface = 0; + bConfNum = 0; + pollInterval = 0; + + ZeroMemory(constBuffLen, prevBuf); +} + +bool HIDUniversal::SetReportParser(uint8_t id, HIDReportParser *prs) { + for(uint8_t i = 0; i < MAX_REPORT_PARSERS; i++) { + if(rptParsers[i].rptId == 0 && rptParsers[i].rptParser == NULL) { + rptParsers[i].rptId = id; + rptParsers[i].rptParser = prs; + return true; + } + } + return false; +} + +HIDReportParser* HIDUniversal::GetReportParser(uint8_t id) { + if(!bHasReportId) + return ((rptParsers[0].rptParser) ? rptParsers[0].rptParser : NULL); + + for(uint8_t i = 0; i < MAX_REPORT_PARSERS; i++) { + if(rptParsers[i].rptId == id) + return rptParsers[i].rptParser; + } + return NULL; +} + +uint8_t HIDUniversal::Init(uint8_t parent, uint8_t port, bool lowspeed) { + const uint8_t constBufSize = sizeof (USB_DEVICE_DESCRIPTOR); + + uint8_t buf[constBufSize]; + USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast(buf); + uint8_t rcode; + UsbDevice *p = NULL; + EpInfo *oldep_ptr = NULL; + uint8_t len = 0; + + uint8_t num_of_conf; // number of configurations + //uint8_t num_of_intf; // number of interfaces + + AddressPool &addrPool = pUsb->GetAddressPool(); + + USBTRACE("HU Init\r\n"); + + if(bAddress) + return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE; + + // Get pointer to pseudo device with address 0 assigned + p = addrPool.GetUsbDevicePtr(0); + + if(!p) + return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; + + if(!p->epinfo) { + USBTRACE("epinfo\r\n"); + return USB_ERROR_EPINFO_IS_NULL; + } + + // Save old pointer to EP_RECORD of address 0 + oldep_ptr = p->epinfo; + + // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence + p->epinfo = epInfo; + + p->lowspeed = lowspeed; + + // Get device descriptor + rcode = pUsb->getDevDescr(0, 0, 8, (uint8_t*)buf); + + if(!rcode) + len = (buf[0] > constBufSize) ? constBufSize : buf[0]; + + if(rcode) { + // Restore p->epinfo + p->epinfo = oldep_ptr; + + goto FailGetDevDescr; + } + + // Restore p->epinfo + p->epinfo = oldep_ptr; + + // Allocate new address according to device class + bAddress = addrPool.AllocAddress(parent, false, port); + + if(!bAddress) + return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL; + + // Extract Max Packet Size from the device descriptor + epInfo[0].maxPktSize = udd->bMaxPacketSize0; + + // Assign new address to the device + rcode = pUsb->setAddr(0, 0, bAddress); + + if(rcode) { + p->lowspeed = false; + addrPool.FreeAddress(bAddress); + bAddress = 0; + USBTRACE2("setAddr:", rcode); + return rcode; + } + + //delay(2); //per USB 2.0 sect.9.2.6.3 + + USBTRACE2("Addr:", bAddress); + + p->lowspeed = false; + + p = addrPool.GetUsbDevicePtr(bAddress); + + if(!p) + return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; + + p->lowspeed = lowspeed; + + if(len) + rcode = pUsb->getDevDescr(bAddress, 0, len, (uint8_t*)buf); + + if(rcode) + goto FailGetDevDescr; + + VID = udd->idVendor; // Can be used by classes that inherits this class to check the VID and PID of the connected device + PID = udd->idProduct; + + num_of_conf = udd->bNumConfigurations; + + // Assign epInfo to epinfo pointer + rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo); + + if(rcode) + goto FailSetDevTblEntry; + + USBTRACE2("NC:", num_of_conf); + + for(uint8_t i = 0; i < num_of_conf; i++) { + //HexDumper HexDump; + ConfigDescParser confDescrParser(this); + + //rcode = pUsb->getConfDescr(bAddress, 0, i, &HexDump); + rcode = pUsb->getConfDescr(bAddress, 0, i, &confDescrParser); + + if(rcode) + goto FailGetConfDescr; + + if(bNumEP > 1) + break; + } // for + + if(bNumEP < 2) + return USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED; + + // Assign epInfo to epinfo pointer + rcode = pUsb->setEpInfoEntry(bAddress, bNumEP, epInfo); + + USBTRACE2("Cnf:", bConfNum); + + // Set Configuration Value + rcode = pUsb->setConf(bAddress, 0, bConfNum); + + if(rcode) + goto FailSetConfDescr; + + for(uint8_t i = 0; i < bNumIface; i++) { + if(hidInterfaces[i].epIndex[epInterruptInIndex] == 0) + continue; + + rcode = SetIdle(hidInterfaces[i].bmInterface, 0, 0); + + if(rcode && rcode != hrSTALL) + goto FailSetIdle; + } + + USBTRACE("HU configured\r\n"); + + OnInitSuccessful(); + + bPollEnable = true; + return 0; + +FailGetDevDescr: +#ifdef DEBUG_USB_HOST + NotifyFailGetDevDescr(); + goto Fail; +#endif + +FailSetDevTblEntry: +#ifdef DEBUG_USB_HOST + NotifyFailSetDevTblEntry(); + goto Fail; +#endif + +FailGetConfDescr: +#ifdef DEBUG_USB_HOST + NotifyFailGetConfDescr(); + goto Fail; +#endif + +FailSetConfDescr: +#ifdef DEBUG_USB_HOST + NotifyFailSetConfDescr(); + goto Fail; +#endif + + +FailSetIdle: +#ifdef DEBUG_USB_HOST + USBTRACE("SetIdle:"); +#endif + +#ifdef DEBUG_USB_HOST +Fail: + NotifyFail(rcode); +#endif + Release(); + return rcode; +} + +HIDUniversal::HIDInterface* HIDUniversal::FindInterface(uint8_t iface, uint8_t alt, uint8_t proto) { + for(uint8_t i = 0; i < bNumIface && i < maxHidInterfaces; i++) + if(hidInterfaces[i].bmInterface == iface && hidInterfaces[i].bmAltSet == alt + && hidInterfaces[i].bmProtocol == proto) + return hidInterfaces + i; + return NULL; +} + +void HIDUniversal::EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *pep) { + // If the first configuration satisfies, the others are not concidered. + if(bNumEP > 1 && conf != bConfNum) + return; + + //ErrorMessage(PSTR("\r\nConf.Val"), conf); + //ErrorMessage(PSTR("Iface Num"), iface); + //ErrorMessage(PSTR("Alt.Set"), alt); + + bConfNum = conf; + + uint8_t index = 0; + HIDInterface *piface = FindInterface(iface, alt, proto); + + // Fill in interface structure in case of new interface + if(!piface) { + piface = hidInterfaces + bNumIface; + piface->bmInterface = iface; + piface->bmAltSet = alt; + piface->bmProtocol = proto; + bNumIface++; + } + + if((pep->bmAttributes & 0x03) == 3 && (pep->bEndpointAddress & 0x80) == 0x80) + index = epInterruptInIndex; + else + index = epInterruptOutIndex; + + if(index) { + // Fill in the endpoint info structure + epInfo[bNumEP].epAddr = (pep->bEndpointAddress & 0x0F); + epInfo[bNumEP].maxPktSize = (uint8_t)pep->wMaxPacketSize; + epInfo[bNumEP].epAttribs = 0; + epInfo[bNumEP].bmNakPower = USB_NAK_NOWAIT; + + // Fill in the endpoint index list + piface->epIndex[index] = bNumEP; //(pep->bEndpointAddress & 0x0F); + + if(pollInterval < pep->bInterval) // Set the polling interval as the largest polling interval obtained from endpoints + pollInterval = pep->bInterval; + + bNumEP++; + } + //PrintEndpointDescriptor(pep); +} + +uint8_t HIDUniversal::Release() { + pUsb->GetAddressPool().FreeAddress(bAddress); + + bNumEP = 1; + bAddress = 0; + qNextPollTime = 0; + bPollEnable = false; + return 0; +} + +bool HIDUniversal::BuffersIdentical(uint8_t len, uint8_t *buf1, uint8_t *buf2) { + for(uint8_t i = 0; i < len; i++) + if(buf1[i] != buf2[i]) + return false; + return true; +} + +void HIDUniversal::ZeroMemory(uint8_t len, uint8_t *buf) { + for(uint8_t i = 0; i < len; i++) + buf[i] = 0; +} + +void HIDUniversal::SaveBuffer(uint8_t len, uint8_t *src, uint8_t *dest) { + for(uint8_t i = 0; i < len; i++) + dest[i] = src[i]; +} + +uint8_t HIDUniversal::Poll() { + uint8_t rcode = 0; + + if(!bPollEnable) + return 0; + + if((long)(millis() - qNextPollTime) >= 0L) { + qNextPollTime = millis() + pollInterval; + + uint8_t buf[constBuffLen]; + + for(uint8_t i = 0; i < bNumIface; i++) { + uint8_t index = hidInterfaces[i].epIndex[epInterruptInIndex]; + uint16_t read = (uint16_t)epInfo[index].maxPktSize; + + ZeroMemory(constBuffLen, buf); + + uint8_t rcode = pUsb->inTransfer(bAddress, epInfo[index].epAddr, &read, buf); + + if(rcode) { + if(rcode != hrNAK) + USBTRACE3("(hiduniversal.h) Poll:", rcode, 0x81); + return rcode; + } + + if(read > constBuffLen) + read = constBuffLen; + + bool identical = BuffersIdentical(read, buf, prevBuf); + + SaveBuffer(read, buf, prevBuf); + + if(identical) + return 0; +#if 0 + Notify(PSTR("\r\nBuf: "), 0x80); + + for(uint8_t i = 0; i < read; i++) { + D_PrintHex (buf[i], 0x80); + Notify(PSTR(" "), 0x80); + } + + Notify(PSTR("\r\n"), 0x80); +#endif + ParseHIDData(this, bHasReportId, (uint8_t)read, buf); + + HIDReportParser *prs = GetReportParser(((bHasReportId) ? *buf : 0)); + + if(prs) + prs->Parse(this, bHasReportId, (uint8_t)read, buf); + } + } + return rcode; +} + +// Send a report to interrupt out endpoint. This is NOT SetReport() request! +uint8_t HIDUniversal::SndRpt(uint16_t nbytes, uint8_t *dataptr) { + return pUsb->outTransfer(bAddress, epInfo[epInterruptOutIndex].epAddr, nbytes, dataptr); +} \ No newline at end of file diff --git a/libraries/USB_Host_Shield/hiduniversal.h b/libraries/USB_Host_Shield/hiduniversal.h new file mode 100755 index 0000000..d7af384 --- /dev/null +++ b/libraries/USB_Host_Shield/hiduniversal.h @@ -0,0 +1,108 @@ +/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved. + +This software may be distributed and modified under the terms of the GNU +General Public License version 2 (GPL2) as published by the Free Software +Foundation and appearing in the file GPL2.TXT included in the packaging of +this file. Please note that GPL2 Section 2[b] requires that all works based +on this software must also be made publicly available under the terms of +the GPL2 ("Copyleft"). + +Contact information +------------------- + +Circuits At Home, LTD +Web : http://www.circuitsathome.com +e-mail : support@circuitsathome.com + */ + +#if !defined(__HIDUNIVERSAL_H__) +#define __HIDUNIVERSAL_H__ + +#include "hid.h" +//#include "hidescriptorparser.h" + +class HIDUniversal : public HID { + + struct ReportParser { + uint8_t rptId; + HIDReportParser *rptParser; + } rptParsers[MAX_REPORT_PARSERS]; + + // HID class specific descriptor type and length info obtained from HID descriptor + HID_CLASS_DESCRIPTOR_LEN_AND_TYPE descrInfo[HID_MAX_HID_CLASS_DESCRIPTORS]; + + // Returns HID class specific descriptor length by its type and order number + uint16_t GetHidClassDescrLen(uint8_t type, uint8_t num); + + struct HIDInterface { + struct { + uint8_t bmInterface : 3; + uint8_t bmAltSet : 3; + uint8_t bmProtocol : 2; + }; + uint8_t epIndex[maxEpPerInterface]; + }; + + uint8_t bConfNum; // configuration number + uint8_t bNumIface; // number of interfaces in the configuration + uint8_t bNumEP; // total number of EP in the configuration + uint32_t qNextPollTime; // next poll time + uint8_t pollInterval; + bool bPollEnable; // poll enable flag + + static const uint16_t constBuffLen = 64; // event buffer length + uint8_t prevBuf[constBuffLen]; // previous event buffer + + void Initialize(); + HIDInterface* FindInterface(uint8_t iface, uint8_t alt, uint8_t proto); + + void ZeroMemory(uint8_t len, uint8_t *buf); + bool BuffersIdentical(uint8_t len, uint8_t *buf1, uint8_t *buf2); + void SaveBuffer(uint8_t len, uint8_t *src, uint8_t *dest); + +protected: + EpInfo epInfo[totalEndpoints]; + HIDInterface hidInterfaces[maxHidInterfaces]; + + bool bHasReportId; + + uint16_t PID, VID; // PID and VID of connected device + + // HID implementation + HIDReportParser* GetReportParser(uint8_t id); + + virtual uint8_t OnInitSuccessful() { + return 0; + }; + + virtual void ParseHIDData(HID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf) { + return; + }; + +public: + HIDUniversal(USB *p); + + // HID implementation + bool SetReportParser(uint8_t id, HIDReportParser *prs); + + // USBDeviceConfig implementation + uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed); + uint8_t Release(); + uint8_t Poll(); + + virtual uint8_t GetAddress() { + return bAddress; + }; + + virtual bool isReady() { + return bPollEnable; + }; + + // UsbConfigXtracter implementation + void EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *ep); + + // Send report - do not mix with SetReport()! + uint8_t SndRpt(uint16_t nbytes, uint8_t *dataptr); +}; + +#endif // __HIDUNIVERSAL_H__ diff --git a/libraries/USB_Host_Shield/hidusagestr.h b/libraries/USB_Host_Shield/hidusagestr.h new file mode 100755 index 0000000..5ef48f9 --- /dev/null +++ b/libraries/USB_Host_Shield/hidusagestr.h @@ -0,0 +1,977 @@ +/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved. + +This software may be distributed and modified under the terms of the GNU +General Public License version 2 (GPL2) as published by the Free Software +Foundation and appearing in the file GPL2.TXT included in the packaging of +this file. Please note that GPL2 Section 2[b] requires that all works based +on this software must also be made publicly available under the terms of +the GPL2 ("Copyleft"). + +Contact information +------------------- + +Circuits At Home, LTD +Web : http://www.circuitsathome.com +e-mail : support@circuitsathome.com + */ +#if !defined( __HIDUSAGESTR_H__) +#define __HIDUSAGESTR_H__ + +#include "Usb.h" + +const char pstrSpace [] PROGMEM = " "; +const char pstrCRLF [] PROGMEM = "\r\n"; +const char pstrSingleTab [] PROGMEM = "\t"; +const char pstrDoubleTab [] PROGMEM = "\t\t"; +const char pstrTripleTab [] PROGMEM = "\t\t\t"; + +// Usage Page String Titles +const char pstrUsagePageUndefined [] PROGMEM = "Undef"; +const char pstrUsagePageGenericDesktopControls [] PROGMEM = "Gen Desktop Ctrls"; +const char pstrUsagePageSimulationControls [] PROGMEM = "Simu Ctrls"; +const char pstrUsagePageVRControls [] PROGMEM = "VR Ctrls"; +const char pstrUsagePageSportControls [] PROGMEM = "Sport Ctrls"; +const char pstrUsagePageGameControls [] PROGMEM = "Game Ctrls"; +const char pstrUsagePageGenericDeviceControls [] PROGMEM = "Gen Dev Ctrls"; +const char pstrUsagePageKeyboardKeypad [] PROGMEM = "Kbrd/Keypad"; +const char pstrUsagePageLEDs [] PROGMEM = "LEDs"; +const char pstrUsagePageButton [] PROGMEM = "Button"; +const char pstrUsagePageOrdinal [] PROGMEM = "Ordinal"; +const char pstrUsagePageTelephone [] PROGMEM = "Tel"; +const char pstrUsagePageConsumer [] PROGMEM = "Consumer"; +const char pstrUsagePageDigitizer [] PROGMEM = "Digitizer"; +const char pstrUsagePagePID [] PROGMEM = "PID"; +const char pstrUsagePageUnicode [] PROGMEM = "Unicode"; +const char pstrUsagePageAlphaNumericDisplay [] PROGMEM = "Alpha Num Disp"; +const char pstrUsagePageMedicalInstruments [] PROGMEM = "Medical Instr"; +const char pstrUsagePageMonitor [] PROGMEM = "Monitor"; +const char pstrUsagePagePower [] PROGMEM = "Power"; +const char pstrUsagePageBarCodeScanner [] PROGMEM = "Bar Code Scan"; +const char pstrUsagePageScale [] PROGMEM = "Scale"; +const char pstrUsagePageMSRDevices [] PROGMEM = "Magn Stripe Read Dev"; +const char pstrUsagePagePointOfSale [] PROGMEM = "POS"; +const char pstrUsagePageCameraControl [] PROGMEM = "Cam Ctrl"; +const char pstrUsagePageArcade [] PROGMEM = "Arcade"; +const char pstrUsagePageReserved [] PROGMEM = "Reserved"; +const char pstrUsagePageVendorDefined [] PROGMEM = "Vendor Def"; + +// Generic Desktop Controls Page +const char pstrUsagePointer [] PROGMEM = "Pointer"; +const char pstrUsageMouse [] PROGMEM = "Mouse"; +const char pstrUsageJoystick [] PROGMEM = "Joystick"; +const char pstrUsageGamePad [] PROGMEM = "Game Pad"; +const char pstrUsageKeyboard [] PROGMEM = "Kbrd"; +const char pstrUsageKeypad [] PROGMEM = "Keypad"; +const char pstrUsageMultiAxisController [] PROGMEM = "Multi-axis Ctrl"; +const char pstrUsageTabletPCSystemControls [] PROGMEM = "Tablet PC Sys Ctrls"; +const char pstrUsageX [] PROGMEM = "X"; +const char pstrUsageY [] PROGMEM = "Y"; +const char pstrUsageZ [] PROGMEM = "Z"; +const char pstrUsageRx [] PROGMEM = "Rx"; +const char pstrUsageRy [] PROGMEM = "Ry"; +const char pstrUsageRz [] PROGMEM = "Rz"; +const char pstrUsageSlider [] PROGMEM = "Slider"; +const char pstrUsageDial [] PROGMEM = "Dial"; +const char pstrUsageWheel [] PROGMEM = "Wheel"; +const char pstrUsageHatSwitch [] PROGMEM = "Hat Switch"; +const char pstrUsageCountedBuffer [] PROGMEM = "Counted Buf"; +const char pstrUsageByteCount [] PROGMEM = "Byte Count"; +const char pstrUsageMotionWakeup [] PROGMEM = "Motion Wakeup"; +const char pstrUsageStart [] PROGMEM = "Start"; +const char pstrUsageSelect [] PROGMEM = "Sel"; +const char pstrUsageVx [] PROGMEM = "Vx"; +const char pstrUsageVy [] PROGMEM = "Vy"; +const char pstrUsageVz [] PROGMEM = "Vz"; +const char pstrUsageVbrx [] PROGMEM = "Vbrx"; +const char pstrUsageVbry [] PROGMEM = "Vbry"; +const char pstrUsageVbrz [] PROGMEM = "Vbrz"; +const char pstrUsageVno [] PROGMEM = "Vno"; +const char pstrUsageFeatureNotification [] PROGMEM = "Feature Notif"; +const char pstrUsageResolutionMultiplier [] PROGMEM = "Res Mult"; +const char pstrUsageSystemControl [] PROGMEM = "Sys Ctrl"; +const char pstrUsageSystemPowerDown [] PROGMEM = "Sys Pwr Down"; +const char pstrUsageSystemSleep [] PROGMEM = "Sys Sleep"; +const char pstrUsageSystemWakeup [] PROGMEM = "Sys Wakeup"; +const char pstrUsageSystemContextMenu [] PROGMEM = "Sys Context Menu"; +const char pstrUsageSystemMainMenu [] PROGMEM = "Sys Main Menu"; +const char pstrUsageSystemAppMenu [] PROGMEM = "Sys App Menu"; +const char pstrUsageSystemMenuHelp [] PROGMEM = "Sys Menu Help"; +const char pstrUsageSystemMenuExit [] PROGMEM = "Sys Menu Exit"; +const char pstrUsageSystemMenuSelect [] PROGMEM = "Sys Menu Select"; +const char pstrUsageSystemMenuRight [] PROGMEM = "Sys Menu Right"; +const char pstrUsageSystemMenuLeft [] PROGMEM = "Sys Menu Left"; +const char pstrUsageSystemMenuUp [] PROGMEM = "Sys Menu Up"; +const char pstrUsageSystemMenuDown [] PROGMEM = "Sys Menu Down"; +const char pstrUsageSystemColdRestart [] PROGMEM = "Sys Cold Restart"; +const char pstrUsageSystemWarmRestart [] PROGMEM = "Sys Warm Restart"; +const char pstrUsageDPadUp [] PROGMEM = "D-pad Up"; +const char pstrUsageDPadDown [] PROGMEM = "D-pad Down"; +const char pstrUsageDPadRight [] PROGMEM = "D-pad Right"; +const char pstrUsageDPadLeft [] PROGMEM = "D-pad Left"; +const char pstrUsageSystemDock [] PROGMEM = "Sys Dock"; +const char pstrUsageSystemUndock [] PROGMEM = "Sys Undock"; +const char pstrUsageSystemSetup [] PROGMEM = "Sys Setup"; +const char pstrUsageSystemBreak [] PROGMEM = "Sys Break"; +const char pstrUsageSystemDebuggerBreak [] PROGMEM = "Sys Dbg Brk"; +const char pstrUsageApplicationBreak [] PROGMEM = "App Break"; +const char pstrUsageApplicationDebuggerBreak [] PROGMEM = "App Dbg Brk"; +const char pstrUsageSystemSpeakerMute [] PROGMEM = "Sys Spk Mute"; +const char pstrUsageSystemHibernate [] PROGMEM = "Sys Hiber"; +const char pstrUsageSystemDisplayInvert [] PROGMEM = "Sys Disp Inv"; +const char pstrUsageSystemDisplayInternal [] PROGMEM = "Sys Disp Int"; +const char pstrUsageSystemDisplayExternal [] PROGMEM = "Sys Disp Ext"; +const char pstrUsageSystemDisplayBoth [] PROGMEM = "Sys Disp Both"; +const char pstrUsageSystemDisplayDual [] PROGMEM = "Sys Disp Dual"; +const char pstrUsageSystemDisplayToggleIntExt [] PROGMEM = "Sys Disp Tgl Int/Ext"; +const char pstrUsageSystemDisplaySwapPriSec [] PROGMEM = "Sys Disp Swap Pri/Sec"; +const char pstrUsageSystemDisplayLCDAutoscale [] PROGMEM = "Sys Disp LCD Autoscale"; + +// Simulation Controls Page +const char pstrUsageFlightSimulationDevice [] PROGMEM = "Flight Simu Dev"; +const char pstrUsageAutomobileSimulationDevice [] PROGMEM = "Auto Simu Dev"; +const char pstrUsageTankSimulationDevice [] PROGMEM = "Tank Simu Dev"; +const char pstrUsageSpaceshipSimulationDevice [] PROGMEM = "Space Simu Dev"; +const char pstrUsageSubmarineSimulationDevice [] PROGMEM = "Subm Simu Dev"; +const char pstrUsageSailingSimulationDevice [] PROGMEM = "Sail Simu Dev"; +const char pstrUsageMotocicleSimulationDevice [] PROGMEM = "Moto Simu Dev"; +const char pstrUsageSportsSimulationDevice [] PROGMEM = "Sport Simu Dev"; +const char pstrUsageAirplaneSimulationDevice [] PROGMEM = "Airp Simu Dev"; +const char pstrUsageHelicopterSimulationDevice [] PROGMEM = "Heli Simu Dev"; +const char pstrUsageMagicCarpetSimulationDevice [] PROGMEM = "Magic Carpet Simu Dev"; +const char pstrUsageBicycleSimulationDevice [] PROGMEM = "Bike Simu Dev"; +const char pstrUsageFlightControlStick [] PROGMEM = "Flight Ctrl Stick"; +const char pstrUsageFlightStick [] PROGMEM = "Flight Stick"; +const char pstrUsageCyclicControl [] PROGMEM = "Cyclic Ctrl"; +const char pstrUsageCyclicTrim [] PROGMEM = "Cyclic Trim"; +const char pstrUsageFlightYoke [] PROGMEM = "Flight Yoke"; +const char pstrUsageTrackControl [] PROGMEM = "Track Ctrl"; +const char pstrUsageAileron [] PROGMEM = "Aileron"; +const char pstrUsageAileronTrim [] PROGMEM = "Aileron Trim"; +const char pstrUsageAntiTorqueControl [] PROGMEM = "Anti-Torque Ctrl"; +const char pstrUsageAutopilotEnable [] PROGMEM = "Autopilot Enable"; +const char pstrUsageChaffRelease [] PROGMEM = "Chaff Release"; +const char pstrUsageCollectiveControl [] PROGMEM = "Collective Ctrl"; +const char pstrUsageDiveBrake [] PROGMEM = "Dive Brake"; +const char pstrUsageElectronicCountermeasures [] PROGMEM = "El Countermeasures"; +const char pstrUsageElevator [] PROGMEM = "Elevator"; +const char pstrUsageElevatorTrim [] PROGMEM = "Elevator Trim"; +const char pstrUsageRudder [] PROGMEM = "Rudder"; +const char pstrUsageThrottle [] PROGMEM = "Throttle"; +const char pstrUsageFlightCommunications [] PROGMEM = "Flight Comm"; +const char pstrUsageFlareRelease [] PROGMEM = "Flare Release"; +const char pstrUsageLandingGear [] PROGMEM = "Landing Gear"; +const char pstrUsageToeBrake [] PROGMEM = "Toe Brake"; +const char pstrUsageTrigger [] PROGMEM = "Trigger"; +const char pstrUsageWeaponsArm [] PROGMEM = "Weapons Arm"; +const char pstrUsageWeaponsSelect [] PROGMEM = "Weapons Sel"; +const char pstrUsageWingFlaps [] PROGMEM = "Wing Flaps"; +const char pstrUsageAccelerator [] PROGMEM = "Accel"; +const char pstrUsageBrake [] PROGMEM = "Brake"; +const char pstrUsageClutch [] PROGMEM = "Clutch"; +const char pstrUsageShifter [] PROGMEM = "Shifter"; +const char pstrUsageSteering [] PROGMEM = "Steering"; +const char pstrUsageTurretDirection [] PROGMEM = "Turret Dir"; +const char pstrUsageBarrelElevation [] PROGMEM = "Barrel Ele"; +const char pstrUsageDivePlane [] PROGMEM = "Dive Plane"; +const char pstrUsageBallast [] PROGMEM = "Ballast"; +const char pstrUsageBicycleCrank [] PROGMEM = "Bicycle Crank"; +const char pstrUsageHandleBars [] PROGMEM = "Handle Bars"; +const char pstrUsageFrontBrake [] PROGMEM = "Front Brake"; +const char pstrUsageRearBrake [] PROGMEM = "Rear Brake"; + +// VR Controls Page +const char pstrUsageBelt [] PROGMEM = "Belt"; +const char pstrUsageBodySuit [] PROGMEM = "Body Suit"; +const char pstrUsageFlexor [] PROGMEM = "Flexor"; +const char pstrUsageGlove [] PROGMEM = "Glove"; +const char pstrUsageHeadTracker [] PROGMEM = "Head Track"; +const char pstrUsageHeadMountedDisplay [] PROGMEM = "Head Disp"; +const char pstrUsageHandTracker [] PROGMEM = "Hand Track"; +const char pstrUsageOculometer [] PROGMEM = "Oculometer"; +const char pstrUsageVest [] PROGMEM = "Vest"; +const char pstrUsageAnimatronicDevice [] PROGMEM = "Animat Dev"; +const char pstrUsageStereoEnable [] PROGMEM = "Stereo Enbl"; +const char pstrUsageDisplayEnable [] PROGMEM = "Display Enbl"; + +// Sport Controls Page +const char pstrUsageBaseballBat [] PROGMEM = "Baseball Bat"; +const char pstrUsageGolfClub [] PROGMEM = "Golf Club"; +const char pstrUsageRowingMachine [] PROGMEM = "Rowing Mach"; +const char pstrUsageTreadmill [] PROGMEM = "Treadmill"; +const char pstrUsageOar [] PROGMEM = "Oar"; +const char pstrUsageSlope [] PROGMEM = "Slope"; +const char pstrUsageRate [] PROGMEM = "Rate"; +const char pstrUsageStickSpeed [] PROGMEM = "Stick Speed"; +const char pstrUsageStickFaceAngle [] PROGMEM = "Stick Face Ang"; +const char pstrUsageStickHeelToe [] PROGMEM = "Stick Heel/Toe"; +const char pstrUsageStickFollowThough [] PROGMEM = "Stick Flw Thru"; +const char pstrUsageStickTempo [] PROGMEM = "Stick Tempo"; +const char pstrUsageStickType [] PROGMEM = "Stick Type"; +const char pstrUsageStickHeight [] PROGMEM = "Stick Hght"; +const char pstrUsagePutter [] PROGMEM = "Putter"; +const char pstrUsage1Iron [] PROGMEM = "1 Iron"; +const char pstrUsage2Iron [] PROGMEM = "2 Iron"; +const char pstrUsage3Iron [] PROGMEM = "3 Iron"; +const char pstrUsage4Iron [] PROGMEM = "4 Iron"; +const char pstrUsage5Iron [] PROGMEM = "5 Iron"; +const char pstrUsage6Iron [] PROGMEM = "6 Iron"; +const char pstrUsage7Iron [] PROGMEM = "7 Iron"; +const char pstrUsage8Iron [] PROGMEM = "8 Iron"; +const char pstrUsage9Iron [] PROGMEM = "9 Iron"; +const char pstrUsage10Iron [] PROGMEM = "10 Iron"; +const char pstrUsage11Iron [] PROGMEM = "11 Iron"; +const char pstrUsageSandWedge [] PROGMEM = "Sand Wedge"; +const char pstrUsageLoftWedge [] PROGMEM = "Loft Wedge"; +const char pstrUsagePowerWedge [] PROGMEM = "Pwr Wedge"; +const char pstrUsage1Wood [] PROGMEM = "1 Wood"; +const char pstrUsage3Wood [] PROGMEM = "3 Wood"; +const char pstrUsage5Wood [] PROGMEM = "5 Wood"; +const char pstrUsage7Wood [] PROGMEM = "7 Wood"; +const char pstrUsage9Wood [] PROGMEM = "9 Wood"; + +// Game Controls Page +const char pstrUsage3DGameController [] PROGMEM = "3D Game Ctrl"; +const char pstrUsagePinballDevice [] PROGMEM = "Pinball Dev"; +const char pstrUsageGunDevice [] PROGMEM = "Gun Dev"; +const char pstrUsagePointOfView [] PROGMEM = "POV"; +const char pstrUsageTurnRightLeft [] PROGMEM = "Turn Right Left"; +const char pstrUsagePitchForwardBackward [] PROGMEM = "Pitch Fwd/Back"; +const char pstrUsageRollRightLeft [] PROGMEM = "Roll Right/Left"; +const char pstrUsageMoveRightLeft [] PROGMEM = "Move Right/Left"; +const char pstrUsageMoveForwardBackward [] PROGMEM = "Move Fwd/Back"; +const char pstrUsageMoveUpDown [] PROGMEM = "Move Up/Down"; +const char pstrUsageLeanRightLeft [] PROGMEM = "Lean Right/Left"; +const char pstrUsageLeanForwardBackward [] PROGMEM = "Lean Fwd/Back"; +const char pstrUsageHeightOfPOV [] PROGMEM = "Height of POV"; +const char pstrUsageFlipper [] PROGMEM = "Flipper"; +const char pstrUsageSecondaryFlipper [] PROGMEM = "Second Flipper"; +const char pstrUsageBump [] PROGMEM = "Bump"; +const char pstrUsageNewGame [] PROGMEM = "New Game"; +const char pstrUsageShootBall [] PROGMEM = "Shoot Ball"; +const char pstrUsagePlayer [] PROGMEM = "Player"; +const char pstrUsageGunBolt [] PROGMEM = "Gun Bolt"; +const char pstrUsageGunClip [] PROGMEM = "Gun Clip"; +const char pstrUsageGunSelector [] PROGMEM = "Gun Sel"; +const char pstrUsageGunSingleShot [] PROGMEM = "Gun Sngl Shot"; +const char pstrUsageGunBurst [] PROGMEM = "Gun Burst"; +const char pstrUsageGunAutomatic [] PROGMEM = "Gun Auto"; +const char pstrUsageGunSafety [] PROGMEM = "Gun Safety"; +const char pstrUsageGamepadFireJump [] PROGMEM = "Gamepad Fire/Jump"; +const char pstrUsageGamepadTrigger [] PROGMEM = "Gamepad Trig"; + +// Generic Device Controls Page +const char pstrUsageBatteryStrength [] PROGMEM = "Bat Strength"; +const char pstrUsageWirelessChannel [] PROGMEM = "Wireless Ch"; +const char pstrUsageWirelessID [] PROGMEM = "Wireless ID"; +const char pstrUsageDiscoverWirelessControl [] PROGMEM = "Discover Wireless Ctrl"; +const char pstrUsageSecurityCodeCharEntered [] PROGMEM = "Sec Code Char Entrd"; +const char pstrUsageSecurityCodeCharErased [] PROGMEM = "Sec Code Char Erased"; +const char pstrUsageSecurityCodeCleared [] PROGMEM = "Sec Code Cleared"; + +// LED Page +const char pstrUsageNumLock [] PROGMEM = "Num Lock"; +const char pstrUsageCapsLock [] PROGMEM = "Caps Lock"; +const char pstrUsageScrollLock [] PROGMEM = "Scroll Lock"; +const char pstrUsageCompose [] PROGMEM = "Compose"; +const char pstrUsageKana [] PROGMEM = "Kana"; +const char pstrUsagePower [] PROGMEM = "Pwr"; +const char pstrUsageShift [] PROGMEM = "Shift"; +const char pstrUsageDoNotDisturb [] PROGMEM = "DND"; +const char pstrUsageMute [] PROGMEM = "Mute"; +const char pstrUsageToneEnable [] PROGMEM = "Tone Enbl"; +const char pstrUsageHighCutFilter [] PROGMEM = "High Cut Fltr"; +const char pstrUsageLowCutFilter [] PROGMEM = "Low Cut Fltr"; +const char pstrUsageEqualizerEnable [] PROGMEM = "Eq Enbl"; +const char pstrUsageSoundFieldOn [] PROGMEM = "Sound Field On"; +const char pstrUsageSurroundOn [] PROGMEM = "Surround On"; +const char pstrUsageRepeat [] PROGMEM = "Repeat"; +const char pstrUsageStereo [] PROGMEM = "Stereo"; +const char pstrUsageSamplingRateDetect [] PROGMEM = "Smpl Rate Detect"; +const char pstrUsageSpinning [] PROGMEM = "Spinning"; +const char pstrUsageCAV [] PROGMEM = "CAV"; +const char pstrUsageCLV [] PROGMEM = "CLV"; +const char pstrUsageRecordingFormatDetect [] PROGMEM = "Rec Format Detect"; +const char pstrUsageOffHook [] PROGMEM = "Off Hook"; +const char pstrUsageRing [] PROGMEM = "Ring"; +const char pstrUsageMessageWaiting [] PROGMEM = "Msg Wait"; +const char pstrUsageDataMode [] PROGMEM = "Data Mode"; +const char pstrUsageBatteryOperation [] PROGMEM = "Bat Op"; +const char pstrUsageBatteryOK [] PROGMEM = "Bat OK"; +const char pstrUsageBatteryLow [] PROGMEM = "Bat Low"; +const char pstrUsageSpeaker [] PROGMEM = "Speaker"; +const char pstrUsageHeadSet [] PROGMEM = "Head Set"; +const char pstrUsageHold [] PROGMEM = "Hold"; +const char pstrUsageMicrophone [] PROGMEM = "Mic"; +const char pstrUsageCoverage [] PROGMEM = "Coverage"; +const char pstrUsageNightMode [] PROGMEM = "Night Mode"; +const char pstrUsageSendCalls [] PROGMEM = "Send Calls"; +const char pstrUsageCallPickup [] PROGMEM = "Call Pickup"; +const char pstrUsageConference [] PROGMEM = "Conf"; +const char pstrUsageStandBy [] PROGMEM = "Stand-by"; +const char pstrUsageCameraOn [] PROGMEM = "Cam On"; +const char pstrUsageCameraOff [] PROGMEM = "Cam Off"; +const char pstrUsageOnLine [] PROGMEM = "On-Line"; +const char pstrUsageOffLine [] PROGMEM = "Off-Line"; +const char pstrUsageBusy [] PROGMEM = "Busy"; +const char pstrUsageReady [] PROGMEM = "Ready"; +const char pstrUsagePaperOut [] PROGMEM = "Paper Out"; +const char pstrUsagePaperJam [] PROGMEM = "Paper Jam"; +const char pstrUsageRemote [] PROGMEM = "Remote"; +const char pstrUsageForward [] PROGMEM = "Fwd"; +const char pstrUsageReverse [] PROGMEM = "Rev"; +const char pstrUsageStop [] PROGMEM = "Stop"; +const char pstrUsageRewind [] PROGMEM = "Rewind"; +const char pstrUsageFastForward [] PROGMEM = "Fast Fwd"; +const char pstrUsagePlay [] PROGMEM = "Play"; +const char pstrUsagePause [] PROGMEM = "Pause"; +const char pstrUsageRecord [] PROGMEM = "Rec"; +const char pstrUsageError [] PROGMEM = "Error"; +const char pstrUsageSelectedIndicator [] PROGMEM = "Usage Sel Ind"; +const char pstrUsageInUseIndicator [] PROGMEM = "Usage In Use Ind"; +const char pstrUsageMultiModeIndicator [] PROGMEM = "Usage Multi Mode Ind"; +const char pstrUsageIndicatorOn [] PROGMEM = "Ind On"; +const char pstrUsageIndicatorFlash [] PROGMEM = "Ind Flash"; +const char pstrUsageIndicatorSlowBlink [] PROGMEM = "Ind Slow Blk"; +const char pstrUsageIndicatorFastBlink [] PROGMEM = "Ind Fast Blk"; +const char pstrUsageIndicatorOff [] PROGMEM = "Ind Off"; +const char pstrUsageFlashOnTime [] PROGMEM = "Flash On Time"; +const char pstrUsageSlowBlinkOnTime [] PROGMEM = "Slow Blk On Time"; +const char pstrUsageSlowBlinkOffTime [] PROGMEM = "Slow Blk Off Time"; +const char pstrUsageFastBlinkOnTime [] PROGMEM = "Fast Blk On Time"; +const char pstrUsageFastBlinkOffTime [] PROGMEM = "Fast Blk Off Time"; +const char pstrUsageIndicatorColor [] PROGMEM = "Usage Ind Color"; +const char pstrUsageIndicatorRed [] PROGMEM = "Ind Red"; +const char pstrUsageIndicatorGreen [] PROGMEM = "Ind Green"; +const char pstrUsageIndicatorAmber [] PROGMEM = "Ind Amber"; +const char pstrUsageGenericIndicator [] PROGMEM = "Gen Ind"; +const char pstrUsageSystemSuspend [] PROGMEM = "Sys Suspend"; +const char pstrUsageExternalPowerConnected [] PROGMEM = "Ext Pwr Conn"; + +// Telephony Usage Page +const char pstrUsagePhone [] PROGMEM = "Phone"; +const char pstrUsageAnsweringMachine [] PROGMEM = "Answ Mach"; +const char pstrUsageMessageControls [] PROGMEM = "Msg Ctrls"; +const char pstrUsageHandset [] PROGMEM = "Handset"; +const char pstrUsageHeadset [] PROGMEM = "Headset"; +const char pstrUsageTelephonyKeyPad [] PROGMEM = "Tel Key Pad"; +const char pstrUsageProgrammableButton [] PROGMEM = "Prog Button"; +const char pstrUsageHookSwitch [] PROGMEM = "Hook Sw"; +const char pstrUsageFlash [] PROGMEM = "Flash"; +const char pstrUsageFeature [] PROGMEM = "Feature"; +//const char pstrUsageHold [] PROGMEM = "Hold"; +const char pstrUsageRedial [] PROGMEM = "Redial"; +const char pstrUsageTransfer [] PROGMEM = "Transfer"; +const char pstrUsageDrop [] PROGMEM = "Drop"; +const char pstrUsagePark [] PROGMEM = "Park"; +const char pstrUsageForwardCalls [] PROGMEM = "Fwd Calls"; +const char pstrUsageAlternateFunction [] PROGMEM = "Alt Func"; +const char pstrUsageLine [] PROGMEM = "Line"; +const char pstrUsageSpeakerPhone [] PROGMEM = "Spk Phone"; +//const char pstrUsageConference [] PROGMEM = "Conference"; +const char pstrUsageRingEnable [] PROGMEM = "Ring Enbl"; +const char pstrUsageRingSelect [] PROGMEM = "Ring Sel"; +const char pstrUsagePhoneMute [] PROGMEM = "Phone Mute"; +const char pstrUsageCallerID [] PROGMEM = "Caller ID"; +const char pstrUsageSend [] PROGMEM = "Send"; +const char pstrUsageSpeedDial [] PROGMEM = "Speed Dial"; +const char pstrUsageStoreNumber [] PROGMEM = "Store Num"; +const char pstrUsageRecallNumber [] PROGMEM = "Recall Num"; +const char pstrUsagePhoneDirectory [] PROGMEM = "Phone Dir"; +const char pstrUsageVoiceMail [] PROGMEM = "Voice Mail"; +const char pstrUsageScreenCalls [] PROGMEM = "Screen Calls"; +//const char pstrUsageDoNotDisturb [] PROGMEM = "Do Not Disturb"; +const char pstrUsageMessage [] PROGMEM = "Msg"; +const char pstrUsageAnswerOnOff [] PROGMEM = "Answer On/Off"; +const char pstrUsageInsideDialTone [] PROGMEM = "Inside Dial Tone"; +const char pstrUsageOutsideDialTone [] PROGMEM = "Outside Dial Tone"; +const char pstrUsageInsideRingTone [] PROGMEM = "Inside Ring Tone"; +const char pstrUsageOutsideRingTone [] PROGMEM = "Outside Ring Tone"; +const char pstrUsagePriorityRingTone [] PROGMEM = "Prior Ring Tone"; +const char pstrUsageInsideRingback [] PROGMEM = "Inside Ringback"; +const char pstrUsagePriorityRingback [] PROGMEM = "Priority Ringback"; +const char pstrUsageLineBusyTone [] PROGMEM = "Ln Busy Tone"; +const char pstrUsageReorderTone [] PROGMEM = "Reorder Tone"; +const char pstrUsageCallWaitingTone [] PROGMEM = "Call Wait Tone"; +const char pstrUsageConfirmationTone1 [] PROGMEM = "Cnfrm Tone1"; +const char pstrUsageConfirmationTone2 [] PROGMEM = "Cnfrm Tone2"; +const char pstrUsageTonesOff [] PROGMEM = "Tones Off"; +const char pstrUsageOutsideRingback [] PROGMEM = "Outside Ringback"; +const char pstrUsageRinger [] PROGMEM = "Ringer"; +const char pstrUsagePhoneKey0 [] PROGMEM = "0"; +const char pstrUsagePhoneKey1 [] PROGMEM = "1"; +const char pstrUsagePhoneKey2 [] PROGMEM = "2"; +const char pstrUsagePhoneKey3 [] PROGMEM = "3"; +const char pstrUsagePhoneKey4 [] PROGMEM = "4"; +const char pstrUsagePhoneKey5 [] PROGMEM = "5"; +const char pstrUsagePhoneKey6 [] PROGMEM = "6"; +const char pstrUsagePhoneKey7 [] PROGMEM = "7"; +const char pstrUsagePhoneKey8 [] PROGMEM = "8"; +const char pstrUsagePhoneKey9 [] PROGMEM = "9"; +const char pstrUsagePhoneKeyStar [] PROGMEM = "*"; +const char pstrUsagePhoneKeyPound [] PROGMEM = "#"; +const char pstrUsagePhoneKeyA [] PROGMEM = "A"; +const char pstrUsagePhoneKeyB [] PROGMEM = "B"; +const char pstrUsagePhoneKeyC [] PROGMEM = "C"; +const char pstrUsagePhoneKeyD [] PROGMEM = "D"; + +// Consumer Usage Page +const char pstrUsageConsumerControl [] PROGMEM = "Consumer Ctrl"; +const char pstrUsageNumericKeyPad [] PROGMEM = "Num Key Pad"; +//const char pstrUsageProgrammableButton [] PROGMEM = "Prog Btn"; +//const char pstrUsageMicrophone [] PROGMEM = "Mic"; +const char pstrUsageHeadphone [] PROGMEM = "Headphone"; +const char pstrUsageGraphicEqualizer [] PROGMEM = "Graph Eq"; +const char pstrUsagePlus10 [] PROGMEM = "+10"; +const char pstrUsagePlus100 [] PROGMEM = "+100"; +const char pstrUsageAMPM [] PROGMEM = "AM/PM"; +//const char pstrUsagePower [] PROGMEM = "Pwr"; +const char pstrUsageReset [] PROGMEM = "Reset"; +const char pstrUsageSleep [] PROGMEM = "Sleep"; +const char pstrUsageSleepAfter [] PROGMEM = "Sleep After"; +const char pstrUsageSleepMode [] PROGMEM = "Sleep Mode"; +const char pstrUsageIllumination [] PROGMEM = "Illumin"; +const char pstrUsageFunctionButtons [] PROGMEM = "Func Btns"; +const char pstrUsageMenu [] PROGMEM = "Menu"; +const char pstrUsageMenuPick [] PROGMEM = "Menu Pick"; +const char pstrUsageMenuUp [] PROGMEM = "Menu Up"; +const char pstrUsageMenuDown [] PROGMEM = "Menu Down"; +const char pstrUsageMenuLeft [] PROGMEM = "Menu Left"; +const char pstrUsageMenuRight [] PROGMEM = "Menu Right"; +const char pstrUsageMenuEscape [] PROGMEM = "Menu Esc"; +const char pstrUsageMenuValueIncrease [] PROGMEM = "Menu Val Inc"; +const char pstrUsageMenuValueDecrease [] PROGMEM = "Menu Val Dec"; +const char pstrUsageDataOnScreen [] PROGMEM = "Data On Scr"; +const char pstrUsageClosedCaption [] PROGMEM = "Closed Cptn"; +const char pstrUsageClosedCaptionSelect [] PROGMEM = "Closed Cptn Sel"; +const char pstrUsageVCRTV [] PROGMEM = "VCR/TV"; +const char pstrUsageBroadcastMode [] PROGMEM = "Brdcast Mode"; +const char pstrUsageSnapshot [] PROGMEM = "Snapshot"; +const char pstrUsageStill [] PROGMEM = "Still"; +const char pstrUsageSelection [] PROGMEM = "Sel"; +const char pstrUsageAssignSelection [] PROGMEM = "Assign Sel"; +const char pstrUsageModeStep [] PROGMEM = "Mode Step"; +const char pstrUsageRecallLast [] PROGMEM = "Recall Last"; +const char pstrUsageEnterChannel [] PROGMEM = "Entr Channel"; +const char pstrUsageOrderMovie [] PROGMEM = "Ord Movie"; +const char pstrUsageChannel [] PROGMEM = "Channel"; +const char pstrUsageMediaSelection [] PROGMEM = "Med Sel"; +const char pstrUsageMediaSelectComputer [] PROGMEM = "Med Sel Comp"; +const char pstrUsageMediaSelectTV [] PROGMEM = "Med Sel TV"; +const char pstrUsageMediaSelectWWW [] PROGMEM = "Med Sel WWW"; +const char pstrUsageMediaSelectDVD [] PROGMEM = "Med Sel DVD"; +const char pstrUsageMediaSelectTelephone [] PROGMEM = "Med Sel Tel"; +const char pstrUsageMediaSelectProgramGuide [] PROGMEM = "Med Sel PG"; +const char pstrUsageMediaSelectVideoPhone [] PROGMEM = "Med Sel Vid"; +const char pstrUsageMediaSelectGames [] PROGMEM = "Med Sel Games"; +const char pstrUsageMediaSelectMessages [] PROGMEM = "Med Sel Msg"; +const char pstrUsageMediaSelectCD [] PROGMEM = "Med Sel CD"; +const char pstrUsageMediaSelectVCR [] PROGMEM = "Med Sel VCR"; +const char pstrUsageMediaSelectTuner [] PROGMEM = "Med Sel Tuner"; +const char pstrUsageQuit [] PROGMEM = "Quit"; +const char pstrUsageHelp [] PROGMEM = "Help"; +const char pstrUsageMediaSelectTape [] PROGMEM = "Med Sel Tape"; +const char pstrUsageMediaSelectCable [] PROGMEM = "Med Sel Cbl"; +const char pstrUsageMediaSelectSatellite [] PROGMEM = "Med Sel Sat"; +const char pstrUsageMediaSelectSecurity [] PROGMEM = "Med Sel Secur"; +const char pstrUsageMediaSelectHome [] PROGMEM = "Med Sel Home"; +const char pstrUsageMediaSelectCall [] PROGMEM = "Med Sel Call"; +const char pstrUsageChannelIncrement [] PROGMEM = "Ch Inc"; +const char pstrUsageChannelDecrement [] PROGMEM = "Ch Dec"; +const char pstrUsageMediaSelectSAP [] PROGMEM = "Med Sel SAP"; +const char pstrUsageVCRPlus [] PROGMEM = "VCR+"; +const char pstrUsageOnce [] PROGMEM = "Once"; +const char pstrUsageDaily [] PROGMEM = "Daily"; +const char pstrUsageWeekly [] PROGMEM = "Weekly"; +const char pstrUsageMonthly [] PROGMEM = "Monthly"; +//const char pstrUsagePlay [] PROGMEM = "Play"; +//const char pstrUsagePause [] PROGMEM = "Pause"; +//const char pstrUsageRecord [] PROGMEM = "Rec"; +//const char pstrUsageFastForward [] PROGMEM = "FF"; +//const char pstrUsageRewind [] PROGMEM = "Rewind"; +const char pstrUsageScanNextTrack [] PROGMEM = "Next Track"; +const char pstrUsageScanPreviousTrack [] PROGMEM = "Prev Track"; +//const char pstrUsageStop [] PROGMEM = "Stop"; +const char pstrUsageEject [] PROGMEM = "Eject"; +const char pstrUsageRandomPlay [] PROGMEM = "Random"; +const char pstrUsageSelectDisk [] PROGMEM = "Sel Disk"; +const char pstrUsageEnterDisk [] PROGMEM = "Ent Disk"; +//const char pstrUsageRepeat [] PROGMEM = "Repeat"; +const char pstrUsageTracking [] PROGMEM = "Tracking"; +const char pstrUsageTrackNormal [] PROGMEM = "Trk Norm"; +const char pstrUsageSlowTracking [] PROGMEM = "Slow Trk"; +const char pstrUsageFrameForward [] PROGMEM = "Frm Fwd"; +const char pstrUsageFrameBackwards [] PROGMEM = "Frm Back"; +const char pstrUsageMark [] PROGMEM = "Mark"; +const char pstrUsageClearMark [] PROGMEM = "Clr Mark"; +const char pstrUsageRepeatFromMark [] PROGMEM = "Rpt Mark"; +const char pstrUsageReturnToMark [] PROGMEM = "Ret to Mark"; +const char pstrUsageSearchMarkForward [] PROGMEM = "Search Mark Fwd"; +const char pstrUsageSearchMarkBackwards [] PROGMEM = "Search Mark Back"; +const char pstrUsageCounterReset [] PROGMEM = "Counter Reset"; +const char pstrUsageShowCounter [] PROGMEM = "Show Counter"; +const char pstrUsageTrackingIncrement [] PROGMEM = "Track Inc"; +const char pstrUsageTrackingDecrement [] PROGMEM = "Track Dec"; +const char pstrUsageStopEject [] PROGMEM = "Stop/Eject"; +const char pstrUsagePlayPause [] PROGMEM = "Play/Pause"; +const char pstrUsagePlaySkip [] PROGMEM = "Play/Skip"; +const char pstrUsageVolume [] PROGMEM = "Vol"; +const char pstrUsageBalance [] PROGMEM = "Balance"; +//const char pstrUsageMute [] PROGMEM = "Mute"; +const char pstrUsageBass [] PROGMEM = "Bass"; +const char pstrUsageTreble [] PROGMEM = "Treble"; +const char pstrUsageBassBoost [] PROGMEM = "Bass Boost"; +const char pstrUsageSurroundMode [] PROGMEM = "Surround"; +const char pstrUsageLoudness [] PROGMEM = "Loud"; +const char pstrUsageMPX [] PROGMEM = "MPX"; +const char pstrUsageVolumeIncrement [] PROGMEM = "Vol Inc"; +const char pstrUsageVolumeDecrement [] PROGMEM = "Vol Dec"; +const char pstrUsageSpeedSelect [] PROGMEM = "Speed"; +const char pstrUsagePlaybackSpeed [] PROGMEM = "Play Speed"; +const char pstrUsageStandardPlay [] PROGMEM = "Std Play"; +const char pstrUsageLongPlay [] PROGMEM = "Long Play"; +const char pstrUsageExtendedPlay [] PROGMEM = "Ext Play"; +const char pstrUsageSlow [] PROGMEM = "Slow"; +const char pstrUsageFanEnable [] PROGMEM = "Fan Enbl"; +const char pstrUsageFanSpeed [] PROGMEM = "Fan Speed"; +const char pstrUsageLightEnable [] PROGMEM = "Light Enbl"; +const char pstrUsageLightIlluminationLevel [] PROGMEM = "Light Illum Lev"; +const char pstrUsageClimateControlEnable [] PROGMEM = "Climate Enbl"; +const char pstrUsageRoomTemperature [] PROGMEM = "Room Temp"; +const char pstrUsageSecurityEnable [] PROGMEM = "Secur Enbl"; +const char pstrUsageFireAlarm [] PROGMEM = "Fire Alm"; +const char pstrUsagePoliceAlarm [] PROGMEM = "Police Alm"; +const char pstrUsageProximity [] PROGMEM = "Prox"; +const char pstrUsageMotion [] PROGMEM = "Motion"; +const char pstrUsageDuresAlarm [] PROGMEM = "Dures Alm"; +const char pstrUsageHoldupAlarm [] PROGMEM = "Holdup Alm"; +const char pstrUsageMedicalAlarm [] PROGMEM = "Med Alm"; +const char pstrUsageBalanceRight [] PROGMEM = "Balance Right"; +const char pstrUsageBalanceLeft [] PROGMEM = "Balance Left"; +const char pstrUsageBassIncrement [] PROGMEM = "Bass Inc"; +const char pstrUsageBassDecrement [] PROGMEM = "Bass Dec"; +const char pstrUsageTrebleIncrement [] PROGMEM = "Treble Inc"; +const char pstrUsageTrebleDecrement [] PROGMEM = "Treble Dec"; +const char pstrUsageSpeakerSystem [] PROGMEM = "Spk Sys"; +const char pstrUsageChannelLeft [] PROGMEM = "Ch Left"; +const char pstrUsageChannelRight [] PROGMEM = "Ch Right"; +const char pstrUsageChannelCenter [] PROGMEM = "Ch Center"; +const char pstrUsageChannelFront [] PROGMEM = "Ch Front"; +const char pstrUsageChannelCenterFront [] PROGMEM = "Ch Cntr Front"; +const char pstrUsageChannelSide [] PROGMEM = "Ch Side"; +const char pstrUsageChannelSurround [] PROGMEM = "Ch Surround"; +const char pstrUsageChannelLowFreqEnhancement [] PROGMEM = "Ch Low Freq Enh"; +const char pstrUsageChannelTop [] PROGMEM = "Ch Top"; +const char pstrUsageChannelUnknown [] PROGMEM = "Ch Unk"; +const char pstrUsageSubChannel [] PROGMEM = "Sub-ch"; +const char pstrUsageSubChannelIncrement [] PROGMEM = "Sub-ch Inc"; +const char pstrUsageSubChannelDecrement [] PROGMEM = "Sub-ch Dec"; +const char pstrUsageAlternateAudioIncrement [] PROGMEM = "Alt Aud Inc"; +const char pstrUsageAlternateAudioDecrement [] PROGMEM = "Alt Aud Dec"; +const char pstrUsageApplicationLaunchButtons [] PROGMEM = "App Launch Btns"; +const char pstrUsageALLaunchButtonConfigTool [] PROGMEM = "AL Launch Conf Tl"; +const char pstrUsageALProgrammableButton [] PROGMEM = "AL Pgm Btn"; +const char pstrUsageALConsumerControlConfig [] PROGMEM = "AL Cons Ctrl Cfg"; +const char pstrUsageALWordProcessor [] PROGMEM = "AL Word Proc"; +const char pstrUsageALTextEditor [] PROGMEM = "AL Txt Edtr"; +const char pstrUsageALSpreadsheet [] PROGMEM = "AL Sprdsheet"; +const char pstrUsageALGraphicsEditor [] PROGMEM = "AL Graph Edtr"; +const char pstrUsageALPresentationApp [] PROGMEM = "AL Present App"; +const char pstrUsageALDatabaseApp [] PROGMEM = "AL DB App"; +const char pstrUsageALEmailReader [] PROGMEM = "AL E-mail Rdr"; +const char pstrUsageALNewsreader [] PROGMEM = "AL Newsrdr"; +const char pstrUsageALVoicemail [] PROGMEM = "AL Voicemail"; +const char pstrUsageALContactsAddressBook [] PROGMEM = "AL Addr Book"; +const char pstrUsageALCalendarSchedule [] PROGMEM = "AL Clndr/Schdlr"; +const char pstrUsageALTaskProjectManager [] PROGMEM = "AL Task/Prj Mgr"; +const char pstrUsageALLogJournalTimecard [] PROGMEM = "AL Log/Jrnl/Tmcrd"; +const char pstrUsageALCheckbookFinance [] PROGMEM = "AL Chckbook/Fin"; +const char pstrUsageALCalculator [] PROGMEM = "AL Calc"; +const char pstrUsageALAVCapturePlayback [] PROGMEM = "AL A/V Capt/Play"; +const char pstrUsageALLocalMachineBrowser [] PROGMEM = "AL Loc Mach Brow"; +const char pstrUsageALLANWANBrow [] PROGMEM = "AL LAN/WAN Brow"; +const char pstrUsageALInternetBrowser [] PROGMEM = "AL I-net Brow"; +const char pstrUsageALRemoteNetISPConnect [] PROGMEM = "AL Rem Net Con"; +const char pstrUsageALNetworkConference [] PROGMEM = "AL Net Conf"; +const char pstrUsageALNetworkChat [] PROGMEM = "AL Net Chat"; +const char pstrUsageALTelephonyDialer [] PROGMEM = "AL Tel/Dial"; +const char pstrUsageALLogon [] PROGMEM = "AL Logon"; +const char pstrUsageALLogoff [] PROGMEM = "AL Logoff"; +const char pstrUsageALLogonLogoff [] PROGMEM = "AL Logon/Logoff"; +const char pstrUsageALTermLockScrSav [] PROGMEM = "AL Term Lock/Scr Sav"; +const char pstrUsageALControlPannel [] PROGMEM = "AL Ctrl Pan"; +const char pstrUsageALCommandLineProcessorRun [] PROGMEM = "AL Cmd/Run"; +const char pstrUsageALProcessTaskManager [] PROGMEM = "AL Task Mgr"; +const char pstrUsageALSelectTaskApplication [] PROGMEM = "AL Sel App"; +const char pstrUsageALNextTaskApplication [] PROGMEM = "AL Next App"; +const char pstrUsageALPreviousTaskApplication [] PROGMEM = "AL Prev App"; +const char pstrUsageALPreemptiveHaltTaskApp [] PROGMEM = "AL Prmpt Halt App"; +const char pstrUsageALIntegratedHelpCenter [] PROGMEM = "AL Hlp Cntr"; +const char pstrUsageALDocuments [] PROGMEM = "AL Docs"; +const char pstrUsageALThesaurus [] PROGMEM = "AL Thsrs"; +const char pstrUsageALDictionary [] PROGMEM = "AL Dict"; +const char pstrUsageALDesktop [] PROGMEM = "AL Desktop"; +const char pstrUsageALSpellCheck [] PROGMEM = "AL Spell Chk"; +const char pstrUsageALGrammarCheck [] PROGMEM = "AL Gram Chk"; +const char pstrUsageALWirelessStatus [] PROGMEM = "AL Wireless Sts"; +const char pstrUsageALKeyboardLayout [] PROGMEM = "AL Kbd Layout"; +const char pstrUsageALVirusProtection [] PROGMEM = "AL Vir Protect"; +const char pstrUsageALEncryption [] PROGMEM = "AL Encrypt"; +const char pstrUsageALScreenSaver [] PROGMEM = "AL Scr Sav"; +const char pstrUsageALAlarms [] PROGMEM = "AL Alarms"; +const char pstrUsageALClock [] PROGMEM = "AL Clock"; +const char pstrUsageALFileBrowser [] PROGMEM = "AL File Brow"; +const char pstrUsageALPowerStatus [] PROGMEM = "AL Pwr Sts"; +const char pstrUsageALImageBrowser [] PROGMEM = "AL Img Brow"; +const char pstrUsageALAudioBrowser [] PROGMEM = "AL Aud Brow"; +const char pstrUsageALMovieBrowser [] PROGMEM = "AL Mov Brow"; +const char pstrUsageALDigitalRightsManager [] PROGMEM = "AL Dig Rights Mgr"; +const char pstrUsageALDigitalWallet [] PROGMEM = "AL Dig Wallet"; +const char pstrUsageALInstantMessaging [] PROGMEM = "AL Inst Msg"; +const char pstrUsageALOEMFeaturesBrowser [] PROGMEM = "AL OEM Tips Brow"; +const char pstrUsageALOEMHelp [] PROGMEM = "AL OEM Hlp"; +const char pstrUsageALOnlineCommunity [] PROGMEM = "AL Online Com"; +const char pstrUsageALEntertainmentContentBrow [] PROGMEM = "AL Ent Cont Brow"; +const char pstrUsageALOnlineShoppingBrowser [] PROGMEM = "AL Online Shop Brow"; +const char pstrUsageALSmartCardInfoHelp [] PROGMEM = "AL SmartCard Inf"; +const char pstrUsageALMarketMonitorFinBrowser [] PROGMEM = "AL Market Brow"; +const char pstrUsageALCustomCorpNewsBrowser [] PROGMEM = "AL Cust Corp News Brow"; +const char pstrUsageALOnlineActivityBrowser [] PROGMEM = "AL Online Act Brow"; +const char pstrUsageALResearchSearchBrowser [] PROGMEM = "AL Search Brow"; +const char pstrUsageALAudioPlayer [] PROGMEM = "AL Aud Player"; +const char pstrUsageGenericGUIAppControls [] PROGMEM = "Gen GUI App Ctrl"; +const char pstrUsageACNew [] PROGMEM = "AC New"; +const char pstrUsageACOpen [] PROGMEM = "AC Open"; +const char pstrUsageACClose [] PROGMEM = "AC Close"; +const char pstrUsageACExit [] PROGMEM = "AC Exit"; +const char pstrUsageACMaximize [] PROGMEM = "AC Max"; +const char pstrUsageACMinimize [] PROGMEM = "AC Min"; +const char pstrUsageACSave [] PROGMEM = "AC Save"; +const char pstrUsageACPrint [] PROGMEM = "AC Print"; +const char pstrUsageACProperties [] PROGMEM = "AC Prop"; +const char pstrUsageACUndo [] PROGMEM = "AC Undo"; +const char pstrUsageACCopy [] PROGMEM = "AC Copy"; +const char pstrUsageACCut [] PROGMEM = "AC Cut"; +const char pstrUsageACPaste [] PROGMEM = "AC Paste"; +const char pstrUsageACSelectAll [] PROGMEM = "AC Sel All"; +const char pstrUsageACFind [] PROGMEM = "AC Find"; +const char pstrUsageACFindAndReplace [] PROGMEM = "AC Find/Replace"; +const char pstrUsageACSearch [] PROGMEM = "AC Search"; +const char pstrUsageACGoto [] PROGMEM = "AC Goto"; +const char pstrUsageACHome [] PROGMEM = "AC Home"; +const char pstrUsageACBack [] PROGMEM = "AC Back"; +const char pstrUsageACForward [] PROGMEM = "AC Fwd"; +const char pstrUsageACStop [] PROGMEM = "AC Stop"; +const char pstrUsageACRefresh [] PROGMEM = "AC Refresh"; +const char pstrUsageACPreviousLink [] PROGMEM = "AC Prev Link"; +const char pstrUsageACNextLink [] PROGMEM = "AC Next Link"; +const char pstrUsageACBookmarks [] PROGMEM = "AC Bkmarks"; +const char pstrUsageACHistory [] PROGMEM = "AC Hist"; +const char pstrUsageACSubscriptions [] PROGMEM = "AC Subscr"; +const char pstrUsageACZoomIn [] PROGMEM = "AC Zoom In"; +const char pstrUsageACZoomOut [] PROGMEM = "AC Zoom Out"; +const char pstrUsageACZoom [] PROGMEM = "AC Zoom"; +const char pstrUsageACFullScreenView [] PROGMEM = "AC Full Scr"; +const char pstrUsageACNormalView [] PROGMEM = "AC Norm View"; +const char pstrUsageACViewToggle [] PROGMEM = "AC View Tgl"; +const char pstrUsageACScrollUp [] PROGMEM = "AC Scroll Up"; +const char pstrUsageACScrollDown [] PROGMEM = "AC Scroll Down"; +const char pstrUsageACScroll [] PROGMEM = "AC Scroll"; +const char pstrUsageACPanLeft [] PROGMEM = "AC Pan Left"; +const char pstrUsageACPanRight [] PROGMEM = "AC Pan Right"; +const char pstrUsageACPan [] PROGMEM = "AC Pan"; +const char pstrUsageACNewWindow [] PROGMEM = "AC New Wnd"; +const char pstrUsageACTileHoriz [] PROGMEM = "AC Tile Horiz"; +const char pstrUsageACTileVert [] PROGMEM = "AC Tile Vert"; +const char pstrUsageACFormat [] PROGMEM = "AC Frmt"; +const char pstrUsageACEdit [] PROGMEM = "AC Edit"; +const char pstrUsageACBold [] PROGMEM = "AC Bold"; +const char pstrUsageACItalics [] PROGMEM = "AC Ital"; +const char pstrUsageACUnderline [] PROGMEM = "AC Under"; +const char pstrUsageACStrikethrough [] PROGMEM = "AC Strike"; +const char pstrUsageACSubscript [] PROGMEM = "AC Sub"; +const char pstrUsageACSuperscript [] PROGMEM = "AC Super"; +const char pstrUsageACAllCaps [] PROGMEM = "AC All Caps"; +const char pstrUsageACRotate [] PROGMEM = "AC Rotate"; +const char pstrUsageACResize [] PROGMEM = "AC Resize"; +const char pstrUsageACFlipHorizontal [] PROGMEM = "AC Flp H"; +const char pstrUsageACFlipVertical [] PROGMEM = "AC Flp V"; +const char pstrUsageACMirrorHorizontal [] PROGMEM = "AC Mir H"; +const char pstrUsageACMirrorVertical [] PROGMEM = "AC Mir V"; +const char pstrUsageACFontSelect [] PROGMEM = "AC Fnt Sel"; +const char pstrUsageACFontColor [] PROGMEM = "AC Fnt Clr"; +const char pstrUsageACFontSize [] PROGMEM = "AC Fnt Size"; +const char pstrUsageACJustifyLeft [] PROGMEM = "AC Just Left"; +const char pstrUsageACJustifyCenterH [] PROGMEM = "AC Just Cent H"; +const char pstrUsageACJustifyRight [] PROGMEM = "AC Just Right"; +const char pstrUsageACJustifyBlockH [] PROGMEM = "AC Just Block H"; +const char pstrUsageACJustifyTop [] PROGMEM = "AC Just Top"; +const char pstrUsageACJustifyCenterV [] PROGMEM = "AC Just Cent V"; +const char pstrUsageACJustifyBottom [] PROGMEM = "AC Just Bot"; +const char pstrUsageACJustifyBlockV [] PROGMEM = "AC Just Block V"; +const char pstrUsageACIndentDecrease [] PROGMEM = "AC Indent Dec"; +const char pstrUsageACIndentIncrease [] PROGMEM = "AC Indent Inc"; +const char pstrUsageACNumberedList [] PROGMEM = "AC Num List"; +const char pstrUsageACRestartNumbering [] PROGMEM = "AC Res Num"; +const char pstrUsageACBulletedList [] PROGMEM = "AC Blt List"; +const char pstrUsageACPromote [] PROGMEM = "AC Promote"; +const char pstrUsageACDemote [] PROGMEM = "AC Demote"; +const char pstrUsageACYes [] PROGMEM = "AC Yes"; +const char pstrUsageACNo [] PROGMEM = "AC No"; +const char pstrUsageACCancel [] PROGMEM = "AC Cancel"; +const char pstrUsageACCatalog [] PROGMEM = "AC Ctlg"; +const char pstrUsageACBuyChkout [] PROGMEM = "AC Buy"; +const char pstrUsageACAddToCart [] PROGMEM = "AC Add2Cart"; +const char pstrUsageACExpand [] PROGMEM = "AC Xpnd"; +const char pstrUsageACExpandAll [] PROGMEM = "AC Xpand All"; +const char pstrUsageACCollapse [] PROGMEM = "AC Collapse"; +const char pstrUsageACCollapseAll [] PROGMEM = "AC Collapse All"; +const char pstrUsageACPrintPreview [] PROGMEM = "AC Prn Prevw"; +const char pstrUsageACPasteSpecial [] PROGMEM = "AC Paste Spec"; +const char pstrUsageACInsertMode [] PROGMEM = "AC Ins Mode"; +const char pstrUsageACDelete [] PROGMEM = "AC Del"; +const char pstrUsageACLock [] PROGMEM = "AC Lock"; +const char pstrUsageACUnlock [] PROGMEM = "AC Unlock"; +const char pstrUsageACProtect [] PROGMEM = "AC Prot"; +const char pstrUsageACUnprotect [] PROGMEM = "AC Unprot"; +const char pstrUsageACAttachComment [] PROGMEM = "AC Attach Cmnt"; +const char pstrUsageACDeleteComment [] PROGMEM = "AC Del Cmnt"; +const char pstrUsageACViewComment [] PROGMEM = "AC View Cmnt"; +const char pstrUsageACSelectWord [] PROGMEM = "AC Sel Word"; +const char pstrUsageACSelectSentence [] PROGMEM = "AC Sel Sntc"; +const char pstrUsageACSelectParagraph [] PROGMEM = "AC Sel Para"; +const char pstrUsageACSelectColumn [] PROGMEM = "AC Sel Col"; +const char pstrUsageACSelectRow [] PROGMEM = "AC Sel Row"; +const char pstrUsageACSelectTable [] PROGMEM = "AC Sel Tbl"; +const char pstrUsageACSelectObject [] PROGMEM = "AC Sel Obj"; +const char pstrUsageACRedoRepeat [] PROGMEM = "AC Redo"; +const char pstrUsageACSort [] PROGMEM = "AC Sort"; +const char pstrUsageACSortAscending [] PROGMEM = "AC Sort Asc"; +const char pstrUsageACSortDescending [] PROGMEM = "AC Sort Desc"; +const char pstrUsageACFilter [] PROGMEM = "AC Filt"; +const char pstrUsageACSetClock [] PROGMEM = "AC Set Clk"; +const char pstrUsageACViewClock [] PROGMEM = "AC View Clk"; +const char pstrUsageACSelectTimeZone [] PROGMEM = "AC Sel Time Z"; +const char pstrUsageACEditTimeZone [] PROGMEM = "AC Edt Time Z"; +const char pstrUsageACSetAlarm [] PROGMEM = "AC Set Alm"; +const char pstrUsageACClearAlarm [] PROGMEM = "AC Clr Alm"; +const char pstrUsageACSnoozeAlarm [] PROGMEM = "AC Snz Alm"; +const char pstrUsageACResetAlarm [] PROGMEM = "AC Rst Alm"; +const char pstrUsageACSyncronize [] PROGMEM = "AC Sync"; +const char pstrUsageACSendReceive [] PROGMEM = "AC Snd/Rcv"; +const char pstrUsageACSendTo [] PROGMEM = "AC Snd To"; +const char pstrUsageACReply [] PROGMEM = "AC Reply"; +const char pstrUsageACReplyAll [] PROGMEM = "AC Reply All"; +const char pstrUsageACForwardMessage [] PROGMEM = "AC Fwd Msg"; +const char pstrUsageACSend [] PROGMEM = "AC Snd"; +const char pstrUsageACAttachFile [] PROGMEM = "AC Att File"; +const char pstrUsageACUpload [] PROGMEM = "AC Upld"; +const char pstrUsageACDownload [] PROGMEM = "AC Dnld"; +const char pstrUsageACSetBorders [] PROGMEM = "AC Set Brd"; +const char pstrUsageACInsertRow [] PROGMEM = "AC Ins Row"; +const char pstrUsageACInsertColumn [] PROGMEM = "AC Ins Col"; +const char pstrUsageACInsertFile [] PROGMEM = "AC Ins File"; +const char pstrUsageACInsertPicture [] PROGMEM = "AC Ins Pic"; +const char pstrUsageACInsertObject [] PROGMEM = "AC Ins Obj"; +const char pstrUsageACInsertSymbol [] PROGMEM = "AC Ins Sym"; +const char pstrUsageACSaveAndClose [] PROGMEM = "AC Sav&Cls"; +const char pstrUsageACRename [] PROGMEM = "AC Rename"; +const char pstrUsageACMerge [] PROGMEM = "AC Merge"; +const char pstrUsageACSplit [] PROGMEM = "AC Split"; +const char pstrUsageACDistributeHorizontaly [] PROGMEM = "AC Dist Hor"; +const char pstrUsageACDistributeVerticaly [] PROGMEM = "AC Dist Ver"; + +// Digitaizers +const char pstrUsageDigitizer [] PROGMEM = "Digitizer"; +const char pstrUsagePen [] PROGMEM = "Pen"; +const char pstrUsageLightPen [] PROGMEM = "Light Pen"; +const char pstrUsageTouchScreen [] PROGMEM = "Touch Scr"; +const char pstrUsageTouchPad [] PROGMEM = "Touch Pad"; +const char pstrUsageWhiteBoard [] PROGMEM = "White Brd"; +const char pstrUsageCoordinateMeasuringMachine [] PROGMEM = "Coord Meas Mach"; +const char pstrUsage3DDigitizer [] PROGMEM = "3D Dgtz"; +const char pstrUsageStereoPlotter [] PROGMEM = "Stereo Plot"; +const char pstrUsageArticulatedArm [] PROGMEM = "Art Arm"; +const char pstrUsageArmature [] PROGMEM = "Armature"; +const char pstrUsageMultiplePointDigitizer [] PROGMEM = "Multi Point Dgtz"; +const char pstrUsageFreeSpaceWand [] PROGMEM = "Free Space Wand"; +const char pstrUsageStylus [] PROGMEM = "Stylus"; +const char pstrUsagePuck [] PROGMEM = "Puck"; +const char pstrUsageFinger [] PROGMEM = "Finger"; +const char pstrUsageTipPressure [] PROGMEM = "Tip Press"; +const char pstrUsageBarrelPressure [] PROGMEM = "Brl Press"; +const char pstrUsageInRange [] PROGMEM = "In Range"; +const char pstrUsageTouch [] PROGMEM = "Touch"; +const char pstrUsageUntouch [] PROGMEM = "Untouch"; +const char pstrUsageTap [] PROGMEM = "Tap"; +const char pstrUsageQuality [] PROGMEM = "Qlty"; +const char pstrUsageDataValid [] PROGMEM = "Data Valid"; +const char pstrUsageTransducerIndex [] PROGMEM = "Transducer Ind"; +const char pstrUsageTabletFunctionKeys [] PROGMEM = "Tabl Func Keys"; +const char pstrUsageProgramChangeKeys [] PROGMEM = "Pgm Chng Keys"; +//const char pstrUsageBatteryStrength [] PROGMEM = "Bat Strength"; +const char pstrUsageInvert [] PROGMEM = "Invert"; +const char pstrUsageXTilt [] PROGMEM = "X Tilt"; +const char pstrUsageYTilt [] PROGMEM = "Y Tilt"; +const char pstrUsageAzimuth [] PROGMEM = "Azimuth"; +const char pstrUsageAltitude [] PROGMEM = "Altitude"; +const char pstrUsageTwist [] PROGMEM = "Twist"; +const char pstrUsageTipSwitch [] PROGMEM = "Tip Sw"; +const char pstrUsageSecondaryTipSwitch [] PROGMEM = "Scnd Tip Sw"; +const char pstrUsageBarrelSwitch [] PROGMEM = "Brl Sw"; +const char pstrUsageEraser [] PROGMEM = "Eraser"; +const char pstrUsageTabletPick [] PROGMEM = "Tbl Pick"; + +// Alphanumeric Display Page +const char pstrUsageAlphanumericDisplay [] PROGMEM = "Alphanum Disp"; +const char pstrUsageBitmappedDisplay [] PROGMEM = "Bmp Disp"; +const char pstrUsageDisplayAttributesReport [] PROGMEM = "Disp Attr Rpt"; +const char pstrUsageASCIICharacterSet [] PROGMEM = "ASCII chset"; +const char pstrUsageDataReadBack [] PROGMEM = "Data Rd Back"; +const char pstrUsageFontReadBack [] PROGMEM = "Fnt Rd Back"; +const char pstrUsageDisplayControlReport [] PROGMEM = "Disp Ctrl Rpt"; +const char pstrUsageClearDisplay [] PROGMEM = "Clr Disp"; +//const char pstrUsageDisplayEnable [] PROGMEM = "Disp Enbl"; +const char pstrUsageScreenSaverDelay [] PROGMEM = "Scr Sav Delay"; +const char pstrUsageScreenSaverEnable [] PROGMEM = "Scr Sav Enbl"; +const char pstrUsageVerticalScroll [] PROGMEM = "V Scroll"; +const char pstrUsageHorizontalScroll [] PROGMEM = "H Scroll"; +const char pstrUsageCharacterReport [] PROGMEM = "Char Rpt"; +const char pstrUsageDisplayData [] PROGMEM = "Disp Data"; +const char pstrUsageDisplayStatus [] PROGMEM = "Disp Stat"; +const char pstrUsageStatusNotReady [] PROGMEM = "Stat !Ready"; +const char pstrUsageStatusReady [] PROGMEM = "Stat Ready"; +const char pstrUsageErrorNotALoadableCharacter [] PROGMEM = "Err Not Ld Char"; +const char pstrUsageErrorFotDataCanNotBeRead [] PROGMEM = "Fnt Data Rd Err"; +const char pstrUsageCursorPositionReport [] PROGMEM = "Cur Pos Rpt"; +const char pstrUsageRow [] PROGMEM = "Row"; +const char pstrUsageColumn [] PROGMEM = "Col"; +const char pstrUsageRows [] PROGMEM = "Rows"; +const char pstrUsageColumns [] PROGMEM = "Cols"; +const char pstrUsageCursorPixelPosition [] PROGMEM = "Cur Pix Pos"; +const char pstrUsageCursorMode [] PROGMEM = "Cur Mode"; +const char pstrUsageCursorEnable [] PROGMEM = "Cur Enbl"; +const char pstrUsageCursorBlink [] PROGMEM = "Cur Blnk"; +const char pstrUsageFontReport [] PROGMEM = "Fnt Rpt"; +const char pstrUsageFontData [] PROGMEM = "Fnt Data"; +const char pstrUsageCharacterWidth [] PROGMEM = "Char Wdth"; +const char pstrUsageCharacterHeight [] PROGMEM = "Char Hght"; +const char pstrUsageCharacterSpacingHorizontal [] PROGMEM = "Char Space H"; +const char pstrUsageCharacterSpacingVertical [] PROGMEM = "Char Space V"; +const char pstrUsageUnicodeCharset [] PROGMEM = "Unicode Char"; +const char pstrUsageFont7Segment [] PROGMEM = "Fnt 7-seg"; +const char pstrUsage7SegmentDirectMap [] PROGMEM = "7-seg map"; +const char pstrUsageFont14Segment [] PROGMEM = "Fnt 14-seg"; +const char pstrUsage14SegmentDirectMap [] PROGMEM = "14-seg map"; +const char pstrUsageDisplayBrightness [] PROGMEM = "Disp Bright"; +const char pstrUsageDisplayContrast [] PROGMEM = "Disp Cntrst"; +const char pstrUsageCharacterAttribute [] PROGMEM = "Char Attr"; +const char pstrUsageAttributeReadback [] PROGMEM = "Attr Readbk"; +const char pstrUsageAttributeData [] PROGMEM = "Attr Data"; +const char pstrUsageCharAttributeEnhance [] PROGMEM = "Char Attr Enh"; +const char pstrUsageCharAttributeUnderline [] PROGMEM = "Char Attr Undl"; +const char pstrUsageCharAttributeBlink [] PROGMEM = "Char Attr Blnk"; +const char pstrUsageBitmapSizeX [] PROGMEM = "Bmp Size X"; +const char pstrUsageBitmapSizeY [] PROGMEM = "Bmp Size Y"; +const char pstrUsageBitDepthFormat [] PROGMEM = "Bit Dpth Fmt"; +const char pstrUsageDisplayOrientation [] PROGMEM = "Disp Ornt"; +const char pstrUsagePaletteReport [] PROGMEM = "Pal Rpt"; +const char pstrUsagePaletteDataSize [] PROGMEM = "Pal Data Size"; +const char pstrUsagePaletteDataOffset [] PROGMEM = "Pal Data Off"; +const char pstrUsagePaletteData [] PROGMEM = "Pal Data"; +const char pstrUsageBlitReport [] PROGMEM = "Blit Rpt"; +const char pstrUsageBlitRectangleX1 [] PROGMEM = "Blit Rect X1"; +const char pstrUsageBlitRectangleY1 [] PROGMEM = "Blit Rect Y1"; +const char pstrUsageBlitRectangleX2 [] PROGMEM = "Blit Rect X2"; +const char pstrUsageBlitRectangleY2 [] PROGMEM = "Blit Rect Y2"; +const char pstrUsageBlitData [] PROGMEM = "Blit Data"; +const char pstrUsageSoftButton [] PROGMEM = "Soft Btn"; +const char pstrUsageSoftButtonID [] PROGMEM = "Soft Btn ID"; +const char pstrUsageSoftButtonSide [] PROGMEM = "Soft Btn Side"; +const char pstrUsageSoftButtonOffset1 [] PROGMEM = "Soft Btn Off1"; +const char pstrUsageSoftButtonOffset2 [] PROGMEM = "Soft Btn Off2"; +const char pstrUsageSoftButtonReport [] PROGMEM = "Soft Btn Rpt"; + +// Medical Instrument Page +const char pstrUsageMedicalUltrasound [] PROGMEM = "Med Ultrasnd"; +const char pstrUsageVCRAcquisition [] PROGMEM = "VCR/Acq"; +const char pstrUsageFreezeThaw [] PROGMEM = "Freeze"; +const char pstrUsageClipStore [] PROGMEM = "Clip Store"; +const char pstrUsageUpdate [] PROGMEM = "Update"; +const char pstrUsageNext [] PROGMEM = "Next"; +const char pstrUsageSave [] PROGMEM = "Save"; +const char pstrUsagePrint [] PROGMEM = "Print"; +const char pstrUsageMicrophoneEnable [] PROGMEM = "Mic Enbl"; +const char pstrUsageCine [] PROGMEM = "Cine"; +const char pstrUsageTransmitPower [] PROGMEM = "Trans Pwr"; +//const char pstrUsageVolume [] PROGMEM = "Vol"; +const char pstrUsageFocus [] PROGMEM = "Focus"; +const char pstrUsageDepth [] PROGMEM = "Depth"; +const char pstrUsageSoftStepPrimary [] PROGMEM = "Soft Stp-Pri"; +const char pstrUsageSoftStepSecondary [] PROGMEM = "Soft Stp-Sec"; +const char pstrUsageDepthGainCompensation [] PROGMEM = "Dpth Gain Comp"; +const char pstrUsageZoomSelect [] PROGMEM = "Zoom Sel"; +const char pstrUsageZoomAdjust [] PROGMEM = "Zoom Adj"; +const char pstrUsageSpectralDopplerModeSelect [] PROGMEM = "Spec Dop Mode Sel"; +const char pstrUsageSpectralDopplerModeAdjust [] PROGMEM = "Spec Dop Mode Adj"; +const char pstrUsageColorDopplerModeSelect [] PROGMEM = "Color Dop Mode Sel"; +const char pstrUsageColorDopplerModeAdjust [] PROGMEM = "Color Dop Mode Adj"; +const char pstrUsageMotionModeSelect [] PROGMEM = "Motion Mode Sel"; +const char pstrUsageMotionModeAdjust [] PROGMEM = "Motion Mode Adj"; +const char pstrUsage2DModeSelect [] PROGMEM = "2D Mode Sel"; +const char pstrUsage2DModeAdjust [] PROGMEM = "2D Mode Adj"; +const char pstrUsageSoftControlSelect [] PROGMEM = "Soft Ctrl Sel"; +const char pstrUsageSoftControlAdjust [] PROGMEM = "Soft Ctrl Adj"; + +//extern const char *usagePageTitles0[15]; +//const char *usagePageTitles1[]; +//const char *genDesktopTitles0[]; +//const char *genDesktopTitles1[]; +//const char *genDesktopTitles2[]; +//const char *genDesktopTitles3[]; +//const char *genDesktopTitles4[]; +//const char *simuTitles0[]; +//const char *simuTitles1[]; +//const char *simuTitles2[]; +//const char *vrTitles0[]; +//const char *vrTitles1[]; +//const char *sportsCtrlTitles0[]; +//const char *sportsCtrlTitles1[]; +//const char *sportsCtrlTitles2[]; +//const char *gameTitles0[]; +//const char *gameTitles1[]; +//const char *genDevCtrlTitles[]; +//const char *ledTitles[]; +//const char *telTitles0[]; +//const char *telTitles1[]; +//const char *telTitles2[]; +//const char *telTitles3[]; +//const char *telTitles4[]; +//const char *telTitles5[]; +//const char *consTitles0[]; +//const char *consTitles1[]; +//const char *consTitles2[]; +//const char *consTitles3[]; +//const char *consTitles4[]; +//const char *consTitles5[]; +//const char *consTitles6[]; +//const char *consTitles7[]; +//const char *consTitles8[]; +//const char *consTitles9[]; +//const char *consTitlesA[]; +//const char *consTitlesB[]; +//const char *consTitlesC[]; +//const char *consTitlesD[]; +//const char *consTitlesE[]; +//const char *digitTitles0[]; +//const char *digitTitles1[]; +//const char *digitTitles2[]; +//const char *aplphanumTitles0[]; +//const char *aplphanumTitles1[]; +//const char *aplphanumTitles2[]; +//const char *medInstrTitles0[]; +//const char *medInstrTitles1[]; +//const char *medInstrTitles2[]; +//const char *medInstrTitles3[]; +//const char *medInstrTitles4[]; + +#endif //__HIDUSAGESTR_H__ diff --git a/libraries/USB_Host_Shield/hidusagetitlearrays.cpp b/libraries/USB_Host_Shield/hidusagetitlearrays.cpp new file mode 100755 index 0000000..ee23300 --- /dev/null +++ b/libraries/USB_Host_Shield/hidusagetitlearrays.cpp @@ -0,0 +1,1048 @@ +/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved. + +This software may be distributed and modified under the terms of the GNU +General Public License version 2 (GPL2) as published by the Free Software +Foundation and appearing in the file GPL2.TXT included in the packaging of +this file. Please note that GPL2 Section 2[b] requires that all works based +on this software must also be made publicly available under the terms of +the GPL2 ("Copyleft"). + +Contact information +------------------- + +Circuits At Home, LTD +Web : http://www.circuitsathome.com +e-mail : support@circuitsathome.com + */ +#if !defined(__HIDUSAGETITLEARRAYS_H__) +#define __HIDUSAGETITLEARRAYS_H__ + +#include "hidusagestr.h" + +// This is here why? + +//const char *usagePageTitles0[] PROGMEM = +//{ +// pstrUsagePageGenericDesktopControls , +// pstrUsagePageSimulationControls , +// pstrUsagePageVRControls , +// pstrUsagePageSportControls , +// pstrUsagePageGameControls , +// pstrUsagePageGenericDeviceControls , +// pstrUsagePageKeyboardKeypad , +// pstrUsagePageLEDs , +// pstrUsagePageButton , +// pstrUsagePageOrdinal , +// pstrUsagePageTelephone , +// pstrUsagePageConsumer , +// pstrUsagePageDigitizer , +// pstrUsagePagePID , +// pstrUsagePageUnicode +//}; +// +//const char *usagePageTitles1[] PROGMEM = +//{ +// pstrUsagePageBarCodeScanner , +// pstrUsagePageScale , +// pstrUsagePageMSRDevices , +// pstrUsagePagePointOfSale , +// pstrUsagePageCameraControl , +// pstrUsagePageArcade +//}; +//const char *genDesktopTitles0[] PROGMEM = +//{ +// pstrUsagePointer , +// pstrUsageMouse , +// pstrUsageJoystick , +// pstrUsageGamePad , +// pstrUsageKeyboard , +// pstrUsageKeypad , +// pstrUsageMultiAxisController , +// pstrUsageTabletPCSystemControls +// +//}; +//const char *genDesktopTitles1[] PROGMEM = +//{ +// pstrUsageX , +// pstrUsageY , +// pstrUsageZ , +// pstrUsageRx , +// pstrUsageRy , +// pstrUsageRz , +// pstrUsageSlider , +// pstrUsageDial , +// pstrUsageWheel , +// pstrUsageHatSwitch , +// pstrUsageCountedBuffer , +// pstrUsageByteCount , +// pstrUsageMotionWakeup , +// pstrUsageStart , +// pstrUsageSelect , +// pstrUsagePageReserved , +// pstrUsageVx , +// pstrUsageVy , +// pstrUsageVz , +// pstrUsageVbrx , +// pstrUsageVbry , +// pstrUsageVbrz , +// pstrUsageVno , +// pstrUsageFeatureNotification , +// pstrUsageResolutionMultiplier +//}; +//const char *genDesktopTitles2[] PROGMEM = +//{ +// pstrUsageSystemControl , +// pstrUsageSystemPowerDown , +// pstrUsageSystemSleep , +// pstrUsageSystemWakeup , +// pstrUsageSystemContextMenu , +// pstrUsageSystemMainMenu , +// pstrUsageSystemAppMenu , +// pstrUsageSystemMenuHelp , +// pstrUsageSystemMenuExit , +// pstrUsageSystemMenuSelect , +// pstrUsageSystemMenuRight , +// pstrUsageSystemMenuLeft , +// pstrUsageSystemMenuUp , +// pstrUsageSystemMenuDown , +// pstrUsageSystemColdRestart , +// pstrUsageSystemWarmRestart , +// pstrUsageDPadUp , +// pstrUsageDPadDown , +// pstrUsageDPadRight , +// pstrUsageDPadLeft +//}; +//const char *genDesktopTitles3[] PROGMEM = +//{ +// pstrUsageSystemDock , +// pstrUsageSystemUndock , +// pstrUsageSystemSetup , +// pstrUsageSystemBreak , +// pstrUsageSystemDebuggerBreak , +// pstrUsageApplicationBreak , +// pstrUsageApplicationDebuggerBreak, +// pstrUsageSystemSpeakerMute , +// pstrUsageSystemHibernate +//}; +//const char *genDesktopTitles4[] PROGMEM = +//{ +// pstrUsageSystemDisplayInvert , +// pstrUsageSystemDisplayInternal , +// pstrUsageSystemDisplayExternal , +// pstrUsageSystemDisplayBoth , +// pstrUsageSystemDisplayDual , +// pstrUsageSystemDisplayToggleIntExt , +// pstrUsageSystemDisplaySwapPriSec , +// pstrUsageSystemDisplayLCDAutoscale +//}; +//const char *simuTitles0[] PROGMEM = +//{ +// pstrUsageFlightSimulationDevice , +// pstrUsageAutomobileSimulationDevice , +// pstrUsageTankSimulationDevice , +// pstrUsageSpaceshipSimulationDevice , +// pstrUsageSubmarineSimulationDevice , +// pstrUsageSailingSimulationDevice , +// pstrUsageMotocicleSimulationDevice , +// pstrUsageSportsSimulationDevice , +// pstrUsageAirplaneSimulationDevice , +// pstrUsageHelicopterSimulationDevice , +// pstrUsageMagicCarpetSimulationDevice, +// pstrUsageBicycleSimulationDevice +//}; +//const char *simuTitles1[] PROGMEM = +//{ +// pstrUsageFlightControlStick , +// pstrUsageFlightStick , +// pstrUsageCyclicControl , +// pstrUsageCyclicTrim , +// pstrUsageFlightYoke , +// pstrUsageTrackControl +//}; +//const char *simuTitles2[] PROGMEM = +//{ +// pstrUsageAileron , +// pstrUsageAileronTrim , +// pstrUsageAntiTorqueControl , +// pstrUsageAutopilotEnable , +// pstrUsageChaffRelease , +// pstrUsageCollectiveControl , +// pstrUsageDiveBrake , +// pstrUsageElectronicCountermeasures , +// pstrUsageElevator , +// pstrUsageElevatorTrim , +// pstrUsageRudder , +// pstrUsageThrottle , +// pstrUsageFlightCommunications , +// pstrUsageFlareRelease , +// pstrUsageLandingGear , +// pstrUsageToeBrake , +// pstrUsageTrigger , +// pstrUsageWeaponsArm , +// pstrUsageWeaponsSelect , +// pstrUsageWingFlaps , +// pstrUsageAccelerator , +// pstrUsageBrake , +// pstrUsageClutch , +// pstrUsageShifter , +// pstrUsageSteering , +// pstrUsageTurretDirection , +// pstrUsageBarrelElevation , +// pstrUsageDivePlane , +// pstrUsageBallast , +// pstrUsageBicycleCrank , +// pstrUsageHandleBars , +// pstrUsageFrontBrake , +// pstrUsageRearBrake +//}; +//const char *vrTitles0[] PROGMEM = +//{ +// pstrUsageBelt , +// pstrUsageBodySuit , +// pstrUsageFlexor , +// pstrUsageGlove , +// pstrUsageHeadTracker , +// pstrUsageHeadMountedDisplay , +// pstrUsageHandTracker , +// pstrUsageOculometer , +// pstrUsageVest , +// pstrUsageAnimatronicDevice +//}; +//const char *vrTitles1[] PROGMEM = +//{ +// pstrUsageStereoEnable , +// pstrUsageDisplayEnable +//}; +//const char *sportsCtrlTitles0[] PROGMEM = +//{ +// pstrUsageBaseballBat , +// pstrUsageGolfClub , +// pstrUsageRowingMachine , +// pstrUsageTreadmill +//}; +//const char *sportsCtrlTitles1[] PROGMEM = +//{ +// pstrUsageOar , +// pstrUsageSlope , +// pstrUsageRate , +// pstrUsageStickSpeed , +// pstrUsageStickFaceAngle , +// pstrUsageStickHeelToe , +// pstrUsageStickFollowThough , +// pstrUsageStickTempo , +// pstrUsageStickType , +// pstrUsageStickHeight +//}; +//const char *sportsCtrlTitles2[] PROGMEM = +//{ +// pstrUsagePutter , +// pstrUsage1Iron , +// pstrUsage2Iron , +// pstrUsage3Iron , +// pstrUsage4Iron , +// pstrUsage5Iron , +// pstrUsage6Iron , +// pstrUsage7Iron , +// pstrUsage8Iron , +// pstrUsage9Iron , +// pstrUsage10Iron , +// pstrUsage11Iron , +// pstrUsageSandWedge , +// pstrUsageLoftWedge , +// pstrUsagePowerWedge , +// pstrUsage1Wood , +// pstrUsage3Wood , +// pstrUsage5Wood , +// pstrUsage7Wood , +// pstrUsage9Wood +//}; +//const char *gameTitles0[] PROGMEM = +//{ +// pstrUsage3DGameController , +// pstrUsagePinballDevice , +// pstrUsageGunDevice +//}; +//const char *gameTitles1[] PROGMEM = +//{ +// pstrUsagePointOfView , +// pstrUsageTurnRightLeft , +// pstrUsagePitchForwardBackward , +// pstrUsageRollRightLeft , +// pstrUsageMoveRightLeft , +// pstrUsageMoveForwardBackward , +// pstrUsageMoveUpDown , +// pstrUsageLeanRightLeft , +// pstrUsageLeanForwardBackward , +// pstrUsageHeightOfPOV , +// pstrUsageFlipper , +// pstrUsageSecondaryFlipper , +// pstrUsageBump , +// pstrUsageNewGame , +// pstrUsageShootBall , +// pstrUsagePlayer , +// pstrUsageGunBolt , +// pstrUsageGunClip , +// pstrUsageGunSelector , +// pstrUsageGunSingleShot , +// pstrUsageGunBurst , +// pstrUsageGunAutomatic , +// pstrUsageGunSafety , +// pstrUsageGamepadFireJump , +// pstrUsageGamepadTrigger +//}; +//const char *genDevCtrlTitles[] PROGMEM = +//{ +// pstrUsageBatteryStrength, +// pstrUsageWirelessChannel, +// pstrUsageWirelessID, +// pstrUsageDiscoverWirelessControl, +// pstrUsageSecurityCodeCharEntered, +// pstrUsageSecurityCodeCharErased, +// pstrUsageSecurityCodeCleared +//}; +//const char *ledTitles[] PROGMEM = +//{ +// pstrUsageNumLock , +// pstrUsageCapsLock , +// pstrUsageScrollLock , +// pstrUsageCompose , +// pstrUsageKana , +// pstrUsagePower , +// pstrUsageShift , +// pstrUsageDoNotDisturb , +// pstrUsageMute , +// pstrUsageToneEnable , +// pstrUsageHighCutFilter , +// pstrUsageLowCutFilter , +// pstrUsageEqualizerEnable , +// pstrUsageSoundFieldOn , +// pstrUsageSurroundOn , +// pstrUsageRepeat , +// pstrUsageStereo , +// pstrUsageSamplingRateDetect , +// pstrUsageSpinning , +// pstrUsageCAV , +// pstrUsageCLV , +// pstrUsageRecordingFormatDetect , +// pstrUsageOffHook , +// pstrUsageRing , +// pstrUsageMessageWaiting , +// pstrUsageDataMode , +// pstrUsageBatteryOperation , +// pstrUsageBatteryOK , +// pstrUsageBatteryLow , +// pstrUsageSpeaker , +// pstrUsageHeadSet , +// pstrUsageHold , +// pstrUsageMicrophone , +// pstrUsageCoverage , +// pstrUsageNightMode , +// pstrUsageSendCalls , +// pstrUsageCallPickup , +// pstrUsageConference , +// pstrUsageStandBy , +// pstrUsageCameraOn , +// pstrUsageCameraOff , +// pstrUsageOnLine , +// pstrUsageOffLine , +// pstrUsageBusy , +// pstrUsageReady , +// pstrUsagePaperOut , +// pstrUsagePaperJam , +// pstrUsageRemote , +// pstrUsageForward , +// pstrUsageReverse , +// pstrUsageStop , +// pstrUsageRewind , +// pstrUsageFastForward , +// pstrUsagePlay , +// pstrUsagePause , +// pstrUsageRecord , +// pstrUsageError , +// pstrUsageSelectedIndicator , +// pstrUsageInUseIndicator , +// pstrUsageMultiModeIndicator , +// pstrUsageIndicatorOn , +// pstrUsageIndicatorFlash , +// pstrUsageIndicatorSlowBlink , +// pstrUsageIndicatorFastBlink , +// pstrUsageIndicatorOff , +// pstrUsageFlashOnTime , +// pstrUsageSlowBlinkOnTime , +// pstrUsageSlowBlinkOffTime , +// pstrUsageFastBlinkOnTime , +// pstrUsageFastBlinkOffTime , +// pstrUsageIndicatorColor , +// pstrUsageIndicatorRed , +// pstrUsageIndicatorGreen , +// pstrUsageIndicatorAmber , +// pstrUsageGenericIndicator , +// pstrUsageSystemSuspend , +// pstrUsageExternalPowerConnected +//}; +//const char *telTitles0 [] PROGMEM = +//{ +// pstrUsagePhone , +// pstrUsageAnsweringMachine , +// pstrUsageMessageControls , +// pstrUsageHandset , +// pstrUsageHeadset , +// pstrUsageTelephonyKeyPad , +// pstrUsageProgrammableButton +//}; +//const char *telTitles1 [] PROGMEM = +//{ +// pstrUsageHookSwitch , +// pstrUsageFlash , +// pstrUsageFeature , +// pstrUsageHold , +// pstrUsageRedial , +// pstrUsageTransfer , +// pstrUsageDrop , +// pstrUsagePark , +// pstrUsageForwardCalls , +// pstrUsageAlternateFunction , +// pstrUsageLine , +// pstrUsageSpeakerPhone , +// pstrUsageConference , +// pstrUsageRingEnable , +// pstrUsageRingSelect , +// pstrUsagePhoneMute , +// pstrUsageCallerID , +// pstrUsageSend +//}; +//const char *telTitles2 [] PROGMEM = +//{ +// pstrUsageSpeedDial , +// pstrUsageStoreNumber , +// pstrUsageRecallNumber , +// pstrUsagePhoneDirectory +//}; +//const char *telTitles3 [] PROGMEM = +//{ +// pstrUsageVoiceMail , +// pstrUsageScreenCalls , +// pstrUsageDoNotDisturb , +// pstrUsageMessage , +// pstrUsageAnswerOnOff +//}; +//const char *telTitles4 [] PROGMEM = +//{ +// pstrUsageInsideDialTone , +// pstrUsageOutsideDialTone , +// pstrUsageInsideRingTone , +// pstrUsageOutsideRingTone , +// pstrUsagePriorityRingTone , +// pstrUsageInsideRingback , +// pstrUsagePriorityRingback , +// pstrUsageLineBusyTone , +// pstrUsageReorderTone , +// pstrUsageCallWaitingTone , +// pstrUsageConfirmationTone1 , +// pstrUsageConfirmationTone2 , +// pstrUsageTonesOff , +// pstrUsageOutsideRingback , +// pstrUsageRinger +//}; +//const char *telTitles5 [] PROGMEM = +//{ +// pstrUsagePhoneKey0 , +// pstrUsagePhoneKey1 , +// pstrUsagePhoneKey2 , +// pstrUsagePhoneKey3 , +// pstrUsagePhoneKey4 , +// pstrUsagePhoneKey5 , +// pstrUsagePhoneKey6 , +// pstrUsagePhoneKey7 , +// pstrUsagePhoneKey8 , +// pstrUsagePhoneKey9 , +// pstrUsagePhoneKeyStar , +// pstrUsagePhoneKeyPound , +// pstrUsagePhoneKeyA , +// pstrUsagePhoneKeyB , +// pstrUsagePhoneKeyC , +// pstrUsagePhoneKeyD +//}; +//const char *consTitles0[] PROGMEM = +//{ +// pstrUsageConsumerControl, +// pstrUsageNumericKeyPad, +// pstrUsageProgrammableButton, +// pstrUsageMicrophone, +// pstrUsageHeadphone, +// pstrUsageGraphicEqualizer +//}; +//const char *consTitles1[] PROGMEM = +//{ +// pstrUsagePlus10 , +// pstrUsagePlus100, +// pstrUsageAMPM +//}; +//const char *consTitles2[] PROGMEM = +//{ +// pstrUsagePower , +// pstrUsageReset , +// pstrUsageSleep , +// pstrUsageSleepAfter , +// pstrUsageSleepMode , +// pstrUsageIllumination , +// pstrUsageFunctionButtons +// +//}; +//const char *consTitles3[] PROGMEM = +//{ +// pstrUsageMenu , +// pstrUsageMenuPick , +// pstrUsageMenuUp , +// pstrUsageMenuDown , +// pstrUsageMenuLeft , +// pstrUsageMenuRight , +// pstrUsageMenuEscape , +// pstrUsageMenuValueIncrease, +// pstrUsageMenuValueDecrease +//}; +//const char *consTitles4[] PROGMEM = +//{ +// pstrUsageDataOnScreen , +// pstrUsageClosedCaption , +// pstrUsageClosedCaptionSelect, +// pstrUsageVCRTV , +// pstrUsageBroadcastMode , +// pstrUsageSnapshot , +// pstrUsageStill +//}; +//const char *consTitles5[] PROGMEM = +//{ +// pstrUsageSelection , +// pstrUsageAssignSelection , +// pstrUsageModeStep , +// pstrUsageRecallLast , +// pstrUsageEnterChannel , +// pstrUsageOrderMovie , +// pstrUsageChannel , +// pstrUsageMediaSelection , +// pstrUsageMediaSelectComputer , +// pstrUsageMediaSelectTV , +// pstrUsageMediaSelectWWW , +// pstrUsageMediaSelectDVD , +// pstrUsageMediaSelectTelephone , +// pstrUsageMediaSelectProgramGuide , +// pstrUsageMediaSelectVideoPhone , +// pstrUsageMediaSelectGames , +// pstrUsageMediaSelectMessages , +// pstrUsageMediaSelectCD , +// pstrUsageMediaSelectVCR , +// pstrUsageMediaSelectTuner , +// pstrUsageQuit , +// pstrUsageHelp , +// pstrUsageMediaSelectTape , +// pstrUsageMediaSelectCable , +// pstrUsageMediaSelectSatellite , +// pstrUsageMediaSelectSecurity , +// pstrUsageMediaSelectHome , +// pstrUsageMediaSelectCall , +// pstrUsageChannelIncrement , +// pstrUsageChannelDecrement , +// pstrUsageMediaSelectSAP , +// pstrUsagePageReserved , +// pstrUsageVCRPlus , +// pstrUsageOnce , +// pstrUsageDaily , +// pstrUsageWeekly , +// pstrUsageMonthly +//}; +//const char *consTitles6[] PROGMEM = +//{ +// pstrUsagePlay , +// pstrUsagePause , +// pstrUsageRecord , +// pstrUsageFastForward , +// pstrUsageRewind , +// pstrUsageScanNextTrack , +// pstrUsageScanPreviousTrack , +// pstrUsageStop , +// pstrUsageEject , +// pstrUsageRandomPlay , +// pstrUsageSelectDisk , +// pstrUsageEnterDisk , +// pstrUsageRepeat , +// pstrUsageTracking , +// pstrUsageTrackNormal , +// pstrUsageSlowTracking , +// pstrUsageFrameForward , +// pstrUsageFrameBackwards , +// pstrUsageMark , +// pstrUsageClearMark , +// pstrUsageRepeatFromMark , +// pstrUsageReturnToMark , +// pstrUsageSearchMarkForward , +// pstrUsageSearchMarkBackwards , +// pstrUsageCounterReset , +// pstrUsageShowCounter , +// pstrUsageTrackingIncrement , +// pstrUsageTrackingDecrement , +// pstrUsageStopEject , +// pstrUsagePlayPause , +// pstrUsagePlaySkip +//}; +//const char *consTitles7[] PROGMEM = +//{ +// pstrUsageVolume , +// pstrUsageBalance , +// pstrUsageMute , +// pstrUsageBass , +// pstrUsageTreble , +// pstrUsageBassBoost , +// pstrUsageSurroundMode , +// pstrUsageLoudness , +// pstrUsageMPX , +// pstrUsageVolumeIncrement , +// pstrUsageVolumeDecrement +//}; +//const char *consTitles8[] PROGMEM = +//{ +// pstrUsageSpeedSelect , +// pstrUsagePlaybackSpeed , +// pstrUsageStandardPlay , +// pstrUsageLongPlay , +// pstrUsageExtendedPlay , +// pstrUsageSlow +//}; +//const char *consTitles9[] PROGMEM = +//{ +// pstrUsageFanEnable , +// pstrUsageFanSpeed , +// pstrUsageLightEnable , +// pstrUsageLightIlluminationLevel , +// pstrUsageClimateControlEnable , +// pstrUsageRoomTemperature , +// pstrUsageSecurityEnable , +// pstrUsageFireAlarm , +// pstrUsagePoliceAlarm , +// pstrUsageProximity , +// pstrUsageMotion , +// pstrUsageDuresAlarm , +// pstrUsageHoldupAlarm , +// pstrUsageMedicalAlarm +//}; +//const char *consTitlesA[] PROGMEM = +//{ +// pstrUsageBalanceRight , +// pstrUsageBalanceLeft , +// pstrUsageBassIncrement , +// pstrUsageBassDecrement , +// pstrUsageTrebleIncrement , +// pstrUsageTrebleDecrement +//}; +//const char *consTitlesB[] PROGMEM = +//{ +// pstrUsageSpeakerSystem , +// pstrUsageChannelLeft , +// pstrUsageChannelRight , +// pstrUsageChannelCenter , +// pstrUsageChannelFront , +// pstrUsageChannelCenterFront , +// pstrUsageChannelSide , +// pstrUsageChannelSurround , +// pstrUsageChannelLowFreqEnhancement , +// pstrUsageChannelTop , +// pstrUsageChannelUnknown +//}; +//const char *consTitlesC[] PROGMEM = +//{ +// pstrUsageSubChannel , +// pstrUsageSubChannelIncrement , +// pstrUsageSubChannelDecrement , +// pstrUsageAlternateAudioIncrement , +// pstrUsageAlternateAudioDecrement +//}; +//const char *consTitlesD[] PROGMEM = +//{ +// pstrUsageApplicationLaunchButtons , +// pstrUsageALLaunchButtonConfigTool , +// pstrUsageALProgrammableButton , +// pstrUsageALConsumerControlConfig , +// pstrUsageALWordProcessor , +// pstrUsageALTextEditor , +// pstrUsageALSpreadsheet , +// pstrUsageALGraphicsEditor , +// pstrUsageALPresentationApp , +// pstrUsageALDatabaseApp , +// pstrUsageALEmailReader , +// pstrUsageALNewsreader , +// pstrUsageALVoicemail , +// pstrUsageALContactsAddressBook , +// pstrUsageALCalendarSchedule , +// pstrUsageALTaskProjectManager , +// pstrUsageALLogJournalTimecard , +// pstrUsageALCheckbookFinance , +// pstrUsageALCalculator , +// pstrUsageALAVCapturePlayback , +// pstrUsageALLocalMachineBrowser , +// pstrUsageALLANWANBrow , +// pstrUsageALInternetBrowser , +// pstrUsageALRemoteNetISPConnect , +// pstrUsageALNetworkConference , +// pstrUsageALNetworkChat , +// pstrUsageALTelephonyDialer , +// pstrUsageALLogon , +// pstrUsageALLogoff , +// pstrUsageALLogonLogoff , +// pstrUsageALTermLockScrSav , +// pstrUsageALControlPannel , +// pstrUsageALCommandLineProcessorRun , +// pstrUsageALProcessTaskManager , +// pstrUsageALSelectTaskApplication , +// pstrUsageALNextTaskApplication , +// pstrUsageALPreviousTaskApplication , +// pstrUsageALPreemptiveHaltTaskApp , +// pstrUsageALIntegratedHelpCenter , +// pstrUsageALDocuments , +// pstrUsageALThesaurus , +// pstrUsageALDictionary , +// pstrUsageALDesktop , +// pstrUsageALSpellCheck , +// pstrUsageALGrammarCheck , +// pstrUsageALWirelessStatus , +// pstrUsageALKeyboardLayout , +// pstrUsageALVirusProtection , +// pstrUsageALEncryption , +// pstrUsageALScreenSaver , +// pstrUsageALAlarms , +// pstrUsageALClock , +// pstrUsageALFileBrowser , +// pstrUsageALPowerStatus , +// pstrUsageALImageBrowser , +// pstrUsageALAudioBrowser , +// pstrUsageALMovieBrowser , +// pstrUsageALDigitalRightsManager , +// pstrUsageALDigitalWallet , +// pstrUsagePageReserved , +// pstrUsageALInstantMessaging , +// pstrUsageALOEMFeaturesBrowser , +// pstrUsageALOEMHelp , +// pstrUsageALOnlineCommunity , +// pstrUsageALEntertainmentContentBrow , +// pstrUsageALOnlineShoppingBrowser , +// pstrUsageALSmartCardInfoHelp , +// pstrUsageALMarketMonitorFinBrowser , +// pstrUsageALCustomCorpNewsBrowser , +// pstrUsageALOnlineActivityBrowser , +// pstrUsageALResearchSearchBrowser , +// pstrUsageALAudioPlayer +//}; +//const char *consTitlesE[] PROGMEM = +//{ +// pstrUsageGenericGUIAppControls , +// pstrUsageACNew , +// pstrUsageACOpen , +// pstrUsageACClose , +// pstrUsageACExit , +// pstrUsageACMaximize , +// pstrUsageACMinimize , +// pstrUsageACSave , +// pstrUsageACPrint , +// pstrUsageACProperties , +// pstrUsageACUndo , +// pstrUsageACCopy , +// pstrUsageACCut , +// pstrUsageACPaste , +// pstrUsageACSelectAll , +// pstrUsageACFind , +// pstrUsageACFindAndReplace , +// pstrUsageACSearch , +// pstrUsageACGoto , +// pstrUsageACHome , +// pstrUsageACBack , +// pstrUsageACForward , +// pstrUsageACStop , +// pstrUsageACRefresh , +// pstrUsageACPreviousLink , +// pstrUsageACNextLink , +// pstrUsageACBookmarks , +// pstrUsageACHistory , +// pstrUsageACSubscriptions , +// pstrUsageACZoomIn , +// pstrUsageACZoomOut , +// pstrUsageACZoom , +// pstrUsageACFullScreenView , +// pstrUsageACNormalView , +// pstrUsageACViewToggle , +// pstrUsageACScrollUp , +// pstrUsageACScrollDown , +// pstrUsageACScroll , +// pstrUsageACPanLeft , +// pstrUsageACPanRight , +// pstrUsageACPan , +// pstrUsageACNewWindow , +// pstrUsageACTileHoriz , +// pstrUsageACTileVert , +// pstrUsageACFormat , +// pstrUsageACEdit , +// pstrUsageACBold , +// pstrUsageACItalics , +// pstrUsageACUnderline , +// pstrUsageACStrikethrough , +// pstrUsageACSubscript , +// pstrUsageACSuperscript , +// pstrUsageACAllCaps , +// pstrUsageACRotate , +// pstrUsageACResize , +// pstrUsageACFlipHorizontal , +// pstrUsageACFlipVertical , +// pstrUsageACMirrorHorizontal , +// pstrUsageACMirrorVertical , +// pstrUsageACFontSelect , +// pstrUsageACFontColor , +// pstrUsageACFontSize , +// pstrUsageACJustifyLeft , +// pstrUsageACJustifyCenterH , +// pstrUsageACJustifyRight , +// pstrUsageACJustifyBlockH , +// pstrUsageACJustifyTop , +// pstrUsageACJustifyCenterV , +// pstrUsageACJustifyBottom , +// pstrUsageACJustifyBlockV , +// pstrUsageACIndentDecrease , +// pstrUsageACIndentIncrease , +// pstrUsageACNumberedList , +// pstrUsageACRestartNumbering , +// pstrUsageACBulletedList , +// pstrUsageACPromote , +// pstrUsageACDemote , +// pstrUsageACYes , +// pstrUsageACNo , +// pstrUsageACCancel , +// pstrUsageACCatalog , +// pstrUsageACBuyChkout , +// pstrUsageACAddToCart , +// pstrUsageACExpand , +// pstrUsageACExpandAll , +// pstrUsageACCollapse , +// pstrUsageACCollapseAll , +// pstrUsageACPrintPreview , +// pstrUsageACPasteSpecial , +// pstrUsageACInsertMode , +// pstrUsageACDelete , +// pstrUsageACLock , +// pstrUsageACUnlock , +// pstrUsageACProtect , +// pstrUsageACUnprotect , +// pstrUsageACAttachComment , +// pstrUsageACDeleteComment , +// pstrUsageACViewComment , +// pstrUsageACSelectWord , +// pstrUsageACSelectSentence , +// pstrUsageACSelectParagraph , +// pstrUsageACSelectColumn , +// pstrUsageACSelectRow , +// pstrUsageACSelectTable , +// pstrUsageACSelectObject , +// pstrUsageACRedoRepeat , +// pstrUsageACSort , +// pstrUsageACSortAscending , +// pstrUsageACSortDescending , +// pstrUsageACFilter , +// pstrUsageACSetClock , +// pstrUsageACViewClock , +// pstrUsageACSelectTimeZone , +// pstrUsageACEditTimeZone , +// pstrUsageACSetAlarm , +// pstrUsageACClearAlarm , +// pstrUsageACSnoozeAlarm , +// pstrUsageACResetAlarm , +// pstrUsageACSyncronize , +// pstrUsageACSendReceive , +// pstrUsageACSendTo , +// pstrUsageACReply , +// pstrUsageACReplyAll , +// pstrUsageACForwardMessage , +// pstrUsageACSend , +// pstrUsageACAttachFile , +// pstrUsageACUpload , +// pstrUsageACDownload , +// pstrUsageACSetBorders , +// pstrUsageACInsertRow , +// pstrUsageACInsertColumn , +// pstrUsageACInsertFile , +// pstrUsageACInsertPicture , +// pstrUsageACInsertObject , +// pstrUsageACInsertSymbol , +// pstrUsageACSaveAndClose , +// pstrUsageACRename , +// pstrUsageACMerge , +// pstrUsageACSplit , +// pstrUsageACDistributeHorizontaly , +// pstrUsageACDistributeVerticaly +//}; +//const char *digitTitles0[] PROGMEM = +//{ +// pstrUsageDigitizer , +// pstrUsagePen , +// pstrUsageLightPen , +// pstrUsageTouchScreen , +// pstrUsageTouchPad , +// pstrUsageWhiteBoard , +// pstrUsageCoordinateMeasuringMachine , +// pstrUsage3DDigitizer , +// pstrUsageStereoPlotter , +// pstrUsageArticulatedArm , +// pstrUsageArmature , +// pstrUsageMultiplePointDigitizer , +// pstrUsageFreeSpaceWand +//}; +//const char *digitTitles1[] PROGMEM = +//{ +// pstrUsageStylus , +// pstrUsagePuck , +// pstrUsageFinger +// +//}; +//const char *digitTitles2[] PROGMEM = +//{ +// pstrUsageTipPressure , +// pstrUsageBarrelPressure , +// pstrUsageInRange , +// pstrUsageTouch , +// pstrUsageUntouch , +// pstrUsageTap , +// pstrUsageQuality , +// pstrUsageDataValid , +// pstrUsageTransducerIndex , +// pstrUsageTabletFunctionKeys , +// pstrUsageProgramChangeKeys , +// pstrUsageBatteryStrength , +// pstrUsageInvert , +// pstrUsageXTilt , +// pstrUsageYTilt , +// pstrUsageAzimuth , +// pstrUsageAltitude , +// pstrUsageTwist , +// pstrUsageTipSwitch , +// pstrUsageSecondaryTipSwitch , +// pstrUsageBarrelSwitch , +// pstrUsageEraser , +// pstrUsageTabletPick +//}; +//const char *aplphanumTitles0[] PROGMEM = +//{ +// pstrUsageAlphanumericDisplay, +// pstrUsageBitmappedDisplay +//}; +//const char *aplphanumTitles1[] PROGMEM = +//{ +// pstrUsageDisplayAttributesReport , +// pstrUsageASCIICharacterSet , +// pstrUsageDataReadBack , +// pstrUsageFontReadBack , +// pstrUsageDisplayControlReport , +// pstrUsageClearDisplay , +// pstrUsageDisplayEnable , +// pstrUsageScreenSaverDelay , +// pstrUsageScreenSaverEnable , +// pstrUsageVerticalScroll , +// pstrUsageHorizontalScroll , +// pstrUsageCharacterReport , +// pstrUsageDisplayData , +// pstrUsageDisplayStatus , +// pstrUsageStatusNotReady , +// pstrUsageStatusReady , +// pstrUsageErrorNotALoadableCharacter , +// pstrUsageErrorFotDataCanNotBeRead , +// pstrUsageCursorPositionReport , +// pstrUsageRow , +// pstrUsageColumn , +// pstrUsageRows , +// pstrUsageColumns , +// pstrUsageCursorPixelPosition , +// pstrUsageCursorMode , +// pstrUsageCursorEnable , +// pstrUsageCursorBlink , +// pstrUsageFontReport , +// pstrUsageFontData , +// pstrUsageCharacterWidth , +// pstrUsageCharacterHeight , +// pstrUsageCharacterSpacingHorizontal , +// pstrUsageCharacterSpacingVertical , +// pstrUsageUnicodeCharset , +// pstrUsageFont7Segment , +// pstrUsage7SegmentDirectMap , +// pstrUsageFont14Segment , +// pstrUsage14SegmentDirectMap , +// pstrUsageDisplayBrightness , +// pstrUsageDisplayContrast , +// pstrUsageCharacterAttribute , +// pstrUsageAttributeReadback , +// pstrUsageAttributeData , +// pstrUsageCharAttributeEnhance , +// pstrUsageCharAttributeUnderline , +// pstrUsageCharAttributeBlink +//}; +//const char *aplphanumTitles2[] PROGMEM = +//{ +// pstrUsageBitmapSizeX , +// pstrUsageBitmapSizeY , +// pstrUsagePageReserved , +// pstrUsageBitDepthFormat , +// pstrUsageDisplayOrientation , +// pstrUsagePaletteReport , +// pstrUsagePaletteDataSize , +// pstrUsagePaletteDataOffset , +// pstrUsagePaletteData , +// pstrUsageBlitReport , +// pstrUsageBlitRectangleX1 , +// pstrUsageBlitRectangleY1 , +// pstrUsageBlitRectangleX2 , +// pstrUsageBlitRectangleY2 , +// pstrUsageBlitData , +// pstrUsageSoftButton , +// pstrUsageSoftButtonID , +// pstrUsageSoftButtonSide , +// pstrUsageSoftButtonOffset1 , +// pstrUsageSoftButtonOffset2 , +// pstrUsageSoftButtonReport +//}; +//const char *medInstrTitles0[] PROGMEM = +//{ +// pstrUsageVCRAcquisition , +// pstrUsageFreezeThaw , +// pstrUsageClipStore , +// pstrUsageUpdate , +// pstrUsageNext , +// pstrUsageSave , +// pstrUsagePrint , +// pstrUsageMicrophoneEnable +//}; +//const char *medInstrTitles1[] PROGMEM = +//{ +// pstrUsageCine , +// pstrUsageTransmitPower , +// pstrUsageVolume , +// pstrUsageFocus , +// pstrUsageDepth +//}; +//const char *medInstrTitles2[] PROGMEM = +//{ +// pstrUsageSoftStepPrimary , +// pstrUsageSoftStepSecondary +//}; +//const char *medInstrTitles3[] PROGMEM = +//{ +// pstrUsageZoomSelect , +// pstrUsageZoomAdjust , +// pstrUsageSpectralDopplerModeSelect , +// pstrUsageSpectralDopplerModeAdjust , +// pstrUsageColorDopplerModeSelect , +// pstrUsageColorDopplerModeAdjust , +// pstrUsageMotionModeSelect , +// pstrUsageMotionModeAdjust , +// pstrUsage2DModeSelect , +// pstrUsage2DModeAdjust +//}; +//const char *medInstrTitles4[] PROGMEM = +//{ +// pstrUsageSoftControlSelect , +// pstrUsageSoftControlAdjust +//}; + +#endif // __HIDUSAGETITLEARRAYS_H__ diff --git a/libraries/USB_Host_Shield/keywords.txt b/libraries/USB_Host_Shield/keywords.txt new file mode 100755 index 0000000..178d52d --- /dev/null +++ b/libraries/USB_Host_Shield/keywords.txt @@ -0,0 +1,361 @@ +#################################################### +# Syntax Coloring Map For USB Library +#################################################### + +#################################################### +# Datatypes (KEYWORD1) +#################################################### + +USB KEYWORD1 +USBHub KEYWORD1 + +#################################################### +# Syntax Coloring Map For BTD (Bluetooth) Library +#################################################### + +#################################################### +# Datatypes (KEYWORD1) +#################################################### + +BTD KEYWORD1 + +#################################################### +# Methods and Functions (KEYWORD2) +#################################################### +Task KEYWORD2 + +#################################################### +# Syntax Coloring Map For PS3/PS4 Bluetooth/USB Library +#################################################### + +#################################################### +# Datatypes (KEYWORD1) +#################################################### + +PS3BT KEYWORD1 +PS3USB KEYWORD1 +PS4BT KEYWORD1 +PS4USB KEYWORD1 + +#################################################### +# Methods and Functions (KEYWORD2) +#################################################### +setBdaddr KEYWORD2 +getBdaddr KEYWORD2 +setMoveBdaddr KEYWORD2 +getMoveBdaddr KEYWORD2 +getMoveCalibration KEYWORD2 + +getButtonPress KEYWORD2 +getButtonClick KEYWORD2 +getAnalogButton KEYWORD2 +getAnalogHat KEYWORD2 +getSensor KEYWORD2 +getAngle KEYWORD2 +get9DOFValues KEYWORD2 +getStatus KEYWORD2 +printStatusString KEYWORD2 +getTemperature KEYWORD2 +disconnect KEYWORD2 + +setAllOff KEYWORD2 +setRumbleOff KEYWORD2 +setRumbleOn KEYWORD2 +setLedOff KEYWORD2 +setLedOn KEYWORD2 +setLedToggle KEYWORD2 +setLedFlash KEYWORD2 +moveSetBulb KEYWORD2 +moveSetRumble KEYWORD2 + +attachOnInit KEYWORD2 + +PS3Connected KEYWORD2 +PS3MoveConnected KEYWORD2 +PS3NavigationConnected KEYWORD2 + +isReady KEYWORD2 +watingForConnection KEYWORD2 + +isTouching KEYWORD2 +getX KEYWORD2 +getY KEYWORD2 +getTouchCounter KEYWORD2 + +getUsbStatus KEYWORD2 +getAudioStatus KEYWORD2 +getMicStatus KEYWORD2 + +#################################################### +# Constants and enums (LITERAL1) +#################################################### +OFF LITERAL1 +LED1 LITERAL1 +LED2 LITERAL1 +LED3 LITERAL1 +LED4 LITERAL1 +LED5 LITERAL1 +LED6 LITERAL1 +LED7 LITERAL1 +LED8 LITERAL1 +LED9 LITERAL1 +LED10 LITERAL1 + +Red LITERAL1 +Green LITERAL1 +Blue LITERAL1 +Yellow LITERAL1 +Lightblue LITERAL1 +Purble LITERAL1 +White LITERAL1 +Off LITERAL1 + +SELECT LITERAL1 +L3 LITERAL1 +R3 LITERAL1 +START LITERAL1 +UP LITERAL1 +RIGHT LITERAL1 +DOWN LITERAL1 +LEFT LITERAL1 +L2 LITERAL1 +R2 LITERAL1 +L1 LITERAL1 +R1 LITERAL1 +TRIANGLE LITERAL1 +CIRCLE LITERAL1 +CROSS LITERAL1 +SQUARE LITERAL1 +PS LITERAL1 +MOVE LITERAL1 +T LITERAL1 + +SHARE LITERAL1 +OPTIONS LITERAL1 +TOUCHPAD LITERAL1 + +LeftHatX LITERAL1 +LeftHatY LITERAL1 +RightHatX LITERAL1 +RightHatY LITERAL1 + +aX LITERAL1 +aY LITERAL1 +aZ LITERAL1 +gX LITERAL1 +gY LITERAL1 +gZ LITERAL1 +aXmove LITERAL1 +aYmove LITERAL1 +aZmove LITERAL1 +gXmove LITERAL1 +gYmove LITERAL1 +gZmove LITERAL1 +tempMove LITERAL1 +mXmove LITERAL1 +mZmove LITERAL1 +mYmove LITERAL1 + +Pitch LITERAL1 +Roll LITERAL1 + +Plugged LITERAL1 +Unplugged LITERAL1 +Charging LITERAL1 +NotCharging LITERAL1 +Shutdown LITERAL1 +Dying LITERAL1 +Low LITERAL1 +High LITERAL1 +Full LITERAL1 +MoveCharging LITERAL1 +MoveNotCharging LITERAL1 +MoveShutdown LITERAL1 +MoveDying LITERAL1 +MoveLow LITERAL1 +MoveHigh LITERAL1 +MoveFull LITERAL1 +CableRumble LITERAL1 +Cable LITERAL1 +BluetoothRumble LITERAL1 +Bluetooth LITERAL1 + +RumbleHigh LITERAL1 +RumbleLow LITERAL1 + +#################################################### +# Syntax Coloring Map For Xbox 360 Libraries +#################################################### + +#################################################### +# Datatypes (KEYWORD1) +#################################################### + +XBOXUSB KEYWORD1 +XBOXOLD KEYWORD1 +XBOXRECV KEYWORD1 + +#################################################### +# Methods and Functions (KEYWORD2) +#################################################### + +setLedRaw KEYWORD2 +setLedBlink KEYWORD2 +setLedMode KEYWORD2 +getBatteryLevel KEYWORD2 +buttonChanged KEYWORD2 + +XboxReceiverConnected KEYWORD2 +Xbox360Connected KEYWORD2 + +#################################################### +# Constants and enums (LITERAL1) +#################################################### + +ALL LITERAL1 + +ROTATING LITERAL1 +FASTBLINK LITERAL1 +SLOWBLINK LITERAL1 +ALTERNATING LITERAL1 + +BACK LITERAL1 + +XBOX LITERAL1 +SYNC LITERAL1 + +BLACK LITERAL1 +WHITE LITERAL1 + +A LITERAL1 +B LITERAL1 +X LITERAL1 +Y LITERAL1 + +#################################################### +# Syntax Coloring Map For RFCOMM/SPP Library +#################################################### + +#################################################### +# Datatypes (KEYWORD1) +#################################################### + +SPP KEYWORD1 + +#################################################### +# Methods and Functions (KEYWORD2) +#################################################### + +connected KEYWORD2 +discard KEYWORD2 + +#################################################### +# Syntax Coloring Map For Wiimote Library +#################################################### + +#################################################### +# Datatypes (KEYWORD1) +#################################################### + +WII KEYWORD1 + +#################################################### +# Methods and Functions (KEYWORD2) +#################################################### + +wiimoteConnected KEYWORD2 +nunchuckConnected KEYWORD2 +motionPlusConnected KEYWORD2 +wiiUProControllerConnected KEYWORD2 +setRumbleToggle KEYWORD2 +getPitch KEYWORD2 +getRoll KEYWORD2 +getYaw KEYWORD2 +getWiimotePitch KEYWORD2 +getWiimoteRoll KEYWORD2 +getNunchuckPitch KEYWORD2 +getNunchuckRoll KEYWORD2 +PAIR KEYWORD2 +statusRequest KEYWORD2 +getBatteryLevel KEYWORD2 +getWiiState KEYWORD2 + +#################################################### +# Constants and enums (LITERAL1) +#################################################### + +PLUS LITERAL1 +MINUS LITERAL1 +ONE LITERAL1 +TWO LITERAL1 +HOME LITERAL1 +Z LITERAL1 +C LITERAL1 +L LITERAL1 +R LITERAL1 +ZL LITERAL1 +ZR LITERAL1 +HatX LITERAL1 +HatY LITERAL1 + +#################################################### +# Methods and Functions for the IR Camera +#################################################### + +IRinitialize KEYWORD2 +isIRCameraEnabled KEYWORD2 +getIRx1 KEYWORD2 +getIRy1 KEYWORD2 +getIRs1 KEYWORD2 +getIRx2 KEYWORD2 +getIRy2 KEYWORD2 +getIRs2 KEYWORD2 +getIRx3 KEYWORD2 +getIRy3 KEYWORD2 +getIRs3 KEYWORD2 +getIRx4 KEYWORD2 +getIRy4 KEYWORD2 +getIRs4 KEYWORD2 + +#################################################### +# Syntax Coloring Map For BTHID Library +#################################################### + +#################################################### +# Datatypes (KEYWORD1) +#################################################### + +BTHID KEYWORD1 + +#################################################### +# Methods and Functions (KEYWORD2) +#################################################### +SetReportParser KEYWORD2 +setProtocolMode KEYWORD2 + +#################################################### +# Syntax Coloring Map For PS Buzz Library +#################################################### + +#################################################### +# Datatypes (KEYWORD1) +#################################################### + +PSBuzz KEYWORD1 + +#################################################### +# Methods and Functions (KEYWORD2) +#################################################### + +setLedOnAll KEYWORD2 +setLedOffAll KEYWORD2 + +#################################################### +# Constants and enums (LITERAL1) +#################################################### + +RED LITERAL1 +YELLOW LITERAL1 +GREEN LITERAL1 +ORANGE LITERAL1 +BLUE LITERAL1 diff --git a/libraries/USB_Host_Shield/library.json b/libraries/USB_Host_Shield/library.json new file mode 100755 index 0000000..6f7c575 --- /dev/null +++ b/libraries/USB_Host_Shield/library.json @@ -0,0 +1,46 @@ +{ + "name": "USB-Host-Shield-20", + "keywords": "usb, host, ftdi, adk, acm, pl2303, hid, bluetooth, spp, ps3, ps4, buzz, xbox, wii, mass storage", + "description": "Revision 2.0 of MAX3421E-based USB Host Shield Library", + "authors": + [ + { + "name": "Oleg Mazurov", + "email": "mazurov@circuitsathome.com", + "url": "http://www.circuitsathome.com", + "maintainer": true + }, + { + "name": "Alexei Glushchenko", + "email": "alex-gl@mail.ru" + }, + { + "name": "Kristian Lauszus", + "email": "kristianl@tkjelectronics.com", + "url": "http://tkjelectronics.com", + "maintainer": true + }, + { + "name": "Andrew Kroll", + "email": "xxxajk@gmail.com", + "maintainer": true + } + ], + "repository": + { + "type": "git", + "url": "https://github.com/felis/USB_Host_Shield_2.0.git" + }, + "examples": + [ + "examples/*/*.ino", + "examples/*/*/*.ino" + ], + "frameworks": "arduino", + "platforms": + [ + "atmelavr", + "teensy", + "atmelsam" + ] +} diff --git a/libraries/USB_Host_Shield/macros.h b/libraries/USB_Host_Shield/macros.h new file mode 100755 index 0000000..e14a711 --- /dev/null +++ b/libraries/USB_Host_Shield/macros.h @@ -0,0 +1,82 @@ +/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved. + +This software may be distributed and modified under the terms of the GNU +General Public License version 2 (GPL2) as published by the Free Software +Foundation and appearing in the file GPL2.TXT included in the packaging of +this file. Please note that GPL2 Section 2[b] requires that all works based +on this software must also be made publicly available under the terms of +the GPL2 ("Copyleft"). + +Contact information +------------------- + +Circuits At Home, LTD +Web : http://www.circuitsathome.com +e-mail : support@circuitsathome.com + */ + +#if !defined(_usb_h_) || defined(MACROS_H) +#error "Never include macros.h directly; include Usb.h instead" +#else +#define MACROS_H + +//////////////////////////////////////////////////////////////////////////////// +// HANDY MACROS +//////////////////////////////////////////////////////////////////////////////// + +#define VALUE_BETWEEN(v,l,h) (((v)>(l)) && ((v)<(h))) +#define VALUE_WITHIN(v,l,h) (((v)>=(l)) && ((v)<=(h))) +#define output_pgm_message(wa,fp,mp,el) wa = &mp, fp((char *)pgm_read_pointer(wa), el) +#define output_if_between(v,l,h,wa,fp,mp,el) if(VALUE_BETWEEN(v,l,h)) output_pgm_message(wa,fp,mp[v-(l+1)],el); + +#define SWAP(a, b) (((a) ^= (b)), ((b) ^= (a)), ((a) ^= (b))) +#ifndef __BYTE_GRABBING_DEFINED__ +#define __BYTE_GRABBING_DEFINED__ 1 +#ifdef BROKEN_OPTIMIZER_LITTLE_ENDIAN +// Note: Use this if your compiler generates horrible assembler! +#define BGRAB0(__usi__) (((uint8_t *)&(__usi__))[0]) +#define BGRAB1(__usi__) (((uint8_t *)&(__usi__))[1]) +#define BGRAB2(__usi__) (((uint8_t *)&(__usi__))[2]) +#define BGRAB3(__usi__) (((uint8_t *)&(__usi__))[3]) +#define BGRAB4(__usi__) (((uint8_t *)&(__usi__))[4]) +#define BGRAB5(__usi__) (((uint8_t *)&(__usi__))[5]) +#define BGRAB6(__usi__) (((uint8_t *)&(__usi__))[6]) +#define BGRAB7(__usi__) (((uint8_t *)&(__usi__))[7]) +#else +// Note: The cast alone to uint8_t is actually enough. +// GCC throws out the "& 0xff", and the size is no different. +// Some compilers need it. +#define BGRAB0(__usi__) ((uint8_t)((__usi__) & 0xff )) +#define BGRAB1(__usi__) ((uint8_t)(((__usi__) >> 8) & 0xff)) +#define BGRAB2(__usi__) ((uint8_t)(((__usi__) >> 16) & 0xff)) +#define BGRAB3(__usi__) ((uint8_t)(((__usi__) >> 24) & 0xff)) +#define BGRAB4(__usi__) ((uint8_t)(((__usi__) >> 32) & 0xff)) +#define BGRAB5(__usi__) ((uint8_t)(((__usi__) >> 40) & 0xff)) +#define BGRAB6(__usi__) ((uint8_t)(((__usi__) >> 48) & 0xff)) +#define BGRAB7(__usi__) ((uint8_t)(((__usi__) >> 56) & 0xff)) +#endif +#define BOVER1(__usi__) ((uint16_t)(__usi__) << 8) +#define BOVER2(__usi__) ((uint32_t)(__usi__) << 16) +#define BOVER3(__usi__) ((uint32_t)(__usi__) << 24) +#define BOVER4(__usi__) ((uint64_t)(__usi__) << 32) +#define BOVER5(__usi__) ((uint64_t)(__usi__) << 40) +#define BOVER6(__usi__) ((uint64_t)(__usi__) << 48) +#define BOVER7(__usi__) ((uint64_t)(__usi__) << 56) + +// These are the smallest and fastest ways I have found so far in pure C/C++. +#define BMAKE16(__usc1__,__usc0__) ((uint16_t)((uint16_t)(__usc0__) | (uint16_t)BOVER1(__usc1__))) +#define BMAKE32(__usc3__,__usc2__,__usc1__,__usc0__) ((uint32_t)((uint32_t)(__usc0__) | (uint32_t)BOVER1(__usc1__) | (uint32_t)BOVER2(__usc2__) | (uint32_t)BOVER3(__usc3__))) +#define BMAKE64(__usc7__,__usc6__,__usc5__,__usc4__,__usc3__,__usc2__,__usc1__,__usc0__) ((uint64_t)((uint64_t)__usc0__ | (uint64_t)BOVER1(__usc1__) | (uint64_t)BOVER2(__usc2__) | (uint64_t)BOVER3(__usc3__) | (uint64_t)BOVER4(__usc4__) | (uint64_t)BOVER5(__usc5__) | (uint64_t)BOVER6(__usc6__) | (uint64_t)BOVER1(__usc7__))) +#endif + +/* + * Debug macros: Strings are stored in progmem (flash) instead of RAM. + */ +#define USBTRACE(s) (Notify(PSTR(s), 0x80)) +#define USBTRACE1(s,l) (Notify(PSTR(s), l)) +#define USBTRACE2(s,r) (Notify(PSTR(s), 0x80), D_PrintHex((r), 0x80), Notify(PSTR("\r\n"), 0x80)) +#define USBTRACE3(s,r,l) (Notify(PSTR(s), l), D_PrintHex((r), l), Notify(PSTR("\r\n"), l)) + + +#endif /* MACROS_H */ + diff --git a/libraries/USB_Host_Shield/masstorage.cpp b/libraries/USB_Host_Shield/masstorage.cpp new file mode 100755 index 0000000..9299f71 --- /dev/null +++ b/libraries/USB_Host_Shield/masstorage.cpp @@ -0,0 +1,1266 @@ +/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved. + +This software may be distributed and modified under the terms of the GNU +General Public License version 2 (GPL2) as published by the Free Software +Foundation and appearing in the file GPL2.TXT included in the packaging of +this file. Please note that GPL2 Section 2[b] requires that all works based +on this software must also be made publicly available under the terms of +the GPL2 ("Copyleft"). + +Contact information +------------------- + +Circuits At Home, LTD +Web : http://www.circuitsathome.com +e-mail : support@circuitsathome.com + */ + +#include "masstorage.h" + +const uint8_t BulkOnly::epDataInIndex = 1; +const uint8_t BulkOnly::epDataOutIndex = 2; +const uint8_t BulkOnly::epInterruptInIndex = 3; + +//////////////////////////////////////////////////////////////////////////////// + +// Interface code + +//////////////////////////////////////////////////////////////////////////////// + +/** + * Get the capacity of the media + * + * @param lun Logical Unit Number + * @return media capacity + */ +uint32_t BulkOnly::GetCapacity(uint8_t lun) { + if(LUNOk[lun]) + return CurrentCapacity[lun]; + return 0LU; +} + +/** + * Get the sector (block) size used on the media + * + * @param lun Logical Unit Number + * @return media sector size + */ +uint16_t BulkOnly::GetSectorSize(uint8_t lun) { + if(LUNOk[lun]) + return CurrentSectorSize[lun]; + return 0U; +} + +/** + * Test if LUN is ready for use + * + * @param lun Logical Unit Number + * @return true if LUN is ready for use + */ +bool BulkOnly::LUNIsGood(uint8_t lun) { + return LUNOk[lun]; +} + +/** + * Test if LUN is write protected + * + * @param lun Logical Unit Number + * @return cached status of write protect switch + */ +bool BulkOnly::WriteProtected(uint8_t lun) { + return WriteOk[lun]; +} + +/** + * Wrap and execute a SCSI CDB with length of 6 + * + * @param cdb CDB to execute + * @param buf_size Size of expected transaction + * @param buf Buffer + * @param dir MASS_CMD_DIR_IN | MASS_CMD_DIR_OUT + * @return + */ +uint8_t BulkOnly::SCSITransaction6(CDB6_t *cdb, uint16_t buf_size, void *buf, uint8_t dir) { + // promote buf_size to 32bits. + CommandBlockWrapper cbw = CommandBlockWrapper(++dCBWTag, (uint32_t)buf_size, cdb, dir); + //SetCurLUN(cdb->LUN); + return (HandleSCSIError(Transaction(&cbw, buf_size, buf))); +} + +/** + * Wrap and execute a SCSI CDB with length of 10 + * + * @param cdb CDB to execute + * @param buf_size Size of expected transaction + * @param buf Buffer + * @param dir MASS_CMD_DIR_IN | MASS_CMD_DIR_OUT + * @return + */ +uint8_t BulkOnly::SCSITransaction10(CDB10_t *cdb, uint16_t buf_size, void *buf, uint8_t dir) { + // promote buf_size to 32bits. + CommandBlockWrapper cbw = CommandBlockWrapper(++dCBWTag, (uint32_t)buf_size, cdb, dir); + //SetCurLUN(cdb->LUN); + return (HandleSCSIError(Transaction(&cbw, buf_size, buf))); +} + +/** + * Lock or Unlock the tray or door on device. + * Caution: Some devices with buggy firmware will lock up. + * + * @param lun Logical Unit Number + * @param lock 1 to lock, 0 to unlock + * @return + */ +uint8_t BulkOnly::LockMedia(uint8_t lun, uint8_t lock) { + Notify(PSTR("\r\nLockMedia\r\n"), 0x80); + Notify(PSTR("---------\r\n"), 0x80); + + CDB6_t cdb = CDB6_t(SCSI_CMD_PREVENT_REMOVAL, lun, (uint8_t)0, lock); + return SCSITransaction6(&cdb, (uint16_t)0, NULL, (uint8_t)MASS_CMD_DIR_IN); +} + +/** + * Media control, for spindle motor and media tray or door. + * This includes CDROM, TAPE and anything with a media loader. + * + * @param lun Logical Unit Number + * @param ctl 0x00 Stop Motor, 0x01 Start Motor, 0x02 Eject Media, 0x03 Load Media + * @return 0 on success + */ +uint8_t BulkOnly::MediaCTL(uint8_t lun, uint8_t ctl) { + Notify(PSTR("\r\nMediaCTL\r\n"), 0x80); + Notify(PSTR("-----------------\r\n"), 0x80); + + uint8_t rcode = MASS_ERR_UNIT_NOT_READY; + if(bAddress) { + CDB6_t cdb = CDB6_t(SCSI_CMD_START_STOP_UNIT, lun, ctl & 0x03, 0); + rcode = SCSITransaction6(&cdb, (uint16_t)0, NULL, (uint8_t)MASS_CMD_DIR_OUT); + } else { + SetCurLUN(lun); + } + return rcode; +} + +/** + * Read data from media + * + * @param lun Logical Unit Number + * @param addr LBA address on media to read + * @param bsize size of a block (we should probably use the cached size) + * @param blocks how many blocks to read + * @param buf memory that is able to hold the requested data + * @return 0 on success + */ +uint8_t BulkOnly::Read(uint8_t lun, uint32_t addr, uint16_t bsize, uint8_t blocks, uint8_t *buf) { + if(!LUNOk[lun]) return MASS_ERR_NO_MEDIA; + Notify(PSTR("\r\nRead LUN:\t"), 0x80); + D_PrintHex (lun, 0x90); + Notify(PSTR("\r\nLBA:\t\t"), 0x90); + D_PrintHex (addr, 0x90); + Notify(PSTR("\r\nblocks:\t\t"), 0x90); + D_PrintHex (blocks, 0x90); + Notify(PSTR("\r\nblock size:\t"), 0x90); + D_PrintHex (bsize, 0x90); + Notify(PSTR("\r\n---------\r\n"), 0x80); + CDB10_t cdb = CDB10_t(SCSI_CMD_READ_10, lun, blocks, addr); + +again: + uint8_t er = SCSITransaction10(&cdb, ((uint16_t)bsize * blocks), buf, (uint8_t)MASS_CMD_DIR_IN); + + if(er == MASS_ERR_STALL) { + MediaCTL(lun, 1); + delay(150); + if(!TestUnitReady(lun)) goto again; + } + return er; +} + +/** + * Write data to media + * + * @param lun Logical Unit Number + * @param addr LBA address on media to write + * @param bsize size of a block (we should probably use the cached size) + * @param blocks how many blocks to write + * @param buf memory that contains the data to write + * @return 0 on success + */ +uint8_t BulkOnly::Write(uint8_t lun, uint32_t addr, uint16_t bsize, uint8_t blocks, const uint8_t * buf) { + if(!LUNOk[lun]) return MASS_ERR_NO_MEDIA; + if(!WriteOk[lun]) return MASS_ERR_WRITE_PROTECTED; + Notify(PSTR("\r\nWrite LUN:\t"), 0x80); + D_PrintHex (lun, 0x90); + Notify(PSTR("\r\nLBA:\t\t"), 0x90); + D_PrintHex (addr, 0x90); + Notify(PSTR("\r\nblocks:\t\t"), 0x90); + D_PrintHex (blocks, 0x90); + Notify(PSTR("\r\nblock size:\t"), 0x90); + D_PrintHex (bsize, 0x90); + Notify(PSTR("\r\n---------\r\n"), 0x80); + CDB10_t cdb = CDB10_t(SCSI_CMD_WRITE_10, lun, blocks, addr); + +again: + uint8_t er = SCSITransaction10(&cdb, ((uint16_t)bsize * blocks), (void*)buf, (uint8_t)MASS_CMD_DIR_OUT); + + if(er == MASS_ERR_WRITE_STALL) { + MediaCTL(lun, 1); + delay(150); + if(!TestUnitReady(lun)) goto again; + } + return er; +} + +// End of user functions, the remaining code below is driver internals. +// Only developer serviceable parts below! + +//////////////////////////////////////////////////////////////////////////////// + +// Main driver code + +//////////////////////////////////////////////////////////////////////////////// + +BulkOnly::BulkOnly(USB *p) : +pUsb(p), +bAddress(0), +bIface(0), +bNumEP(1), +qNextPollTime(0), +bPollEnable(false), +//dCBWTag(0), +bLastUsbError(0) { + ClearAllEP(); + dCBWTag = 0; + if(pUsb) + pUsb->RegisterDeviceClass(this); +} + +/** + * USB_ERROR_CONFIG_REQUIRES_ADDITIONAL_RESET == success + * We need to standardize either the rcode, or change the API to return values + * so a signal that additional actions are required can be produced. + * Some of these codes do exist already. + * + * TECHNICAL: We could do most of this code elsewhere, with the exception of checking the class instance. + * Doing so would save some program memory when using multiple drivers. + * + * @param parent USB address of parent + * @param port address of port on parent + * @param lowspeed true if device is low speed + * @return + */ +uint8_t BulkOnly::ConfigureDevice(uint8_t parent, uint8_t port, bool lowspeed) { + + const uint8_t constBufSize = sizeof (USB_DEVICE_DESCRIPTOR); + + uint8_t buf[constBufSize]; + USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast(buf); + uint8_t rcode; + UsbDevice *p = NULL; + EpInfo *oldep_ptr = NULL; + USBTRACE("MS ConfigureDevice\r\n"); + ClearAllEP(); + AddressPool &addrPool = pUsb->GetAddressPool(); + + + if(bAddress) + return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE; + + // + // Get pointer to pseudo device with address 0 assigned + p = addrPool.GetUsbDevicePtr(0); + if(!p) { + return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; + } + + if(!p->epinfo) { + USBTRACE("epinfo\r\n"); + return USB_ERROR_EPINFO_IS_NULL; + } + + // Save old pointer to EP_RECORD of address 0 + oldep_ptr = p->epinfo; + + // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence + p->epinfo = epInfo; + + p->lowspeed = lowspeed; + // Get device descriptor + rcode = pUsb->getDevDescr(0, 0, constBufSize, (uint8_t*)buf); + + // Restore p->epinfo + p->epinfo = oldep_ptr; + + if(rcode) { + goto FailGetDevDescr; + } + // Allocate new address according to device class + bAddress = addrPool.AllocAddress(parent, false, port); + + if(!bAddress) + return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL; + + // Extract Max Packet Size from the device descriptor + epInfo[0].maxPktSize = udd->bMaxPacketSize0; + // Steal and abuse from epInfo structure to save on memory. + epInfo[1].epAddr = udd->bNumConfigurations; + // + return USB_ERROR_CONFIG_REQUIRES_ADDITIONAL_RESET; + +FailGetDevDescr: +#ifdef DEBUG_USB_HOST + NotifyFailGetDevDescr(rcode); +#endif + rcode = USB_ERROR_FailGetDevDescr; + + Release(); + return rcode; +}; + +/** + * + * @param parent (not used) + * @param port (not used) + * @param lowspeed true if device is low speed + * @return 0 for success + */ +uint8_t BulkOnly::Init(uint8_t parent, uint8_t port, bool lowspeed) { + uint8_t rcode; + uint8_t num_of_conf = epInfo[1].epAddr; // number of configurations + epInfo[1].epAddr = 0; + USBTRACE("MS Init\r\n"); + + AddressPool &addrPool = pUsb->GetAddressPool(); + UsbDevice *p = addrPool.GetUsbDevicePtr(bAddress); + + if(!p) + return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; + + // Assign new address to the device + delay(2000); + rcode = pUsb->setAddr(0, 0, bAddress); + + if(rcode) { + p->lowspeed = false; + addrPool.FreeAddress(bAddress); + bAddress = 0; + USBTRACE2("setAddr:", rcode); + return rcode; + } + + USBTRACE2("Addr:", bAddress); + + p->lowspeed = false; + + p = addrPool.GetUsbDevicePtr(bAddress); + + if(!p) + return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; + + p->lowspeed = lowspeed; + + // Assign epInfo to epinfo pointer + rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo); + + if(rcode) + goto FailSetDevTblEntry; + + USBTRACE2("NC:", num_of_conf); + + for(uint8_t i = 0; i < num_of_conf; i++) { + ConfigDescParser< USB_CLASS_MASS_STORAGE, + MASS_SUBCLASS_SCSI, + MASS_PROTO_BBB, + CP_MASK_COMPARE_CLASS | + CP_MASK_COMPARE_SUBCLASS | + CP_MASK_COMPARE_PROTOCOL > BulkOnlyParser(this); + + rcode = pUsb->getConfDescr(bAddress, 0, i, &BulkOnlyParser); + + if(rcode) + goto FailGetConfDescr; + + if(bNumEP > 1) + break; + } + + if(bNumEP < 3) + return USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED; + + // Assign epInfo to epinfo pointer + pUsb->setEpInfoEntry(bAddress, bNumEP, epInfo); + + USBTRACE2("Conf:", bConfNum); + + // Set Configuration Value + rcode = pUsb->setConf(bAddress, 0, bConfNum); + + if(rcode) + goto FailSetConfDescr; + + //Linux does a 1sec delay after this. + delay(1000); + + rcode = GetMaxLUN(&bMaxLUN); + if(rcode) + goto FailGetMaxLUN; + + if(bMaxLUN >= MASS_MAX_SUPPORTED_LUN) bMaxLUN = MASS_MAX_SUPPORTED_LUN - 1; + ErrorMessage (PSTR("MaxLUN"), bMaxLUN); + + delay(1000); // Delay a bit for slow firmware. + + for(uint8_t lun = 0; lun <= bMaxLUN; lun++) { + InquiryResponse response; + rcode = Inquiry(lun, sizeof (InquiryResponse), (uint8_t*) & response); + if(rcode) { + ErrorMessage (PSTR("Inquiry"), rcode); + } else { +#if 0 + printf("LUN %i `", lun); + uint8_t *buf = response.VendorID; + for(int i = 0; i < 28; i++) printf("%c", buf[i]); + printf("'\r\nQualifier %1.1X ", response.PeripheralQualifier); + printf("Device type %2.2X ", response.DeviceType); + printf("RMB %1.1X ", response.Removable); + printf("SSCS %1.1X ", response.SCCS); + uint8_t sv = response.Version; + printf("SCSI version %2.2X\r\nDevice conforms to ", sv); + switch(sv) { + case 0: + printf("No specific"); + break; + case 1: + printf("ANSI X3.131-1986 (ANSI 1)"); + break; + case 2: + printf("ANSI X3.131-1994 (ANSI 2)"); + break; + case 3: + printf("ANSI INCITS 301-1997 (SPC)"); + break; + case 4: + printf("ANSI INCITS 351-2001 (SPC-2)"); + break; + case 5: + printf("ANSI INCITS 408-2005 (SPC-4)"); + break; + case 6: + printf("T10/1731-D (SPC-4)"); + break; + default: + printf("unknown"); + } + printf(" standards.\r\n"); +#endif + uint8_t tries = 0xf0; + while((rcode = TestUnitReady(lun))) { + if(rcode == 0x08) break; // break on no media, this is OK to do. + // try to lock media and spin up + if(tries < 14) { + LockMedia(lun, 1); + MediaCTL(lun, 1); // I actually have a USB stick that needs this! + } else delay(2 * (tries + 1)); + tries++; + if(!tries) break; + } + if(!rcode) { + delay(1000); + LUNOk[lun] = CheckLUN(lun); + if(!LUNOk[lun]) LUNOk[lun] = CheckLUN(lun); + } + } + } + + + CheckMedia(); + + rcode = OnInit(); + + if(rcode) + goto FailOnInit; + +#ifdef DEBUG_USB_HOST + USBTRACE("MS configured\r\n\r\n"); +#endif + + bPollEnable = true; + + //USBTRACE("Poll enabled\r\n"); + return 0; + +FailSetConfDescr: +#ifdef DEBUG_USB_HOST + NotifyFailSetConfDescr(); + goto Fail; +#endif + +FailOnInit: +#ifdef DEBUG_USB_HOST + USBTRACE("OnInit:"); + goto Fail; +#endif + +FailGetMaxLUN: +#ifdef DEBUG_USB_HOST + USBTRACE("GetMaxLUN:"); + goto Fail; +#endif + + //#ifdef DEBUG_USB_HOST + //FailInvalidSectorSize: + // USBTRACE("Sector Size is NOT VALID: "); + // goto Fail; + //#endif + +FailSetDevTblEntry: +#ifdef DEBUG_USB_HOST + NotifyFailSetDevTblEntry(); + goto Fail; +#endif + +FailGetConfDescr: +#ifdef DEBUG_USB_HOST + NotifyFailGetConfDescr(); +#endif + +#ifdef DEBUG_USB_HOST +Fail: + NotifyFail(rcode); +#endif + Release(); + return rcode; +} + +/** + * For driver use only. + * + * @param conf + * @param iface + * @param alt + * @param proto + * @param pep + */ +void BulkOnly::EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR * pep) { + ErrorMessage (PSTR("Conf.Val"), conf); + ErrorMessage (PSTR("Iface Num"), iface); + ErrorMessage (PSTR("Alt.Set"), alt); + + bConfNum = conf; + + uint8_t index; + +#if 1 + if((pep->bmAttributes & 0x02) == 2) { + index = ((pep->bEndpointAddress & 0x80) == 0x80) ? epDataInIndex : epDataOutIndex; + // Fill in the endpoint info structure + epInfo[index].epAddr = (pep->bEndpointAddress & 0x0F); + epInfo[index].maxPktSize = (uint8_t)pep->wMaxPacketSize; + epInfo[index].epAttribs = 0; + + bNumEP++; + + PrintEndpointDescriptor(pep); + + } +#else + if((pep->bmAttributes & 0x03) == 3 && (pep->bEndpointAddress & 0x80) == 0x80) + index = epInterruptInIndex; + else + if((pep->bmAttributes & 0x02) == 2) + index = ((pep->bEndpointAddress & 0x80) == 0x80) ? epDataInIndex : epDataOutIndex; + else + return; + + // Fill in the endpoint info structure + epInfo[index].epAddr = (pep->bEndpointAddress & 0x0F); + epInfo[index].maxPktSize = (uint8_t)pep->wMaxPacketSize; + epInfo[index].epAttribs = 0; + + bNumEP++; + + PrintEndpointDescriptor(pep); +#endif +} + +/** + * For driver use only. + * + * @return + */ +uint8_t BulkOnly::Release() { + ClearAllEP(); + pUsb->GetAddressPool().FreeAddress(bAddress); + return 0; +} + +/** + * For driver use only. + * + * @param lun Logical Unit Number + * @return true if LUN is ready for use. + */ +bool BulkOnly::CheckLUN(uint8_t lun) { + uint8_t rcode; + Capacity capacity; + for(uint8_t i = 0; i < 8; i++) capacity.data[i] = 0; + + rcode = ReadCapacity10(lun, (uint8_t*)capacity.data); + if(rcode) { + //printf(">>>>>>>>>>>>>>>>ReadCapacity returned %i\r\n", rcode); + return false; + } + ErrorMessage (PSTR(">>>>>>>>>>>>>>>>CAPACITY OK ON LUN"), lun); + for(uint8_t i = 0; i < 8 /*sizeof (Capacity)*/; i++) + D_PrintHex (capacity.data[i], 0x80); + Notify(PSTR("\r\n\r\n"), 0x80); + // Only 512/1024/2048/4096 are valid values! + uint32_t c = BMAKE32(capacity.data[4], capacity.data[5], capacity.data[6], capacity.data[7]); + if(c != 0x0200LU && c != 0x0400LU && c != 0x0800LU && c != 0x1000LU) { + return false; + } + // Store capacity information. + CurrentSectorSize[lun] = (uint16_t)(c); // & 0xFFFF); + + CurrentCapacity[lun] = BMAKE32(capacity.data[0], capacity.data[1], capacity.data[2], capacity.data[3]) + 1; + if(CurrentCapacity[lun] == /*0xffffffffLU */ 0x01LU || CurrentCapacity[lun] == 0x00LU) { + // Buggy firmware will report 0xffffffff or 0 for no media + if(CurrentCapacity[lun]) + ErrorMessage (PSTR(">>>>>>>>>>>>>>>>BUGGY FIRMWARE. CAPACITY FAIL ON LUN"), lun); + return false; + } + delay(20); + Page3F(lun); + if(!TestUnitReady(lun)) return true; + return false; +} + +/** + * For driver use only. + * + * Scan for media change on all LUNs + */ +void BulkOnly::CheckMedia() { + for(uint8_t lun = 0; lun <= bMaxLUN; lun++) { + if(TestUnitReady(lun)) { + LUNOk[lun] = false; + continue; + } + if(!LUNOk[lun]) + LUNOk[lun] = CheckLUN(lun); + } +#if 0 + printf("}}}}}}}}}}}}}}}}STATUS "); + for(uint8_t lun = 0; lun <= bMaxLUN; lun++) { + if(LUNOk[lun]) + printf("#"); + else printf("."); + } + printf("\r\n"); +#endif + qNextPollTime = millis() + 2000; +} + +/** + * For driver use only. + * + * @return + */ +uint8_t BulkOnly::Poll() { + //uint8_t rcode = 0; + + if(!bPollEnable) + return 0; + + if((long)(millis() - qNextPollTime) >= 0L) { + CheckMedia(); + } + //rcode = 0; + + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// + + +// SCSI code + + +//////////////////////////////////////////////////////////////////////////////// + +/** + * For driver use only. + * + * @param plun + * @return + */ +uint8_t BulkOnly::GetMaxLUN(uint8_t *plun) { + uint8_t ret = pUsb->ctrlReq(bAddress, 0, bmREQ_MASSIN, MASS_REQ_GET_MAX_LUN, 0, 0, bIface, 1, 1, plun, NULL); + + if(ret == hrSTALL) + *plun = 0; + + return 0; +} + +/** + * For driver use only. Used during Driver Init + * + * @param lun Logical Unit Number + * @param bsize + * @param buf + * @return + */ +uint8_t BulkOnly::Inquiry(uint8_t lun, uint16_t bsize, uint8_t *buf) { + Notify(PSTR("\r\nInquiry\r\n"), 0x80); + Notify(PSTR("---------\r\n"), 0x80); + + CDB6_t cdb = CDB6_t(SCSI_CMD_INQUIRY, lun, 0LU, (uint8_t)bsize, 0); + uint8_t rc = SCSITransaction6(&cdb, bsize, buf, (uint8_t)MASS_CMD_DIR_IN); + + return rc; +} + +/** + * For driver use only. + * + * @param lun Logical Unit Number + * @return + */ +uint8_t BulkOnly::TestUnitReady(uint8_t lun) { + //SetCurLUN(lun); + if(!bAddress) + return MASS_ERR_UNIT_NOT_READY; + + Notify(PSTR("\r\nTestUnitReady\r\n"), 0x80); + Notify(PSTR("-----------------\r\n"), 0x80); + + CDB6_t cdb = CDB6_t(SCSI_CMD_TEST_UNIT_READY, lun, (uint8_t)0, 0); + return SCSITransaction6(&cdb, 0, NULL, (uint8_t)MASS_CMD_DIR_IN); + +} + +/** + * For driver use only. + * + * @param lun Logical Unit Number + * @param pc + * @param page + * @param subpage + * @param len + * @param pbuf + * @return + */ +uint8_t BulkOnly::ModeSense6(uint8_t lun, uint8_t pc, uint8_t page, uint8_t subpage, uint8_t len, uint8_t * pbuf) { + Notify(PSTR("\r\rModeSense\r\n"), 0x80); + Notify(PSTR("------------\r\n"), 0x80); + + CDB6_t cdb = CDB6_t(SCSI_CMD_TEST_UNIT_READY, lun, (uint32_t)((((pc << 6) | page) << 8) | subpage), len, 0); + return SCSITransaction6(&cdb, len, pbuf, (uint8_t)MASS_CMD_DIR_IN); +} + +/** + * For driver use only. + * + * @param lun Logical Unit Number + * @param bsize + * @param buf + * @return + */ +uint8_t BulkOnly::ReadCapacity10(uint8_t lun, uint8_t *buf) { + Notify(PSTR("\r\nReadCapacity\r\n"), 0x80); + Notify(PSTR("---------------\r\n"), 0x80); + + CDB10_t cdb = CDB10_t(SCSI_CMD_READ_CAPACITY_10, lun); + return SCSITransaction10(&cdb, 8, buf, (uint8_t)MASS_CMD_DIR_IN); +} + +/** + * For driver use only. + * + * Page 3F contains write protect status. + * + * @param lun Logical Unit Number to test. + * @return Write protect switch status. + */ +uint8_t BulkOnly::Page3F(uint8_t lun) { + uint8_t buf[192]; + for(int i = 0; i < 192; i++) { + buf[i] = 0x00; + } + WriteOk[lun] = true; + uint8_t rc = ModeSense6(lun, 0, 0x3f, 0, 192, buf); + if(!rc) { + WriteOk[lun] = ((buf[2] & 0x80) == 0); + Notify(PSTR("Mode Sense: "), 0x80); + for(int i = 0; i < 4; i++) { + D_PrintHex (buf[i], 0x80); + Notify(PSTR(" "), 0x80); + } + Notify(PSTR("\r\n"), 0x80); + } + return rc; +} + +/** + * For driver use only. + * + * @param lun Logical Unit Number + * @param size + * @param buf + * @return + */ +uint8_t BulkOnly::RequestSense(uint8_t lun, uint16_t size, uint8_t *buf) { + Notify(PSTR("\r\nRequestSense\r\n"), 0x80); + Notify(PSTR("----------------\r\n"), 0x80); + + CDB6_t cdb = CDB6_t(SCSI_CMD_REQUEST_SENSE, lun, 0LU, (uint8_t)size, 0); + CommandBlockWrapper cbw = CommandBlockWrapper(++dCBWTag, (uint32_t)size, &cdb, (uint8_t)MASS_CMD_DIR_IN); + //SetCurLUN(lun); + return Transaction(&cbw, size, buf); +} + + +//////////////////////////////////////////////////////////////////////////////// + + +// USB code + + +//////////////////////////////////////////////////////////////////////////////// + +/** + * For driver use only. + * + * @param index + * @return + */ +uint8_t BulkOnly::ClearEpHalt(uint8_t index) { + if(index == 0) + return 0; + + uint8_t ret = 0; + + while((ret = (pUsb->ctrlReq(bAddress, 0, USB_SETUP_HOST_TO_DEVICE | USB_SETUP_TYPE_STANDARD | USB_SETUP_RECIPIENT_ENDPOINT, USB_REQUEST_CLEAR_FEATURE, USB_FEATURE_ENDPOINT_HALT, 0, ((index == epDataInIndex) ? (0x80 | epInfo[index].epAddr) : epInfo[index].epAddr), 0, 0, NULL, NULL)) == 0x01)) + delay(6); + + if(ret) { + ErrorMessage (PSTR("ClearEpHalt"), ret); + ErrorMessage (PSTR("EP"), ((index == epDataInIndex) ? (0x80 | epInfo[index].epAddr) : epInfo[index].epAddr)); + return ret; + } + epInfo[index].bmSndToggle = 0; + epInfo[index].bmRcvToggle = 0; + // epAttribs = 0; + return 0; +} + +/** + * For driver use only. + * + */ +void BulkOnly::Reset() { + while(pUsb->ctrlReq(bAddress, 0, bmREQ_MASSOUT, MASS_REQ_BOMSR, 0, 0, bIface, 0, 0, NULL, NULL) == 0x01) delay(6); +} + +/** + * For driver use only. + * + * @return 0 if successful + */ +uint8_t BulkOnly::ResetRecovery() { + Notify(PSTR("\r\nResetRecovery\r\n"), 0x80); + Notify(PSTR("-----------------\r\n"), 0x80); + + delay(6); + Reset(); + delay(6); + ClearEpHalt(epDataInIndex); + delay(6); + bLastUsbError = ClearEpHalt(epDataOutIndex); + delay(6); + return bLastUsbError; +} + +/** + * For driver use only. + * + * Clear all EP data and clear all LUN status + */ +void BulkOnly::ClearAllEP() { + for(uint8_t i = 0; i < MASS_MAX_ENDPOINTS; i++) { + epInfo[i].epAddr = 0; + epInfo[i].maxPktSize = (i) ? 0 : 8; + epInfo[i].epAttribs = 0; + + epInfo[i].bmNakPower = USB_NAK_DEFAULT; + } + + for(uint8_t i = 0; i < MASS_MAX_SUPPORTED_LUN; i++) { + LUNOk[i] = false; + WriteOk[i] = false; + CurrentCapacity[i] = 0lu; + CurrentSectorSize[i] = 0; + } + + bIface = 0; + bNumEP = 1; + bAddress = 0; + qNextPollTime = 0; + bPollEnable = false; + bLastUsbError = 0; + bMaxLUN = 0; + bTheLUN = 0; +} + +/** + * For driver use only. + * + * @param pcsw + * @param pcbw + * @return + */ +bool BulkOnly::IsValidCSW(CommandStatusWrapper *pcsw, CommandBlockWrapperBase *pcbw) { + if(pcsw->dCSWSignature != MASS_CSW_SIGNATURE) { + Notify(PSTR("CSW:Sig error\r\n"), 0x80); + return false; + } + if(pcsw->dCSWTag != pcbw->dCBWTag) { + Notify(PSTR("CSW:Wrong tag\r\n"), 0x80); + return false; + } + return true; +} + +/** + * For driver use only. + * + * @param error + * @param index + * @return + */ +uint8_t BulkOnly::HandleUsbError(uint8_t error, uint8_t index) { + uint8_t count = 3; + + bLastUsbError = error; + //if (error) + //ClearEpHalt(index); + while(error && count) { + if(error != hrSUCCESS) { + ErrorMessage (PSTR("USB Error"), error); + ErrorMessage (PSTR("Index"), index); + } + switch(error) { + // case hrWRONGPID: + case hrSUCCESS: + return MASS_ERR_SUCCESS; + case hrBUSY: + // SIE is busy, just hang out and try again. + return MASS_ERR_UNIT_BUSY; + case hrTIMEOUT: + case hrJERR: return MASS_ERR_DEVICE_DISCONNECTED; + case hrSTALL: + if(index == 0) + return MASS_ERR_STALL; + ClearEpHalt(index); + if(index != epDataInIndex) + return MASS_ERR_WRITE_STALL; + return MASS_ERR_STALL; + + case hrNAK: + if(index == 0) + return MASS_ERR_UNIT_BUSY; + return MASS_ERR_UNIT_BUSY; + + case hrTOGERR: + // Handle a very super rare corner case, where toggles become de-synched. + // I have only ran into one device that has this firmware bug, and this is + // the only clean way to get back into sync with the buggy device firmware. + // --AJK + if(bAddress && bConfNum) { + error = pUsb->setConf(bAddress, 0, bConfNum); + + if(error) + break; + } + return MASS_ERR_SUCCESS; + default: + ErrorMessage (PSTR("\r\nUSB"), error); + return MASS_ERR_GENERAL_USB_ERROR; + } + count--; + } // while + + return ((error && !count) ? MASS_ERR_GENERAL_USB_ERROR : MASS_ERR_SUCCESS); +} + +#if MS_WANT_PARSER + +uint8_t BulkOnly::Transaction(CommandBlockWrapper *pcbw, uint16_t buf_size, void *buf) { + return Transaction(CommandBlockWrapper *pcbw, uint16_t buf_size, void *buf, 0); +} +#endif + +/** + * For driver use only. + * + * @param pcbw + * @param buf_size + * @param buf + * @param flags + * @return + */ +uint8_t BulkOnly::Transaction(CommandBlockWrapper *pcbw, uint16_t buf_size, void *buf +#if MS_WANT_PARSER + , uint8_t flags +#endif + ) { + +#if MS_WANT_PARSER + uint16_t bytes = (pcbw->dCBWDataTransferLength > buf_size) ? buf_size : pcbw->dCBWDataTransferLength; + printf("Transfersize %i\r\n", bytes); + delay(1000); + + bool callback = (flags & MASS_TRANS_FLG_CALLBACK) == MASS_TRANS_FLG_CALLBACK; +#else + uint16_t bytes = buf_size; +#endif + bool write = (pcbw->bmCBWFlags & MASS_CMD_DIR_IN) != MASS_CMD_DIR_IN; + uint8_t ret = 0; + uint8_t usberr; + CommandStatusWrapper csw; // up here, we allocate ahead to save cpu cycles. + SetCurLUN(pcbw->bmCBWLUN); + ErrorMessage (PSTR("CBW.dCBWTag"), pcbw->dCBWTag); + + while((usberr = pUsb->outTransfer(bAddress, epInfo[epDataOutIndex].epAddr, sizeof (CommandBlockWrapper), (uint8_t*)pcbw)) == hrBUSY) delay(1); + + ret = HandleUsbError(usberr, epDataOutIndex); + //ret = HandleUsbError(pUsb->outTransfer(bAddress, epInfo[epDataOutIndex].epAddr, sizeof (CommandBlockWrapper), (uint8_t*)pcbw), epDataOutIndex); + if(ret) { + ErrorMessage (PSTR("============================ CBW"), ret); + } else { + if(bytes) { + if(!write) { +#if MS_WANT_PARSER + if(callback) { + uint8_t rbuf[bytes]; + while((usberr = pUsb->inTransfer(bAddress, epInfo[epDataInIndex].epAddr, &bytes, rbuf)) == hrBUSY) delay(1); + if(usberr == hrSUCCESS) ((USBReadParser*)buf)->Parse(bytes, rbuf, 0); + } else { +#endif + while((usberr = pUsb->inTransfer(bAddress, epInfo[epDataInIndex].epAddr, &bytes, (uint8_t*)buf)) == hrBUSY) delay(1); +#if MS_WANT_PARSER + + } +#endif + ret = HandleUsbError(usberr, epDataInIndex); + } else { + while((usberr = pUsb->outTransfer(bAddress, epInfo[epDataOutIndex].epAddr, bytes, (uint8_t*)buf)) == hrBUSY) delay(1); + ret = HandleUsbError(usberr, epDataOutIndex); + } + if(ret) { + ErrorMessage (PSTR("============================ DAT"), ret); + } + } + } + + { + bytes = sizeof (CommandStatusWrapper); + int tries = 2; + while(tries--) { + while((usberr = pUsb->inTransfer(bAddress, epInfo[epDataInIndex].epAddr, &bytes, (uint8_t*) & csw)) == hrBUSY) delay(1); + if(!usberr) break; + ClearEpHalt(epDataInIndex); + if(tries) ResetRecovery(); + } + if(!ret) { + Notify(PSTR("CBW:\t\tOK\r\n"), 0x80); + Notify(PSTR("Data Stage:\tOK\r\n"), 0x80); + } else { + // Throw away csw, IT IS NOT OF ANY USE. + ResetRecovery(); + return ret; + } + ret = HandleUsbError(usberr, epDataInIndex); + if(ret) { + ErrorMessage (PSTR("============================ CSW"), ret); + } + if(usberr == hrSUCCESS) { + if(IsValidCSW(&csw, pcbw)) { + //ErrorMessage (PSTR("CSW.dCBWTag"), csw.dCSWTag); + //ErrorMessage (PSTR("bCSWStatus"), csw.bCSWStatus); + //ErrorMessage (PSTR("dCSWDataResidue"), csw.dCSWDataResidue); + Notify(PSTR("CSW:\t\tOK\r\n\r\n"), 0x80); + return csw.bCSWStatus; + } else { + // NOTE! Sometimes this is caused by the reported residue being wrong. + // Get a different device. It isn't compliant, and should have never passed Q&A. + // I own one... 05e3:0701 Genesys Logic, Inc. USB 2.0 IDE Adapter. + // Other devices that exhibit this behavior exist in the wild too. + // Be sure to check quirks in the Linux source code before reporting a bug. --xxxajk + Notify(PSTR("Invalid CSW\r\n"), 0x80); + ResetRecovery(); + //return MASS_ERR_SUCCESS; + return MASS_ERR_INVALID_CSW; + } + } + } + return ret; +} + +/** + * For driver use only. + * + * @param lun Logical Unit Number + * @return + */ +uint8_t BulkOnly::SetCurLUN(uint8_t lun) { + if(lun > bMaxLUN) + return MASS_ERR_INVALID_LUN; + bTheLUN = lun; + return MASS_ERR_SUCCESS; +}; + +/** + * For driver use only. + * + * @param status + * @return + */ +uint8_t BulkOnly::HandleSCSIError(uint8_t status) { + uint8_t ret = 0; + + switch(status) { + case 0: return MASS_ERR_SUCCESS; + + case 2: + ErrorMessage (PSTR("Phase Error"), status); + ErrorMessage (PSTR("LUN"), bTheLUN); + ResetRecovery(); + return MASS_ERR_GENERAL_SCSI_ERROR; + + case 1: + ErrorMessage (PSTR("SCSI Error"), status); + ErrorMessage (PSTR("LUN"), bTheLUN); + RequestSenseResponce rsp; + + ret = RequestSense(bTheLUN, sizeof (RequestSenseResponce), (uint8_t*) & rsp); + + if(ret) { + return MASS_ERR_GENERAL_SCSI_ERROR; + } + ErrorMessage (PSTR("Response Code"), rsp.bResponseCode); + if(rsp.bResponseCode & 0x80) { + Notify(PSTR("Information field: "), 0x80); + for(int i = 0; i < 4; i++) { + D_PrintHex (rsp.CmdSpecificInformation[i], 0x80); + Notify(PSTR(" "), 0x80); + } + Notify(PSTR("\r\n"), 0x80); + } + ErrorMessage (PSTR("Sense Key"), rsp.bmSenseKey); + ErrorMessage (PSTR("Add Sense Code"), rsp.bAdditionalSenseCode); + ErrorMessage (PSTR("Add Sense Qual"), rsp.bAdditionalSenseQualifier); + // warning, this is not testing ASQ, only SK and ASC. + switch(rsp.bmSenseKey) { + case SCSI_S_UNIT_ATTENTION: + switch(rsp.bAdditionalSenseCode) { + case SCSI_ASC_MEDIA_CHANGED: + return MASS_ERR_MEDIA_CHANGED; + default: + return MASS_ERR_UNIT_NOT_READY; + } + case SCSI_S_NOT_READY: + switch(rsp.bAdditionalSenseCode) { + case SCSI_ASC_MEDIUM_NOT_PRESENT: + return MASS_ERR_NO_MEDIA; + default: + return MASS_ERR_UNIT_NOT_READY; + } + case SCSI_S_ILLEGAL_REQUEST: + switch(rsp.bAdditionalSenseCode) { + case SCSI_ASC_LBA_OUT_OF_RANGE: + return MASS_ERR_BAD_LBA; + default: + return MASS_ERR_CMD_NOT_SUPPORTED; + } + default: + return MASS_ERR_GENERAL_SCSI_ERROR; + } + + // case 4: return MASS_ERR_UNIT_BUSY; // Busy means retry later. + // case 0x05/0x14: we stalled out + // case 0x15/0x16: we naked out. + default: + ErrorMessage (PSTR("Gen SCSI Err"), status); + ErrorMessage (PSTR("LUN"), bTheLUN); + return status; + } // switch +} + + +//////////////////////////////////////////////////////////////////////////////// + + +// Debugging code + + +//////////////////////////////////////////////////////////////////////////////// + +/** + * + * @param ep_ptr + */ +void BulkOnly::PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR * ep_ptr) { + Notify(PSTR("Endpoint descriptor:"), 0x80); + Notify(PSTR("\r\nLength:\t\t"), 0x80); + D_PrintHex (ep_ptr->bLength, 0x80); + Notify(PSTR("\r\nType:\t\t"), 0x80); + D_PrintHex (ep_ptr->bDescriptorType, 0x80); + Notify(PSTR("\r\nAddress:\t"), 0x80); + D_PrintHex (ep_ptr->bEndpointAddress, 0x80); + Notify(PSTR("\r\nAttributes:\t"), 0x80); + D_PrintHex (ep_ptr->bmAttributes, 0x80); + Notify(PSTR("\r\nMaxPktSize:\t"), 0x80); + D_PrintHex (ep_ptr->wMaxPacketSize, 0x80); + Notify(PSTR("\r\nPoll Intrv:\t"), 0x80); + D_PrintHex (ep_ptr->bInterval, 0x80); + Notify(PSTR("\r\n"), 0x80); +} + + +//////////////////////////////////////////////////////////////////////////////// + + +// misc/to kill/to-do + + +//////////////////////////////////////////////////////////////////////////////// + +/* We won't be needing this... */ +uint8_t BulkOnly::Read(uint8_t lun, uint32_t addr, uint16_t bsize, uint8_t blocks, USBReadParser * prs) { +#if MS_WANT_PARSER + if(!LUNOk[lun]) return MASS_ERR_NO_MEDIA; + Notify(PSTR("\r\nRead (With parser)\r\n"), 0x80); + Notify(PSTR("---------\r\n"), 0x80); + + CommandBlockWrapper cbw = CommandBlockWrapper(); + + cbw.dCBWSignature = MASS_CBW_SIGNATURE; + cbw.dCBWTag = ++dCBWTag; + cbw.dCBWDataTransferLength = ((uint32_t)bsize * blocks); + cbw.bmCBWFlags = MASS_CMD_DIR_IN, + cbw.bmCBWLUN = lun; + cbw.bmCBWCBLength = 10; + + cbw.CBWCB[0] = SCSI_CMD_READ_10; + cbw.CBWCB[8] = blocks; + cbw.CBWCB[2] = ((addr >> 24) & 0xff); + cbw.CBWCB[3] = ((addr >> 16) & 0xff); + cbw.CBWCB[4] = ((addr >> 8) & 0xff); + cbw.CBWCB[5] = (addr & 0xff); + + return HandleSCSIError(Transaction(&cbw, bsize, prs, 1)); +#else + return MASS_ERR_NOT_IMPLEMENTED; +#endif +} diff --git a/libraries/USB_Host_Shield/masstorage.h b/libraries/USB_Host_Shield/masstorage.h new file mode 100755 index 0000000..d39fd66 --- /dev/null +++ b/libraries/USB_Host_Shield/masstorage.h @@ -0,0 +1,571 @@ +/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved. + +This software may be distributed and modified under the terms of the GNU +General Public License version 2 (GPL2) as published by the Free Software +Foundation and appearing in the file GPL2.TXT included in the packaging of +this file. Please note that GPL2 Section 2[b] requires that all works based +on this software must also be made publicly available under the terms of +the GPL2 ("Copyleft"). + +Contact information +------------------- + +Circuits At Home, LTD +Web : http://www.circuitsathome.com +e-mail : support@circuitsathome.com + */ + +#if !defined(__MASSTORAGE_H__) +#define __MASSTORAGE_H__ + +// Cruft removal, makes driver smaller, faster. +#ifndef MS_WANT_PARSER +#define MS_WANT_PARSER 0 +#endif + +#include "Usb.h" + +#define bmREQ_MASSOUT USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_INTERFACE +#define bmREQ_MASSIN USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_INTERFACE + +// Mass Storage Subclass Constants +#define MASS_SUBCLASS_SCSI_NOT_REPORTED 0x00 // De facto use +#define MASS_SUBCLASS_RBC 0x01 +#define MASS_SUBCLASS_ATAPI 0x02 // MMC-5 (ATAPI) +#define MASS_SUBCLASS_OBSOLETE1 0x03 // Was QIC-157 +#define MASS_SUBCLASS_UFI 0x04 // Specifies how to interface Floppy Disk Drives to USB +#define MASS_SUBCLASS_OBSOLETE2 0x05 // Was SFF-8070i +#define MASS_SUBCLASS_SCSI 0x06 // SCSI Transparent Command Set +#define MASS_SUBCLASS_LSDFS 0x07 // Specifies how host has to negotiate access before trying SCSI +#define MASS_SUBCLASS_IEEE1667 0x08 + +// Mass Storage Class Protocols +#define MASS_PROTO_CBI 0x00 // CBI (with command completion interrupt) +#define MASS_PROTO_CBI_NO_INT 0x01 // CBI (without command completion interrupt) +#define MASS_PROTO_OBSOLETE 0x02 +#define MASS_PROTO_BBB 0x50 // Bulk Only Transport +#define MASS_PROTO_UAS 0x62 + +// Request Codes +#define MASS_REQ_ADSC 0x00 +#define MASS_REQ_GET 0xFC +#define MASS_REQ_PUT 0xFD +#define MASS_REQ_GET_MAX_LUN 0xFE +#define MASS_REQ_BOMSR 0xFF // Bulk-Only Mass Storage Reset + +#define MASS_CBW_SIGNATURE 0x43425355 +#define MASS_CSW_SIGNATURE 0x53425355 + +#define MASS_CMD_DIR_OUT 0 // (0 << 7) +#define MASS_CMD_DIR_IN 0x80 //(1 << 7) + +/* + * Reference documents from T10 (http://www.t10.org) + * SCSI Primary Commands - 3 (SPC-3) + * SCSI Block Commands - 2 (SBC-2) + * Multi-Media Commands - 5 (MMC-5) + */ + +/* Group 1 commands (CDB's here are should all be 6-bytes) */ +#define SCSI_CMD_TEST_UNIT_READY 0x00 +#define SCSI_CMD_REQUEST_SENSE 0x03 +#define SCSI_CMD_FORMAT_UNIT 0x04 +#define SCSI_CMD_READ_6 0x08 +#define SCSI_CMD_WRITE_6 0x0A +#define SCSI_CMD_INQUIRY 0x12 +#define SCSI_CMD_MODE_SELECT_6 0x15 +#define SCSI_CMD_MODE_SENSE_6 0x1A +#define SCSI_CMD_START_STOP_UNIT 0x1B +#define SCSI_CMD_PREVENT_REMOVAL 0x1E +/* Group 2 Commands (CDB's here are 10-bytes) */ +#define SCSI_CMD_READ_FORMAT_CAPACITIES 0x23 +#define SCSI_CMD_READ_CAPACITY_10 0x25 +#define SCSI_CMD_READ_10 0x28 +#define SCSI_CMD_WRITE_10 0x2A +#define SCSI_CMD_SEEK_10 0x2B +#define SCSI_CMD_ERASE_10 0x2C +#define SCSI_CMD_WRITE_AND_VERIFY_10 0x2E +#define SCSI_CMD_VERIFY_10 0x2F +#define SCSI_CMD_SYNCHRONIZE_CACHE 0x35 +#define SCSI_CMD_WRITE_BUFFER 0x3B +#define SCSI_CMD_READ_BUFFER 0x3C +#define SCSI_CMD_READ_SUBCHANNEL 0x42 +#define SCSI_CMD_READ_TOC 0x43 +#define SCSI_CMD_READ_HEADER 0x44 +#define SCSI_CMD_PLAY_AUDIO_10 0x45 +#define SCSI_CMD_GET_CONFIGURATION 0x46 +#define SCSI_CMD_PLAY_AUDIO_MSF 0x47 +#define SCSI_CMD_PLAY_AUDIO_TI 0x48 +#define SCSI_CMD_PLAY_TRACK_REL_10 0x49 +#define SCSI_CMD_GET_EVENT_STATUS 0x4A +#define SCSI_CMD_PAUSE_RESUME 0x4B +#define SCSI_CMD_READ_DISC_INFORMATION 0x51 +#define SCSI_CMD_READ_TRACK_INFORMATION 0x52 +#define SCSI_CMD_RESERVE_TRACK 0x53 +#define SCSI_CMD_SEND_OPC_INFORMATION 0x54 +#define SCSI_CMD_MODE_SELECT_10 0x55 +#define SCSI_CMD_REPAIR_TRACK 0x58 +#define SCSI_CMD_MODE_SENSE_10 0x5A +#define SCSI_CMD_CLOSE_TRACK_SESSION 0x5B +#define SCSI_CMD_READ_BUFFER_CAPACITY 0x5C +#define SCSI_CMD_SEND_CUE_SHEET 0x5D +/* Group 5 Commands (CDB's here are 12-bytes) */ +#define SCSI_CMD_REPORT_LUNS 0xA0 +#define SCSI_CMD_BLANK 0xA1 +#define SCSI_CMD_SECURITY_PROTOCOL_IN 0xA2 +#define SCSI_CMD_SEND_KEY 0xA3 +#define SCSI_CMD_REPORT_KEY 0xA4 +#define SCSI_CMD_PLAY_AUDIO_12 0xA5 +#define SCSI_CMD_LOAD_UNLOAD 0xA6 +#define SCSI_CMD_SET_READ_AHEAD 0xA7 +#define SCSI_CMD_READ_12 0xA8 +#define SCSI_CMD_PLAY_TRACK_REL_12 0xA9 +#define SCSI_CMD_WRITE_12 0xAA +#define SCSI_CMD_READ_MEDIA_SERIAL_12 0xAB +#define SCSI_CMD_GET_PERFORMANCE 0xAC +#define SCSI_CMD_READ_DVD_STRUCTURE 0xAD +#define SCSI_CMD_SECURITY_PROTOCOL_OUT 0xB5 +#define SCSI_CMD_SET_STREAMING 0xB6 +#define SCSI_CMD_READ_MSF 0xB9 +#define SCSI_CMD_SET_SPEED 0xBB +#define SCSI_CMD_MECHANISM_STATUS 0xBD +#define SCSI_CMD_READ_CD 0xBE +#define SCSI_CMD_SEND_DISC_STRUCTURE 0xBF +/* Vendor-unique Commands, included for completeness */ +#define SCSI_CMD_CD_PLAYBACK_STATUS 0xC4 /* SONY unique */ +#define SCSI_CMD_PLAYBACK_CONTROL 0xC9 /* SONY unique */ +#define SCSI_CMD_READ_CDDA 0xD8 /* Vendor unique */ +#define SCSI_CMD_READ_CDXA 0xDB /* Vendor unique */ +#define SCSI_CMD_READ_ALL_SUBCODES 0xDF /* Vendor unique */ + +/* SCSI error codes */ +#define SCSI_S_NOT_READY 0x02 +#define SCSI_S_MEDIUM_ERROR 0x03 +#define SCSI_S_ILLEGAL_REQUEST 0x05 +#define SCSI_S_UNIT_ATTENTION 0x06 +#define SCSI_ASC_LBA_OUT_OF_RANGE 0x21 +#define SCSI_ASC_MEDIA_CHANGED 0x28 +#define SCSI_ASC_MEDIUM_NOT_PRESENT 0x3A + +/* USB error codes */ +#define MASS_ERR_SUCCESS 0x00 +#define MASS_ERR_PHASE_ERROR 0x02 +#define MASS_ERR_UNIT_NOT_READY 0x03 +#define MASS_ERR_UNIT_BUSY 0x04 +#define MASS_ERR_STALL 0x05 +#define MASS_ERR_CMD_NOT_SUPPORTED 0x06 +#define MASS_ERR_INVALID_CSW 0x07 +#define MASS_ERR_NO_MEDIA 0x08 +#define MASS_ERR_BAD_LBA 0x09 +#define MASS_ERR_MEDIA_CHANGED 0x0A +#define MASS_ERR_DEVICE_DISCONNECTED 0x11 +#define MASS_ERR_UNABLE_TO_RECOVER 0x12 // Reset recovery error +#define MASS_ERR_INVALID_LUN 0x13 +#define MASS_ERR_WRITE_STALL 0x14 +#define MASS_ERR_READ_NAKS 0x15 +#define MASS_ERR_WRITE_NAKS 0x16 +#define MASS_ERR_WRITE_PROTECTED 0x17 +#define MASS_ERR_NOT_IMPLEMENTED 0xFD +#define MASS_ERR_GENERAL_SCSI_ERROR 0xFE +#define MASS_ERR_GENERAL_USB_ERROR 0xFF +#define MASS_ERR_USER 0xA0 // For subclasses to define their own error codes + +#define MASS_TRANS_FLG_CALLBACK 0x01 // Callback is involved +#define MASS_TRANS_FLG_NO_STALL_CHECK 0x02 // STALL condition is not checked +#define MASS_TRANS_FLG_NO_PHASE_CHECK 0x04 // PHASE_ERROR is not checked + +#define MASS_MAX_ENDPOINTS 3 + +struct Capacity { + uint8_t data[8]; + //uint32_t dwBlockAddress; + //uint32_t dwBlockLength; +} __attribute__((packed)); + +struct BASICCDB { + uint8_t Opcode; + + unsigned unused : 5; + unsigned LUN : 3; + + uint8_t info[12]; +} __attribute__((packed)); + +typedef BASICCDB BASICCDB_t; + +struct CDB6 { + uint8_t Opcode; + + unsigned LBAMSB : 5; + unsigned LUN : 3; + + uint8_t LBAHB; + uint8_t LBALB; + uint8_t AllocationLength; + uint8_t Control; + +public: + + CDB6(uint8_t _Opcode, uint8_t _LUN, uint32_t LBA, uint8_t _AllocationLength, uint8_t _Control) : + Opcode(_Opcode), LBAMSB(BGRAB2(LBA) & 0x1f), LUN(_LUN), LBAHB(BGRAB1(LBA)), LBALB(BGRAB0(LBA)), + AllocationLength(_AllocationLength), Control(_Control) { + } + + CDB6(uint8_t _Opcode, uint8_t _LUN, uint8_t _AllocationLength, uint8_t _Control) : + Opcode(_Opcode), LBAMSB(0), LUN(_LUN), LBAHB(0), LBALB(0), + AllocationLength(_AllocationLength), Control(_Control) { + } +} __attribute__((packed)); + +typedef CDB6 CDB6_t; + +struct CDB10 { + uint8_t Opcode; + + unsigned Service_Action : 5; + unsigned LUN : 3; + + uint8_t LBA_L_M_MB; + uint8_t LBA_L_M_LB; + uint8_t LBA_L_L_MB; + uint8_t LBA_L_L_LB; + + uint8_t Misc2; + + uint8_t ALC_MB; + uint8_t ALC_LB; + + uint8_t Control; +public: + + CDB10(uint8_t _Opcode, uint8_t _LUN) : + Opcode(_Opcode), Service_Action(0), LUN(_LUN), + LBA_L_M_MB(0), LBA_L_M_LB(0), LBA_L_L_MB(0), LBA_L_L_LB(0), + Misc2(0), ALC_MB(0), ALC_LB(0), Control(0) { + } + + CDB10(uint8_t _Opcode, uint8_t _LUN, uint16_t xflen, uint32_t _LBA) : + Opcode(_Opcode), Service_Action(0), LUN(_LUN), + LBA_L_M_MB(BGRAB3(_LBA)), LBA_L_M_LB(BGRAB2(_LBA)), LBA_L_L_MB(BGRAB1(_LBA)), LBA_L_L_LB(BGRAB0(_LBA)), + Misc2(0), ALC_MB(BGRAB1(xflen)), ALC_LB(BGRAB0(xflen)), Control(0) { + } +} __attribute__((packed)); + +typedef CDB10 CDB10_t; + +struct CDB12 { + uint8_t Opcode; + + unsigned Service_Action : 5; + unsigned Misc : 3; + + uint8_t LBA_L_M_LB; + uint8_t LBA_L_L_MB; + uint8_t LBA_L_L_LB; + + uint8_t ALC_M_LB; + uint8_t ALC_L_MB; + uint8_t ALC_L_LB; + uint8_t Control; +} __attribute__((packed)); + +typedef CDB12 CDB12_t; + +struct CDB_LBA32_16 { + uint8_t Opcode; + + unsigned Service_Action : 5; + unsigned Misc : 3; + + uint8_t LBA_L_M_MB; + uint8_t LBA_L_M_LB; + uint8_t LBA_L_L_MB; + uint8_t LBA_L_L_LB; + + uint8_t A_M_M_MB; + uint8_t A_M_M_LB; + uint8_t A_M_L_MB; + uint8_t A_M_L_LB; + + uint8_t ALC_M_MB; + uint8_t ALC_M_LB; + uint8_t ALC_L_MB; + uint8_t ALC_L_LB; + + uint8_t Misc2; + uint8_t Control; +} __attribute__((packed)); + +struct CDB_LBA64_16 { + uint8_t Opcode; + uint8_t Misc; + + uint8_t LBA_M_M_MB; + uint8_t LBA_M_M_LB; + uint8_t LBA_M_L_MB; + uint8_t LBA_M_L_LB; + + uint8_t LBA_L_M_MB; + uint8_t LBA_L_M_LB; + uint8_t LBA_L_L_MB; + uint8_t LBA_L_L_LB; + + uint8_t ALC_M_MB; + uint8_t ALC_M_LB; + uint8_t ALC_L_MB; + uint8_t ALC_L_LB; + + uint8_t Misc2; + uint8_t Control; +} __attribute__((packed)); + +struct InquiryResponse { + uint8_t DeviceType : 5; + uint8_t PeripheralQualifier : 3; + + unsigned Reserved : 7; + unsigned Removable : 1; + + uint8_t Version; + + unsigned ResponseDataFormat : 4; + unsigned HISUP : 1; + unsigned NormACA : 1; + unsigned TrmTsk : 1; + unsigned AERC : 1; + + uint8_t AdditionalLength; + //uint8_t Reserved3[2]; + + unsigned PROTECT : 1; + unsigned Res : 2; + unsigned ThreePC : 1; + unsigned TPGS : 2; + unsigned ACC : 1; + unsigned SCCS : 1; + + unsigned ADDR16 : 1; + unsigned R1 : 1; + unsigned R2 : 1; + unsigned MCHNGR : 1; + unsigned MULTIP : 1; + unsigned VS : 1; + unsigned ENCSERV : 1; + unsigned BQUE : 1; + + unsigned SoftReset : 1; + unsigned CmdQue : 1; + unsigned Reserved4 : 1; + unsigned Linked : 1; + unsigned Sync : 1; + unsigned WideBus16Bit : 1; + unsigned WideBus32Bit : 1; + unsigned RelAddr : 1; + + uint8_t VendorID[8]; + uint8_t ProductID[16]; + uint8_t RevisionID[4]; +} __attribute__((packed)); + +struct CommandBlockWrapperBase { + uint32_t dCBWSignature; + uint32_t dCBWTag; + uint32_t dCBWDataTransferLength; + uint8_t bmCBWFlags; +public: + + CommandBlockWrapperBase() { + } + + CommandBlockWrapperBase(uint32_t tag, uint32_t xflen, uint8_t flgs) : + dCBWSignature(MASS_CBW_SIGNATURE), dCBWTag(tag), dCBWDataTransferLength(xflen), bmCBWFlags(flgs) { + } +} __attribute__((packed)); + +struct CommandBlockWrapper : public CommandBlockWrapperBase { + + struct { + uint8_t bmCBWLUN : 4; + uint8_t bmReserved1 : 4; + }; + + struct { + uint8_t bmCBWCBLength : 4; + uint8_t bmReserved2 : 4; + }; + + uint8_t CBWCB[16]; + +public: + // All zeroed. + + CommandBlockWrapper() : + CommandBlockWrapperBase(0, 0, 0), bmReserved1(0), bmReserved2(0) { + for(int i = 0; i < 16; i++) CBWCB[i] = 0; + } + + // Generic Wrap, CDB zeroed. + + CommandBlockWrapper(uint32_t tag, uint32_t xflen, uint8_t flgs, uint8_t lu, uint8_t cmdlen, uint8_t cmd) : + CommandBlockWrapperBase(tag, xflen, flgs), + bmCBWLUN(lu), bmReserved1(0), bmCBWCBLength(cmdlen), bmReserved2(0) { + for(int i = 0; i < 16; i++) CBWCB[i] = 0; + // Type punning can cause optimization problems and bugs. + // Using reinterpret_cast to a dreinterpretifferent object is the proper way to do this. + //(((BASICCDB_t *) CBWCB)->LUN) = cmd; + BASICCDB_t *x = reinterpret_cast(CBWCB); + x->LUN = cmd; + } + + // Wrap for CDB of 6 + + CommandBlockWrapper(uint32_t tag, uint32_t xflen, CDB6_t *cdb, uint8_t dir) : + CommandBlockWrapperBase(tag, xflen, dir), + bmCBWLUN(cdb->LUN), bmReserved1(0), bmCBWCBLength(6), bmReserved2(0) { + memcpy(&CBWCB, cdb, 6); + } + // Wrap for CDB of 10 + + CommandBlockWrapper(uint32_t tag, uint32_t xflen, CDB10_t *cdb, uint8_t dir) : + CommandBlockWrapperBase(tag, xflen, dir), + bmCBWLUN(cdb->LUN), bmReserved1(0), bmCBWCBLength(10), bmReserved2(0) { + memcpy(&CBWCB, cdb, 10); + } +} __attribute__((packed)); + +struct CommandStatusWrapper { + uint32_t dCSWSignature; + uint32_t dCSWTag; + uint32_t dCSWDataResidue; + uint8_t bCSWStatus; +} __attribute__((packed)); + +struct RequestSenseResponce { + uint8_t bResponseCode; + uint8_t bSegmentNumber; + + uint8_t bmSenseKey : 4; + uint8_t bmReserved : 1; + uint8_t bmILI : 1; + uint8_t bmEOM : 1; + uint8_t bmFileMark : 1; + + uint8_t Information[4]; + uint8_t bAdditionalLength; + uint8_t CmdSpecificInformation[4]; + uint8_t bAdditionalSenseCode; + uint8_t bAdditionalSenseQualifier; + uint8_t bFieldReplaceableUnitCode; + uint8_t SenseKeySpecific[3]; +} __attribute__((packed)); + +class BulkOnly : public USBDeviceConfig, public UsbConfigXtracter { +protected: + static const uint8_t epDataInIndex; // DataIn endpoint index + static const uint8_t epDataOutIndex; // DataOUT endpoint index + static const uint8_t epInterruptInIndex; // InterruptIN endpoint index + + USB *pUsb; + uint8_t bAddress; + uint8_t bConfNum; // configuration number + uint8_t bIface; // interface value + uint8_t bNumEP; // total number of EP in the configuration + uint32_t qNextPollTime; // next poll time + bool bPollEnable; // poll enable flag + + EpInfo epInfo[MASS_MAX_ENDPOINTS]; + + uint32_t dCBWTag; // Tag + //uint32_t dCBWDataTransferLength; // Data Transfer Length + uint8_t bLastUsbError; // Last USB error + uint8_t bMaxLUN; // Max LUN + uint8_t bTheLUN; // Active LUN + uint32_t CurrentCapacity[MASS_MAX_SUPPORTED_LUN]; // Total sectors + uint16_t CurrentSectorSize[MASS_MAX_SUPPORTED_LUN]; // Sector size, clipped to 16 bits + bool LUNOk[MASS_MAX_SUPPORTED_LUN]; // use this to check for media changes. + bool WriteOk[MASS_MAX_SUPPORTED_LUN]; + void PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr); + + + // Additional Initialization Method for Subclasses + + virtual uint8_t OnInit() { + return 0; + }; +public: + BulkOnly(USB *p); + + uint8_t GetLastUsbError() { + return bLastUsbError; + }; + + uint8_t GetbMaxLUN() { + return bMaxLUN; // Max LUN + } + + uint8_t GetbTheLUN() { + return bTheLUN; // Active LUN + } + + bool WriteProtected(uint8_t lun); + uint8_t MediaCTL(uint8_t lun, uint8_t ctl); + uint8_t Read(uint8_t lun, uint32_t addr, uint16_t bsize, uint8_t blocks, uint8_t *buf); + uint8_t Read(uint8_t lun, uint32_t addr, uint16_t bsize, uint8_t blocks, USBReadParser *prs); + uint8_t Write(uint8_t lun, uint32_t addr, uint16_t bsize, uint8_t blocks, const uint8_t *buf); + uint8_t LockMedia(uint8_t lun, uint8_t lock); + + bool LUNIsGood(uint8_t lun); + uint32_t GetCapacity(uint8_t lun); + uint16_t GetSectorSize(uint8_t lun); + + // USBDeviceConfig implementation + uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed); + uint8_t ConfigureDevice(uint8_t parent, uint8_t port, bool lowspeed); + + uint8_t Release(); + uint8_t Poll(); + + virtual uint8_t GetAddress() { + return bAddress; + }; + + // UsbConfigXtracter implementation + void EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *ep); + + virtual bool DEVCLASSOK(uint8_t klass) { + return (klass == USB_CLASS_MASS_STORAGE); + } + + uint8_t SCSITransaction6(CDB6_t *cdb, uint16_t buf_size, void *buf, uint8_t dir); + uint8_t SCSITransaction10(CDB10_t *cdb, uint16_t buf_size, void *buf, uint8_t dir); + +private: + uint8_t Inquiry(uint8_t lun, uint16_t size, uint8_t *buf); + uint8_t TestUnitReady(uint8_t lun); + uint8_t RequestSense(uint8_t lun, uint16_t size, uint8_t *buf); + uint8_t ModeSense6(uint8_t lun, uint8_t pc, uint8_t page, uint8_t subpage, uint8_t len, uint8_t *buf); + uint8_t GetMaxLUN(uint8_t *max_lun); + uint8_t SetCurLUN(uint8_t lun); + void Reset(); + uint8_t ResetRecovery(); + uint8_t ReadCapacity10(uint8_t lun, uint8_t *buf); + void ClearAllEP(); + void CheckMedia(); + bool CheckLUN(uint8_t lun); + uint8_t Page3F(uint8_t lun); + bool IsValidCBW(uint8_t size, uint8_t *pcbw); + bool IsMeaningfulCBW(uint8_t size, uint8_t *pcbw); + + bool IsValidCSW(CommandStatusWrapper *pcsw, CommandBlockWrapperBase *pcbw); + + uint8_t ClearEpHalt(uint8_t index); +#if MS_WANT_PARSER + uint8_t Transaction(CommandBlockWrapper *cbw, uint16_t bsize, void *buf, uint8_t flags); +#endif + uint8_t Transaction(CommandBlockWrapper *cbw, uint16_t bsize, void *buf); + uint8_t HandleUsbError(uint8_t error, uint8_t index); + uint8_t HandleSCSIError(uint8_t status); + +}; + +#endif // __MASSTORAGE_H__ diff --git a/libraries/USB_Host_Shield/max3421e.h b/libraries/USB_Host_Shield/max3421e.h new file mode 100755 index 0000000..4e45a35 --- /dev/null +++ b/libraries/USB_Host_Shield/max3421e.h @@ -0,0 +1,228 @@ +/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved. + +This software may be distributed and modified under the terms of the GNU +General Public License version 2 (GPL2) as published by the Free Software +Foundation and appearing in the file GPL2.TXT included in the packaging of +this file. Please note that GPL2 Section 2[b] requires that all works based +on this software must also be made publicly available under the terms of +the GPL2 ("Copyleft"). + +Contact information +------------------- + +Circuits At Home, LTD +Web : http://www.circuitsathome.com +e-mail : support@circuitsathome.com + */ +#if !defined(_usb_h_) || defined(_max3421e_h_) +#error "Never include max3421e.h directly; include Usb.h instead" +#else + +#define _max3421e_h_ + +/* MAX3421E register/bit names and bitmasks */ + +/* Arduino pin definitions */ +/* pin numbers to port numbers */ + +#define SE0 0 +#define SE1 1 +#define FSHOST 2 +#define LSHOST 3 + +/* MAX3421E command byte format: rrrrr0wa where 'r' is register number */ +// +// MAX3421E Registers in HOST mode. +// +#define rRCVFIFO 0x08 //1<<3 +#define rSNDFIFO 0x10 //2<<3 +#define rSUDFIFO 0x20 //4<<3 +#define rRCVBC 0x30 //6<<3 +#define rSNDBC 0x38 //7<<3 + +#define rUSBIRQ 0x68 //13<<3 +/* USBIRQ Bits */ +#define bmVBUSIRQ 0x40 //b6 +#define bmNOVBUSIRQ 0x20 //b5 +#define bmOSCOKIRQ 0x01 //b0 + +#define rUSBIEN 0x70 //14<<3 +/* USBIEN Bits */ +#define bmVBUSIE 0x40 //b6 +#define bmNOVBUSIE 0x20 //b5 +#define bmOSCOKIE 0x01 //b0 + +#define rUSBCTL 0x78 //15<<3 +/* USBCTL Bits */ +#define bmCHIPRES 0x20 //b5 +#define bmPWRDOWN 0x10 //b4 + +#define rCPUCTL 0x80 //16<<3 +/* CPUCTL Bits */ +#define bmPUSLEWID1 0x80 //b7 +#define bmPULSEWID0 0x40 //b6 +#define bmIE 0x01 //b0 + +#define rPINCTL 0x88 //17<<3 +/* PINCTL Bits */ +#define bmFDUPSPI 0x10 //b4 +#define bmINTLEVEL 0x08 //b3 +#define bmPOSINT 0x04 //b2 +#define bmGPXB 0x02 //b1 +#define bmGPXA 0x01 //b0 +// GPX pin selections +#define GPX_OPERATE 0x00 +#define GPX_VBDET 0x01 +#define GPX_BUSACT 0x02 +#define GPX_SOF 0x03 + +#define rREVISION 0x90 //18<<3 + +#define rIOPINS1 0xa0 //20<<3 + +/* IOPINS1 Bits */ +#define bmGPOUT0 0x01 +#define bmGPOUT1 0x02 +#define bmGPOUT2 0x04 +#define bmGPOUT3 0x08 +#define bmGPIN0 0x10 +#define bmGPIN1 0x20 +#define bmGPIN2 0x40 +#define bmGPIN3 0x80 + +#define rIOPINS2 0xa8 //21<<3 +/* IOPINS2 Bits */ +#define bmGPOUT4 0x01 +#define bmGPOUT5 0x02 +#define bmGPOUT6 0x04 +#define bmGPOUT7 0x08 +#define bmGPIN4 0x10 +#define bmGPIN5 0x20 +#define bmGPIN6 0x40 +#define bmGPIN7 0x80 + +#define rGPINIRQ 0xb0 //22<<3 +/* GPINIRQ Bits */ +#define bmGPINIRQ0 0x01 +#define bmGPINIRQ1 0x02 +#define bmGPINIRQ2 0x04 +#define bmGPINIRQ3 0x08 +#define bmGPINIRQ4 0x10 +#define bmGPINIRQ5 0x20 +#define bmGPINIRQ6 0x40 +#define bmGPINIRQ7 0x80 + +#define rGPINIEN 0xb8 //23<<3 +/* GPINIEN Bits */ +#define bmGPINIEN0 0x01 +#define bmGPINIEN1 0x02 +#define bmGPINIEN2 0x04 +#define bmGPINIEN3 0x08 +#define bmGPINIEN4 0x10 +#define bmGPINIEN5 0x20 +#define bmGPINIEN6 0x40 +#define bmGPINIEN7 0x80 + +#define rGPINPOL 0xc0 //24<<3 +/* GPINPOL Bits */ +#define bmGPINPOL0 0x01 +#define bmGPINPOL1 0x02 +#define bmGPINPOL2 0x04 +#define bmGPINPOL3 0x08 +#define bmGPINPOL4 0x10 +#define bmGPINPOL5 0x20 +#define bmGPINPOL6 0x40 +#define bmGPINPOL7 0x80 + +#define rHIRQ 0xc8 //25<<3 +/* HIRQ Bits */ +#define bmBUSEVENTIRQ 0x01 // indicates BUS Reset Done or BUS Resume +#define bmRWUIRQ 0x02 +#define bmRCVDAVIRQ 0x04 +#define bmSNDBAVIRQ 0x08 +#define bmSUSDNIRQ 0x10 +#define bmCONDETIRQ 0x20 +#define bmFRAMEIRQ 0x40 +#define bmHXFRDNIRQ 0x80 + +#define rHIEN 0xd0 //26<<3 + +/* HIEN Bits */ +#define bmBUSEVENTIE 0x01 +#define bmRWUIE 0x02 +#define bmRCVDAVIE 0x04 +#define bmSNDBAVIE 0x08 +#define bmSUSDNIE 0x10 +#define bmCONDETIE 0x20 +#define bmFRAMEIE 0x40 +#define bmHXFRDNIE 0x80 + +#define rMODE 0xd8 //27<<3 + +/* MODE Bits */ +#define bmHOST 0x01 +#define bmLOWSPEED 0x02 +#define bmHUBPRE 0x04 +#define bmSOFKAENAB 0x08 +#define bmSEPIRQ 0x10 +#define bmDELAYISO 0x20 +#define bmDMPULLDN 0x40 +#define bmDPPULLDN 0x80 + +#define rPERADDR 0xe0 //28<<3 + +#define rHCTL 0xe8 //29<<3 +/* HCTL Bits */ +#define bmBUSRST 0x01 +#define bmFRMRST 0x02 +#define bmSAMPLEBUS 0x04 +#define bmSIGRSM 0x08 +#define bmRCVTOG0 0x10 +#define bmRCVTOG1 0x20 +#define bmSNDTOG0 0x40 +#define bmSNDTOG1 0x80 + +#define rHXFR 0xf0 //30<<3 +/* Host transfer token values for writing the HXFR register (R30) */ +/* OR this bit field with the endpoint number in bits 3:0 */ +#define tokSETUP 0x10 // HS=0, ISO=0, OUTNIN=0, SETUP=1 +#define tokIN 0x00 // HS=0, ISO=0, OUTNIN=0, SETUP=0 +#define tokOUT 0x20 // HS=0, ISO=0, OUTNIN=1, SETUP=0 +#define tokINHS 0x80 // HS=1, ISO=0, OUTNIN=0, SETUP=0 +#define tokOUTHS 0xA0 // HS=1, ISO=0, OUTNIN=1, SETUP=0 +#define tokISOIN 0x40 // HS=0, ISO=1, OUTNIN=0, SETUP=0 +#define tokISOOUT 0x60 // HS=0, ISO=1, OUTNIN=1, SETUP=0 + +#define rHRSL 0xf8 //31<<3 + +/* HRSL Bits */ +#define bmRCVTOGRD 0x10 +#define bmSNDTOGRD 0x20 +#define bmKSTATUS 0x40 +#define bmJSTATUS 0x80 +#define bmSE0 0x00 //SE0 - disconnect state +#define bmSE1 0xc0 //SE1 - illegal state + +/* Host error result codes, the 4 LSB's in the HRSL register */ +#define hrSUCCESS 0x00 +#define hrBUSY 0x01 +#define hrBADREQ 0x02 +#define hrUNDEF 0x03 +#define hrNAK 0x04 +#define hrSTALL 0x05 +#define hrTOGERR 0x06 +#define hrWRONGPID 0x07 +#define hrBADBC 0x08 +#define hrPIDERR 0x09 +#define hrPKTERR 0x0A +#define hrCRCERR 0x0B +#define hrKERR 0x0C +#define hrJERR 0x0D +#define hrTIMEOUT 0x0E +#define hrBABBLE 0x0F + +#define MODE_FS_HOST (bmDPPULLDN|bmDMPULLDN|bmHOST|bmSOFKAENAB) +#define MODE_LS_HOST (bmDPPULLDN|bmDMPULLDN|bmHOST|bmLOWSPEED|bmSOFKAENAB) + + +#endif //_max3421e_h_ diff --git a/libraries/USB_Host_Shield/max_LCD.cpp b/libraries/USB_Host_Shield/max_LCD.cpp new file mode 100755 index 0000000..f0c6466 --- /dev/null +++ b/libraries/USB_Host_Shield/max_LCD.cpp @@ -0,0 +1,255 @@ +/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved. + +This software may be distributed and modified under the terms of the GNU +General Public License version 2 (GPL2) as published by the Free Software +Foundation and appearing in the file GPL2.TXT included in the packaging of +this file. Please note that GPL2 Section 2[b] requires that all works based +on this software must also be made publicly available under the terms of +the GPL2 ("Copyleft"). + +Contact information +------------------- + +Circuits At Home, LTD +Web : http://www.circuitsathome.com +e-mail : support@circuitsathome.com + */ +#include "max_LCD.h" +#include + +// pin definition and set/clear + +#define RS 0x04 // RS pin +#define E 0x08 // E pin + +#define SET_RS lcdPins |= RS +#define CLR_RS lcdPins &= ~RS +#define SET_E lcdPins |= E +#define CLR_E lcdPins &= ~E + +#define SENDlcdPins() pUsb->gpioWr( lcdPins ) + +#define LCD_sendcmd(a) { CLR_RS; \ + sendbyte(a); \ + } + +#define LCD_sendchar(a) { SET_RS; \ + sendbyte(a); \ + } + +static byte lcdPins; //copy of LCD pins + +Max_LCD::Max_LCD(USB *pusb) : pUsb(pusb) { + lcdPins = 0; +} + +void Max_LCD::init() { + _displayfunction = LCD_4BITMODE | LCD_1LINE | LCD_5x8DOTS; + + // MAX3421E::gpioWr(0x55); + + begin(16, 1); +} + +void Max_LCD::begin(uint8_t cols, uint8_t lines, uint8_t dotsize) { + if(lines > 1) { + _displayfunction |= LCD_2LINE; + } + _numlines = lines; + _currline = 0; + + // for some 1 line displays you can select a 10 pixel high font + if((dotsize != 0) && (lines == 1)) { + _displayfunction |= LCD_5x10DOTS; + } + + // SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION! + // according to datasheet, we need at least 40ms after power rises above 2.7V + // before sending commands. Arduino can turn on way befer 4.5V so we'll wait 50 + delayMicroseconds(50000); + lcdPins = 0x30; + SET_E; + SENDlcdPins(); + CLR_E; + SENDlcdPins(); + delayMicroseconds(10000); // wait min 4.1ms + //second try + SET_E; + SENDlcdPins(); + CLR_E; + SENDlcdPins(); + delayMicroseconds(10000); // wait min 4.1ms + // third go! + SET_E; + SENDlcdPins(); + CLR_E; + SENDlcdPins(); + delayMicroseconds(10000); + // finally, set to 4-bit interface + lcdPins = 0x20; + //SET_RS; + SET_E; + SENDlcdPins(); + //CLR_RS; + CLR_E; + SENDlcdPins(); + delayMicroseconds(10000); + // finally, set # lines, font size, etc. + command(LCD_FUNCTIONSET | _displayfunction); + + // turn the display on with no cursor or blinking default + _displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF; + display(); + + // clear it off + clear(); + + // Initialize to default text direction (for romance languages) + _displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT; + // set the entry mode + command(LCD_ENTRYMODESET | _displaymode); +} + +/********** high level commands, for the user! */ +void Max_LCD::clear() { + command(LCD_CLEARDISPLAY); // clear display, set cursor position to zero + delayMicroseconds(2000); // this command takes a long time! +} + +void Max_LCD::home() { + command(LCD_RETURNHOME); // set cursor position to zero + delayMicroseconds(2000); // this command takes a long time! +} + +void Max_LCD::setCursor(uint8_t col, uint8_t row) { + int row_offsets[] = {0x00, 0x40, 0x14, 0x54}; + if(row > _numlines) { + row = _numlines - 1; // we count rows starting w/0 + } + + command(LCD_SETDDRAMADDR | (col + row_offsets[row])); +} + +// Turn the display on/off (quickly) + +void Max_LCD::noDisplay() { + _displaycontrol &= ~LCD_DISPLAYON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} + +void Max_LCD::display() { + _displaycontrol |= LCD_DISPLAYON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} + +// Turns the underline cursor on/off + +void Max_LCD::noCursor() { + _displaycontrol &= ~LCD_CURSORON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} + +void Max_LCD::cursor() { + _displaycontrol |= LCD_CURSORON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} + + +// Turn on and off the blinking cursor + +void Max_LCD::noBlink() { + _displaycontrol &= ~LCD_BLINKON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} + +void Max_LCD::blink() { + _displaycontrol |= LCD_BLINKON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} + +// These commands scroll the display without changing the RAM + +void Max_LCD::scrollDisplayLeft(void) { + command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT); +} + +void Max_LCD::scrollDisplayRight(void) { + command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT); +} + +// This is for text that flows Left to Right + +void Max_LCD::leftToRight(void) { + _displaymode |= LCD_ENTRYLEFT; + command(LCD_ENTRYMODESET | _displaymode); +} + +// This is for text that flows Right to Left + +void Max_LCD::rightToLeft(void) { + _displaymode &= ~LCD_ENTRYLEFT; + command(LCD_ENTRYMODESET | _displaymode); +} + +// This will 'right justify' text from the cursor + +void Max_LCD::autoscroll(void) { + _displaymode |= LCD_ENTRYSHIFTINCREMENT; + command(LCD_ENTRYMODESET | _displaymode); +} + +// This will 'left justify' text from the cursor + +void Max_LCD::noAutoscroll(void) { + _displaymode &= ~LCD_ENTRYSHIFTINCREMENT; + command(LCD_ENTRYMODESET | _displaymode); +} + +// Allows us to fill the first 8 CGRAM locations +// with custom characters + +void Max_LCD::createChar(uint8_t location, uint8_t charmap[]) { + location &= 0x7; // we only have 8 locations 0-7 + command(LCD_SETCGRAMADDR | (location << 3)); + for(int i = 0; i < 8; i++) { + write(charmap[i]); + } +} + +/*********** mid level commands, for sending data/cmds */ + +inline void Max_LCD::command(uint8_t value) { + LCD_sendcmd(value); + delayMicroseconds(100); +} + +#if defined(ARDUINO) && ARDUINO >=100 + +inline size_t Max_LCD::write(uint8_t value) { + LCD_sendchar(value); + return 1; // Assume success +} +#else + +inline void Max_LCD::write(uint8_t value) { + LCD_sendchar(value); +} +#endif + +void Max_LCD::sendbyte(uint8_t val) { + lcdPins &= 0x0f; //prepare place for the upper nibble + lcdPins |= (val & 0xf0); //copy upper nibble to LCD variable + SET_E; //send + SENDlcdPins(); + delayMicroseconds(2); + CLR_E; + delayMicroseconds(2); + SENDlcdPins(); + lcdPins &= 0x0f; //prepare place for the lower nibble + lcdPins |= (val << 4) & 0xf0; //copy lower nibble to LCD variable + SET_E; //send + SENDlcdPins(); + CLR_E; + SENDlcdPins(); + delayMicroseconds(100); +} diff --git a/libraries/USB_Host_Shield/max_LCD.h b/libraries/USB_Host_Shield/max_LCD.h new file mode 100755 index 0000000..950c9c5 --- /dev/null +++ b/libraries/USB_Host_Shield/max_LCD.h @@ -0,0 +1,106 @@ +/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved. + +This software may be distributed and modified under the terms of the GNU +General Public License version 2 (GPL2) as published by the Free Software +Foundation and appearing in the file GPL2.TXT included in the packaging of +this file. Please note that GPL2 Section 2[b] requires that all works based +on this software must also be made publicly available under the terms of +the GPL2 ("Copyleft"). + +Contact information +------------------- + +Circuits At Home, LTD +Web : http://www.circuitsathome.com +e-mail : support@circuitsathome.com + */ +//HD44780 compatible LCD display via MAX3421E GPOUT support header +//pinout: D[4-7] -> GPOUT[4-7], RS-> GPOUT[2], E ->GPOUT[3] +// + +#ifndef _Max_LCD_h_ +#define _Max_LCD_h_ + +#include "Usb.h" +#include "Print.h" + +// commands +#define LCD_CLEARDISPLAY 0x01 +#define LCD_RETURNHOME 0x02 +#define LCD_ENTRYMODESET 0x04 +#define LCD_DISPLAYCONTROL 0x08 +#define LCD_CURSORSHIFT 0x10 +#define LCD_FUNCTIONSET 0x20 +#define LCD_SETCGRAMADDR 0x40 +#define LCD_SETDDRAMADDR 0x80 + +// flags for display entry mode +#define LCD_ENTRYRIGHT 0x00 +#define LCD_ENTRYLEFT 0x02 +#define LCD_ENTRYSHIFTINCREMENT 0x01 +#define LCD_ENTRYSHIFTDECREMENT 0x00 + +// flags for display on/off control +#define LCD_DISPLAYON 0x04 +#define LCD_DISPLAYOFF 0x00 +#define LCD_CURSORON 0x02 +#define LCD_CURSOROFF 0x00 +#define LCD_BLINKON 0x01 +#define LCD_BLINKOFF 0x00 + +// flags for display/cursor shift +#define LCD_DISPLAYMOVE 0x08 +#define LCD_CURSORMOVE 0x00 +#define LCD_MOVERIGHT 0x04 +#define LCD_MOVELEFT 0x00 + +// flags for function set +#define LCD_8BITMODE 0x10 +#define LCD_4BITMODE 0x00 +#define LCD_2LINE 0x08 +#define LCD_1LINE 0x00 +#define LCD_5x10DOTS 0x04 +#define LCD_5x8DOTS 0x00 + +class Max_LCD : public Print { + USB *pUsb; + +public: + Max_LCD(USB *pusb); + void init(); + void begin(uint8_t cols, uint8_t rows, uint8_t charsize = LCD_5x8DOTS); + void clear(); + void home(); + void noDisplay(); + void display(); + void noBlink(); + void blink(); + void noCursor(); + void cursor(); + void scrollDisplayLeft(); + void scrollDisplayRight(); + void leftToRight(); + void rightToLeft(); + void autoscroll(); + void noAutoscroll(); + void createChar(uint8_t, uint8_t[]); + void setCursor(uint8_t, uint8_t); + void command(uint8_t); + +#if defined(ARDUINO) && ARDUINO >=100 + size_t write(uint8_t); + using Print::write; +#else + void write(uint8_t); +#endif + +private: + void sendbyte(uint8_t val); + uint8_t _displayfunction; //tokill + uint8_t _displaycontrol; + uint8_t _displaymode; + uint8_t _initialized; + uint8_t _numlines, _currline; +}; + +#endif diff --git a/libraries/USB_Host_Shield/message.cpp b/libraries/USB_Host_Shield/message.cpp new file mode 100755 index 0000000..bdcdd18 --- /dev/null +++ b/libraries/USB_Host_Shield/message.cpp @@ -0,0 +1,116 @@ +/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved. + +This software may be distributed and modified under the terms of the GNU +General Public License version 2 (GPL2) as published by the Free Software +Foundation and appearing in the file GPL2.TXT included in the packaging of +this file. Please note that GPL2 Section 2[b] requires that all works based +on this software must also be made publicly available under the terms of +the GPL2 ("Copyleft"). + +Contact information +------------------- + +Circuits At Home, LTD +Web : http://www.circuitsathome.com +e-mail : support@circuitsathome.com + */ + +#include "Usb.h" +// 0x80 is the default (i.e. trace) to turn off set this global to something lower. +// this allows for 126 other debugging levels. +// TO-DO: Allow assignment to a different serial port by software +int UsbDEBUGlvl = 0x80; + +void E_Notifyc(char c, int lvl) { + if(UsbDEBUGlvl < lvl) return; +#if defined(ARDUINO) && ARDUINO >=100 + USB_HOST_SERIAL.print(c); +#else + USB_HOST_SERIAL.print(c, BYTE); +#endif + //USB_HOST_SERIAL.flush(); +} + +void E_Notify(char const * msg, int lvl) { + if(UsbDEBUGlvl < lvl) return; + if(!msg) return; + char c; + + while((c = pgm_read_byte(msg++))) E_Notifyc(c, lvl); +} + +void E_NotifyStr(char const * msg, int lvl) { + if(UsbDEBUGlvl < lvl) return; + if(!msg) return; + char c; + + while((c = *msg++)) E_Notifyc(c, lvl); +} + +void E_Notify(uint8_t b, int lvl) { + if(UsbDEBUGlvl < lvl) return; +#if defined(ARDUINO) && ARDUINO >=100 + USB_HOST_SERIAL.print(b); +#else + USB_HOST_SERIAL.print(b, DEC); +#endif + //USB_HOST_SERIAL.flush(); +} + +void E_Notify(double d, int lvl) { + if(UsbDEBUGlvl < lvl) return; + USB_HOST_SERIAL.print(d); + //USB_HOST_SERIAL.flush(); +} + +#ifdef DEBUG_USB_HOST + +void NotifyFailGetDevDescr(void) { + Notify(PSTR("\r\ngetDevDescr "), 0x80); +} + +void NotifyFailSetDevTblEntry(void) { + Notify(PSTR("\r\nsetDevTblEn "), 0x80); +} + +void NotifyFailGetConfDescr(void) { + Notify(PSTR("\r\ngetConf "), 0x80); +} + +void NotifyFailSetConfDescr(void) { + Notify(PSTR("\r\nsetConf "), 0x80); +} + +void NotifyFailGetDevDescr(uint8_t reason) { + NotifyFailGetDevDescr(); + NotifyFail(reason); +} + +void NotifyFailSetDevTblEntry(uint8_t reason) { + NotifyFailSetDevTblEntry(); + NotifyFail(reason); + +} + +void NotifyFailGetConfDescr(uint8_t reason) { + NotifyFailGetConfDescr(); + NotifyFail(reason); +} + +void NotifyFailSetConfDescr(uint8_t reason) { + NotifyFailSetConfDescr(); + NotifyFail(reason); +} + +void NotifyFailUnknownDevice(uint16_t VID, uint16_t PID) { + Notify(PSTR("\r\nUnknown Device Connected - VID: "), 0x80); + D_PrintHex (VID, 0x80); + Notify(PSTR(" PID: "), 0x80); + D_PrintHex (PID, 0x80); +} + +void NotifyFail(uint8_t rcode) { + D_PrintHex (rcode, 0x80); + Notify(PSTR("\r\n"), 0x80); +} +#endif diff --git a/libraries/USB_Host_Shield/message.h b/libraries/USB_Host_Shield/message.h new file mode 100755 index 0000000..c26628e --- /dev/null +++ b/libraries/USB_Host_Shield/message.h @@ -0,0 +1,78 @@ +/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved. + +This software may be distributed and modified under the terms of the GNU +General Public License version 2 (GPL2) as published by the Free Software +Foundation and appearing in the file GPL2.TXT included in the packaging of +this file. Please note that GPL2 Section 2[b] requires that all works based +on this software must also be made publicly available under the terms of +the GPL2 ("Copyleft"). + +Contact information +------------------- + +Circuits At Home, LTD +Web : http://www.circuitsathome.com +e-mail : support@circuitsathome.com + */ +#if !defined(_usb_h_) || defined(__MESSAGE_H__) +#error "Never include message.h directly; include Usb.h instead" +#else +#define __MESSAGE_H__ + +extern int UsbDEBUGlvl; + +void E_Notify(char const * msg, int lvl); +void E_Notify(uint8_t b, int lvl); +void E_NotifyStr(char const * msg, int lvl); +void E_Notifyc(char c, int lvl); + +#ifdef DEBUG_USB_HOST +#define Notify E_Notify +#define NotifyStr E_NotifyStr +#define Notifyc E_Notifyc +void NotifyFailGetDevDescr(uint8_t reason); +void NotifyFailSetDevTblEntry(uint8_t reason); +void NotifyFailGetConfDescr(uint8_t reason); +void NotifyFailSetConfDescr(uint8_t reason); +void NotifyFailGetDevDescr(void); +void NotifyFailSetDevTblEntry(void); +void NotifyFailGetConfDescr(void); +void NotifyFailSetConfDescr(void); +void NotifyFailUnknownDevice(uint16_t VID, uint16_t PID); +void NotifyFail(uint8_t rcode); +#else +#define Notify(...) ((void)0) +#define NotifyStr(...) ((void)0) +#define Notifyc(...) ((void)0) +#define NotifyFailGetDevDescr(...) ((void)0) +#define NotifyFailSetDevTblEntry(...) ((void)0) +#define NotifyFailGetConfDescr(...) ((void)0) +#define NotifyFailGetDevDescr(...) ((void)0) +#define NotifyFailSetDevTblEntry(...) ((void)0) +#define NotifyFailGetConfDescr(...) ((void)0) +#define NotifyFailSetConfDescr(...) ((void)0) +#define NotifyFailUnknownDevice(...) ((void)0) +#define NotifyFail(...) ((void)0) +#endif + +template +void ErrorMessage(uint8_t level, char const * msg, ERROR_TYPE rcode = 0) { +#ifdef DEBUG_USB_HOST + Notify(msg, level); + Notify(PSTR(": "), level); + D_PrintHex (rcode, level); + Notify(PSTR("\r\n"), level); +#endif +} + +template +void ErrorMessage(char const * msg, ERROR_TYPE rcode = 0) { +#ifdef DEBUG_USB_HOST + Notify(msg, 0x80); + Notify(PSTR(": "), 0x80); + D_PrintHex (rcode, 0x80); + Notify(PSTR("\r\n"), 0x80); +#endif +} + +#endif // __MESSAGE_H__ diff --git a/libraries/USB_Host_Shield/parsetools.cpp b/libraries/USB_Host_Shield/parsetools.cpp new file mode 100755 index 0000000..74a8610 --- /dev/null +++ b/libraries/USB_Host_Shield/parsetools.cpp @@ -0,0 +1,67 @@ +/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved. + +This software may be distributed and modified under the terms of the GNU +General Public License version 2 (GPL2) as published by the Free Software +Foundation and appearing in the file GPL2.TXT included in the packaging of +this file. Please note that GPL2 Section 2[b] requires that all works based +on this software must also be made publicly available under the terms of +the GPL2 ("Copyleft"). + +Contact information +------------------- + +Circuits At Home, LTD +Web : http://www.circuitsathome.com +e-mail : support@circuitsathome.com + */ +#include "Usb.h" + +bool MultiByteValueParser::Parse(uint8_t **pp, uint16_t *pcntdn) { + if(!pBuf) { + Notify(PSTR("Buffer pointer is NULL!\r\n"), 0x80); + return false; + } + for(; countDown && (*pcntdn); countDown--, (*pcntdn)--, (*pp)++) + pBuf[valueSize - countDown] = (**pp); + + if(countDown) + return false; + + countDown = valueSize; + return true; +} + +bool PTPListParser::Parse(uint8_t **pp, uint16_t *pcntdn, PTP_ARRAY_EL_FUNC pf, const void *me) { + switch(nStage) { + case 0: + pBuf->valueSize = lenSize; + theParser.Initialize(pBuf); + nStage = 1; + + case 1: + if(!theParser.Parse(pp, pcntdn)) + return false; + + arLen = 0; + arLen = (pBuf->valueSize >= 4) ? *((uint32_t*)pBuf->pValue) : (uint32_t)(*((uint16_t*)pBuf->pValue)); + arLenCntdn = arLen; + nStage = 2; + + case 2: + pBuf->valueSize = valSize; + theParser.Initialize(pBuf); + nStage = 3; + + case 3: + for(; arLenCntdn; arLenCntdn--) { + if(!theParser.Parse(pp, pcntdn)) + return false; + + if(pf) + pf(pBuf, (arLen - arLenCntdn), me); + } + + nStage = 0; + } + return true; +} diff --git a/libraries/USB_Host_Shield/parsetools.h b/libraries/USB_Host_Shield/parsetools.h new file mode 100755 index 0000000..66e9531 --- /dev/null +++ b/libraries/USB_Host_Shield/parsetools.h @@ -0,0 +1,140 @@ +/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved. + +This software may be distributed and modified under the terms of the GNU +General Public License version 2 (GPL2) as published by the Free Software +Foundation and appearing in the file GPL2.TXT included in the packaging of +this file. Please note that GPL2 Section 2[b] requires that all works based +on this software must also be made publicly available under the terms of +the GPL2 ("Copyleft"). + +Contact information +------------------- + +Circuits At Home, LTD +Web : http://www.circuitsathome.com +e-mail : support@circuitsathome.com + */ + +#if !defined(_usb_h_) || defined(__PARSETOOLS_H__) +#error "Never include parsetools.h directly; include Usb.h instead" +#else +#define __PARSETOOLS_H__ + +struct MultiValueBuffer { + uint8_t valueSize; + void *pValue; +} __attribute__((packed)); + +class MultiByteValueParser { + uint8_t * pBuf; + uint8_t countDown; + uint8_t valueSize; + +public: + + MultiByteValueParser() : pBuf(NULL), countDown(0), valueSize(0) { + }; + + const uint8_t* GetBuffer() { + return pBuf; + }; + + void Initialize(MultiValueBuffer * const pbuf) { + pBuf = (uint8_t*)pbuf->pValue; + countDown = valueSize = pbuf->valueSize; + }; + + bool Parse(uint8_t **pp, uint16_t *pcntdn); +}; + +class ByteSkipper { + uint8_t *pBuf; + uint8_t nStage; + uint16_t countDown; + +public: + + ByteSkipper() : pBuf(NULL), nStage(0), countDown(0) { + }; + + void Initialize(MultiValueBuffer *pbuf) { + pBuf = (uint8_t*)pbuf->pValue; + countDown = 0; + }; + + bool Skip(uint8_t **pp, uint16_t *pcntdn, uint16_t bytes_to_skip) { + switch(nStage) { + case 0: + countDown = bytes_to_skip; + nStage++; + case 1: + for(; countDown && (*pcntdn); countDown--, (*pp)++, (*pcntdn)--); + + if(!countDown) + nStage = 0; + }; + return (!countDown); + }; +}; + +// Pointer to a callback function triggered for each element of PTP array when used with PTPArrayParser +typedef void (*PTP_ARRAY_EL_FUNC)(const MultiValueBuffer * const p, uint32_t count, const void *me); + +class PTPListParser { +public: + + enum ParseMode { + modeArray, modeRange/*, modeEnum*/ + }; + +private: + uint8_t nStage; + uint8_t enStage; + + uint32_t arLen; + uint32_t arLenCntdn; + + uint8_t lenSize; // size of the array length field in bytes + uint8_t valSize; // size of the array element in bytes + + MultiValueBuffer *pBuf; + + // The only parser for both size and array element parsing + MultiByteValueParser theParser; + + uint8_t /*ParseMode*/ prsMode; + +public: + + PTPListParser() : + nStage(0), + enStage(0), + arLen(0), + arLenCntdn(0), + lenSize(0), + valSize(0), + pBuf(NULL), + prsMode(modeArray) { + }; + + void Initialize(const uint8_t len_size, const uint8_t val_size, MultiValueBuffer * const p, const uint8_t mode = modeArray) { + pBuf = p; + lenSize = len_size; + valSize = val_size; + prsMode = mode; + + if(prsMode == modeRange) { + arLenCntdn = arLen = 3; + nStage = 2; + } else { + arLenCntdn = arLen = 0; + nStage = 0; + } + enStage = 0; + theParser.Initialize(p); + }; + + bool Parse(uint8_t **pp, uint16_t *pcntdn, PTP_ARRAY_EL_FUNC pf, const void *me = NULL); +}; + +#endif // __PARSETOOLS_H__ diff --git a/libraries/USB_Host_Shield/printhex.h b/libraries/USB_Host_Shield/printhex.h new file mode 100755 index 0000000..369d7e1 --- /dev/null +++ b/libraries/USB_Host_Shield/printhex.h @@ -0,0 +1,84 @@ +/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved. + +This software may be distributed and modified under the terms of the GNU +General Public License version 2 (GPL2) as published by the Free Software +Foundation and appearing in the file GPL2.TXT included in the packaging of +this file. Please note that GPL2 Section 2[b] requires that all works based +on this software must also be made publicly available under the terms of +the GPL2 ("Copyleft"). + +Contact information +------------------- + +Circuits At Home, LTD +Web : http://www.circuitsathome.com +e-mail : support@circuitsathome.com + */ + +#if !defined(_usb_h_) || defined(__PRINTHEX_H__) +#error "Never include printhex.h directly; include Usb.h instead" +#else +#define __PRINTHEX_H__ + +void E_Notifyc(char c, int lvl); + +template +void PrintHex(T val, int lvl) { + int num_nibbles = sizeof (T) * 2; + + do { + char v = 48 + (((val >> (num_nibbles - 1) * 4)) & 0x0f); + if(v > 57) v += 7; + E_Notifyc(v, lvl); + } while(--num_nibbles); +} + +template +void PrintBin(T val, int lvl) { + for(T mask = (((T)1) << ((sizeof (T) << 3) - 1)); mask; mask >>= 1) + if(val & mask) + E_Notifyc('1', lvl); + else + E_Notifyc('0', lvl); +} + +template +void SerialPrintHex(T val) { + int num_nibbles = sizeof (T) * 2; + + do { + char v = 48 + (((val >> (num_nibbles - 1) * 4)) & 0x0f); + if(v > 57) v += 7; + USB_HOST_SERIAL.print(v); + } while(--num_nibbles); +} + +template +void PrintHex2(Print *prn, T val) { + T mask = (((T)1) << (((sizeof (T) << 1) - 1) << 2)); + + while(mask > 1) { + if(val < mask) + prn->print("0"); + + mask >>= 4; + } + prn->print((T)val, HEX); +} + +template void D_PrintHex(T val, int lvl) { +#ifdef DEBUG_USB_HOST + PrintHex (val, lvl); +#endif +} + +template +void D_PrintBin(T val, int lvl) { +#ifdef DEBUG_USB_HOST + PrintBin (val, lvl); +#endif +} + + + +#endif // __PRINTHEX_H__ diff --git a/libraries/USB_Host_Shield/settings.h b/libraries/USB_Host_Shield/settings.h new file mode 100755 index 0000000..3028161 --- /dev/null +++ b/libraries/USB_Host_Shield/settings.h @@ -0,0 +1,138 @@ +/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved. + +This software may be distributed and modified under the terms of the GNU +General Public License version 2 (GPL2) as published by the Free Software +Foundation and appearing in the file GPL2.TXT included in the packaging of +this file. Please note that GPL2 Section 2[b] requires that all works based +on this software must also be made publicly available under the terms of +the GPL2 ("Copyleft"). + +Contact information +------------------- + +Circuits At Home, LTD +Web : http://www.circuitsathome.com +e-mail : support@circuitsathome.com + */ + +#ifndef USB_HOST_SHIELD_SETTINGS_H +#define USB_HOST_SHIELD_SETTINGS_H +#include "macros.h" + +//////////////////////////////////////////////////////////////////////////////// +// DEBUGGING +//////////////////////////////////////////////////////////////////////////////// + +/* Set this to 1 to activate serial debugging */ +#define ENABLE_UHS_DEBUGGING 0 + +/* This can be used to select which serial port to use for debugging if + * multiple serial ports are available. + * For example Serial3. + */ +#ifndef USB_HOST_SERIAL +#define USB_HOST_SERIAL Serial +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Manual board activation +//////////////////////////////////////////////////////////////////////////////// + +/* Set this to 1 if you are using an Arduino Mega ADK board with MAX3421e built-in */ +#define USE_UHS_MEGA_ADK 0 // If you are using Arduino 1.5.5 or newer there is no need to do this manually + +/* Set this to 1 if you are using a Black Widdow */ +#define USE_UHS_BLACK_WIDDOW 0 + +/* Set this to a one to use the xmem2 lock. This is needed for multitasking and threading */ +#define USE_XMEM_SPI_LOCK 0 + +//////////////////////////////////////////////////////////////////////////////// +// Wii IR camera +//////////////////////////////////////////////////////////////////////////////// + +/* Set this to 1 to activate code for the Wii IR camera */ +#define ENABLE_WII_IR_CAMERA 0 + +//////////////////////////////////////////////////////////////////////////////// +// MASS STORAGE +//////////////////////////////////////////////////////////////////////////////// +// <<<<<<<<<<<<<<<< IMPORTANT >>>>>>>>>>>>>>> +// Set this to 1 to support single LUN devices, and save RAM. -- I.E. thumb drives. +// Each LUN needs ~13 bytes to be able to track the state of each unit. +#ifndef MASS_MAX_SUPPORTED_LUN +#define MASS_MAX_SUPPORTED_LUN 8 +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Set to 1 to use the faster spi4teensy3 driver. +//////////////////////////////////////////////////////////////////////////////// +#ifndef USE_SPI4TEENSY3 +#define USE_SPI4TEENSY3 1 +#endif + +//////////////////////////////////////////////////////////////////////////////// +// AUTOMATIC Settings +//////////////////////////////////////////////////////////////////////////////// + +// No user serviceable parts below this line. +// DO NOT change anything below here unless you are a developer! + +#include "version_helper.h" + +#if defined(__GNUC__) && defined(__AVR__) +#ifndef GCC_VERSION +#define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) +#endif +#if GCC_VERSION < 40602 // Test for GCC < 4.6.2 +#ifdef PROGMEM +#undef PROGMEM +#define PROGMEM __attribute__((section(".progmem.data"))) // Workaround for http://gcc.gnu.org/bugzilla/show_bug.cgi?id=34734#c4 +#ifdef PSTR +#undef PSTR +#define PSTR(s) (__extension__({static const char __c[] PROGMEM = (s); &__c[0];})) // Copied from pgmspace.h in avr-libc source +#endif +#endif +#endif +#endif + +#if !defined(DEBUG_USB_HOST) && ENABLE_UHS_DEBUGGING +#define DEBUG_USB_HOST +#endif + +#if !defined(WIICAMERA) && ENABLE_WII_IR_CAMERA +#define WIICAMERA +#endif + +// To use some other locking (e.g. freertos), +// define XMEM_ACQUIRE_SPI and XMEM_RELEASE_SPI to point to your lock and unlock. +// NOTE: NO argument is passed. You have to do this within your routine for +// whatever you are using to lock and unlock. +#if !defined(XMEM_ACQUIRE_SPI) +#if USE_XMEM_SPI_LOCK || defined(USE_MULTIPLE_APP_API) +#include +#else +#define XMEM_ACQUIRE_SPI() (void(0)) +#define XMEM_RELEASE_SPI() (void(0)) +#endif +#endif + +#if !defined(EXT_RAM) && defined(EXT_RAM_STACK) || defined(EXT_RAM_HEAP) +#include +#else +#define EXT_RAM 0 +#endif + +#if defined(CORE_TEENSY) && (defined(__MK20DX128__) || defined(__MK20DX256__)) +#define USING_SPI4TEENSY3 USE_SPI4TEENSY3 +#else +#define USING_SPI4TEENSY3 0 +#endif + +#if (defined(ARDUINO_SAM_DUE) && defined(__SAM3X8E__)) || defined(RBL_NRF51822) +#include // Use the Arduino SPI library for the Arduino Due and RedBearLab nRF51822 +#endif +#if defined(__PIC32MX__) || defined(__PIC32MZ__) +#include <../../../../hardware/pic32/libraries/SPI/SPI.h> // Hack to use the SPI library +#endif +#endif /* SETTINGS_H */ diff --git a/libraries/USB_Host_Shield/sink_parser.h b/libraries/USB_Host_Shield/sink_parser.h new file mode 100755 index 0000000..a23637d --- /dev/null +++ b/libraries/USB_Host_Shield/sink_parser.h @@ -0,0 +1,41 @@ +/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved. + +This software may be distributed and modified under the terms of the GNU +General Public License version 2 (GPL2) as published by the Free Software +Foundation and appearing in the file GPL2.TXT included in the packaging of +this file. Please note that GPL2 Section 2[b] requires that all works based +on this software must also be made publicly available under the terms of +the GPL2 ("Copyleft"). + +Contact information +------------------- + +Circuits At Home, LTD +Web : http://www.circuitsathome.com +e-mail : support@circuitsathome.com + */ +#if !defined(_usb_h_) || defined(__SINK_PARSER_H__) +#error "Never include hexdump.h directly; include Usb.h instead" +#else +#define __SINK_PARSER_H__ + +extern int UsbDEBUGlvl; + +// This parser does absolutely nothing with the data, just swallows it. + +template +class SinkParser : public BASE_CLASS { +public: + + SinkParser() { + }; + + void Initialize() { + }; + + void Parse(const LEN_TYPE len, const uint8_t *pbuf, const OFFSET_TYPE &offset) { + }; +}; + + +#endif // __HEXDUMP_H__ diff --git a/libraries/USB_Host_Shield/usb_ch9.h b/libraries/USB_Host_Shield/usb_ch9.h new file mode 100755 index 0000000..18f2d3e --- /dev/null +++ b/libraries/USB_Host_Shield/usb_ch9.h @@ -0,0 +1,166 @@ +/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved. + +This software may be distributed and modified under the terms of the GNU +General Public License version 2 (GPL2) as published by the Free Software +Foundation and appearing in the file GPL2.TXT included in the packaging of +this file. Please note that GPL2 Section 2[b] requires that all works based +on this software must also be made publicly available under the terms of +the GPL2 ("Copyleft"). + +Contact information +------------------- + +Circuits At Home, LTD +Web : http://www.circuitsathome.com +e-mail : support@circuitsathome.com + */ + +#if !defined(_usb_h_) || defined(_ch9_h_) +#error "Never include usb_ch9.h directly; include Usb.h instead" +#else + +/* USB chapter 9 structures */ +#define _ch9_h_ + +/* Misc.USB constants */ +#define DEV_DESCR_LEN 18 //device descriptor length +#define CONF_DESCR_LEN 9 //configuration descriptor length +#define INTR_DESCR_LEN 9 //interface descriptor length +#define EP_DESCR_LEN 7 //endpoint descriptor length + +/* Standard Device Requests */ + +#define USB_REQUEST_GET_STATUS 0 // Standard Device Request - GET STATUS +#define USB_REQUEST_CLEAR_FEATURE 1 // Standard Device Request - CLEAR FEATURE +#define USB_REQUEST_SET_FEATURE 3 // Standard Device Request - SET FEATURE +#define USB_REQUEST_SET_ADDRESS 5 // Standard Device Request - SET ADDRESS +#define USB_REQUEST_GET_DESCRIPTOR 6 // Standard Device Request - GET DESCRIPTOR +#define USB_REQUEST_SET_DESCRIPTOR 7 // Standard Device Request - SET DESCRIPTOR +#define USB_REQUEST_GET_CONFIGURATION 8 // Standard Device Request - GET CONFIGURATION +#define USB_REQUEST_SET_CONFIGURATION 9 // Standard Device Request - SET CONFIGURATION +#define USB_REQUEST_GET_INTERFACE 10 // Standard Device Request - GET INTERFACE +#define USB_REQUEST_SET_INTERFACE 11 // Standard Device Request - SET INTERFACE +#define USB_REQUEST_SYNCH_FRAME 12 // Standard Device Request - SYNCH FRAME + +#define USB_FEATURE_ENDPOINT_HALT 0 // CLEAR/SET FEATURE - Endpoint Halt +#define USB_FEATURE_DEVICE_REMOTE_WAKEUP 1 // CLEAR/SET FEATURE - Device remote wake-up +#define USB_FEATURE_TEST_MODE 2 // CLEAR/SET FEATURE - Test mode + +/* Setup Data Constants */ + +#define USB_SETUP_HOST_TO_DEVICE 0x00 // Device Request bmRequestType transfer direction - host to device transfer +#define USB_SETUP_DEVICE_TO_HOST 0x80 // Device Request bmRequestType transfer direction - device to host transfer +#define USB_SETUP_TYPE_STANDARD 0x00 // Device Request bmRequestType type - standard +#define USB_SETUP_TYPE_CLASS 0x20 // Device Request bmRequestType type - class +#define USB_SETUP_TYPE_VENDOR 0x40 // Device Request bmRequestType type - vendor +#define USB_SETUP_RECIPIENT_DEVICE 0x00 // Device Request bmRequestType recipient - device +#define USB_SETUP_RECIPIENT_INTERFACE 0x01 // Device Request bmRequestType recipient - interface +#define USB_SETUP_RECIPIENT_ENDPOINT 0x02 // Device Request bmRequestType recipient - endpoint +#define USB_SETUP_RECIPIENT_OTHER 0x03 // Device Request bmRequestType recipient - other + +/* USB descriptors */ + +#define USB_DESCRIPTOR_DEVICE 0x01 // bDescriptorType for a Device Descriptor. +#define USB_DESCRIPTOR_CONFIGURATION 0x02 // bDescriptorType for a Configuration Descriptor. +#define USB_DESCRIPTOR_STRING 0x03 // bDescriptorType for a String Descriptor. +#define USB_DESCRIPTOR_INTERFACE 0x04 // bDescriptorType for an Interface Descriptor. +#define USB_DESCRIPTOR_ENDPOINT 0x05 // bDescriptorType for an Endpoint Descriptor. +#define USB_DESCRIPTOR_DEVICE_QUALIFIER 0x06 // bDescriptorType for a Device Qualifier. +#define USB_DESCRIPTOR_OTHER_SPEED 0x07 // bDescriptorType for a Other Speed Configuration. +#define USB_DESCRIPTOR_INTERFACE_POWER 0x08 // bDescriptorType for Interface Power. +#define USB_DESCRIPTOR_OTG 0x09 // bDescriptorType for an OTG Descriptor. + +#define HID_DESCRIPTOR_HID 0x21 + + + +/* OTG SET FEATURE Constants */ +#define OTG_FEATURE_B_HNP_ENABLE 3 // SET FEATURE OTG - Enable B device to perform HNP +#define OTG_FEATURE_A_HNP_SUPPORT 4 // SET FEATURE OTG - A device supports HNP +#define OTG_FEATURE_A_ALT_HNP_SUPPORT 5 // SET FEATURE OTG - Another port on the A device supports HNP + +/* USB Endpoint Transfer Types */ +#define USB_TRANSFER_TYPE_CONTROL 0x00 // Endpoint is a control endpoint. +#define USB_TRANSFER_TYPE_ISOCHRONOUS 0x01 // Endpoint is an isochronous endpoint. +#define USB_TRANSFER_TYPE_BULK 0x02 // Endpoint is a bulk endpoint. +#define USB_TRANSFER_TYPE_INTERRUPT 0x03 // Endpoint is an interrupt endpoint. +#define bmUSB_TRANSFER_TYPE 0x03 // bit mask to separate transfer type from ISO attributes + + +/* Standard Feature Selectors for CLEAR_FEATURE Requests */ +#define USB_FEATURE_ENDPOINT_STALL 0 // Endpoint recipient +#define USB_FEATURE_DEVICE_REMOTE_WAKEUP 1 // Device recipient +#define USB_FEATURE_TEST_MODE 2 // Device recipient + +/* descriptor data structures */ + +/* Device descriptor structure */ +typedef struct { + uint8_t bLength; // Length of this descriptor. + uint8_t bDescriptorType; // DEVICE descriptor type (USB_DESCRIPTOR_DEVICE). + uint16_t bcdUSB; // USB Spec Release Number (BCD). + uint8_t bDeviceClass; // Class code (assigned by the USB-IF). 0xFF-Vendor specific. + uint8_t bDeviceSubClass; // Subclass code (assigned by the USB-IF). + uint8_t bDeviceProtocol; // Protocol code (assigned by the USB-IF). 0xFF-Vendor specific. + uint8_t bMaxPacketSize0; // Maximum packet size for endpoint 0. + uint16_t idVendor; // Vendor ID (assigned by the USB-IF). + uint16_t idProduct; // Product ID (assigned by the manufacturer). + uint16_t bcdDevice; // Device release number (BCD). + uint8_t iManufacturer; // Index of String Descriptor describing the manufacturer. + uint8_t iProduct; // Index of String Descriptor describing the product. + uint8_t iSerialNumber; // Index of String Descriptor with the device's serial number. + uint8_t bNumConfigurations; // Number of possible configurations. +} __attribute__((packed)) USB_DEVICE_DESCRIPTOR; + +/* Configuration descriptor structure */ +typedef struct { + uint8_t bLength; // Length of this descriptor. + uint8_t bDescriptorType; // CONFIGURATION descriptor type (USB_DESCRIPTOR_CONFIGURATION). + uint16_t wTotalLength; // Total length of all descriptors for this configuration. + uint8_t bNumInterfaces; // Number of interfaces in this configuration. + uint8_t bConfigurationValue; // Value of this configuration (1 based). + uint8_t iConfiguration; // Index of String Descriptor describing the configuration. + uint8_t bmAttributes; // Configuration characteristics. + uint8_t bMaxPower; // Maximum power consumed by this configuration. +} __attribute__((packed)) USB_CONFIGURATION_DESCRIPTOR; + +/* Interface descriptor structure */ +typedef struct { + uint8_t bLength; // Length of this descriptor. + uint8_t bDescriptorType; // INTERFACE descriptor type (USB_DESCRIPTOR_INTERFACE). + uint8_t bInterfaceNumber; // Number of this interface (0 based). + uint8_t bAlternateSetting; // Value of this alternate interface setting. + uint8_t bNumEndpoints; // Number of endpoints in this interface. + uint8_t bInterfaceClass; // Class code (assigned by the USB-IF). 0xFF-Vendor specific. + uint8_t bInterfaceSubClass; // Subclass code (assigned by the USB-IF). + uint8_t bInterfaceProtocol; // Protocol code (assigned by the USB-IF). 0xFF-Vendor specific. + uint8_t iInterface; // Index of String Descriptor describing the interface. +} __attribute__((packed)) USB_INTERFACE_DESCRIPTOR; + +/* Endpoint descriptor structure */ +typedef struct { + uint8_t bLength; // Length of this descriptor. + uint8_t bDescriptorType; // ENDPOINT descriptor type (USB_DESCRIPTOR_ENDPOINT). + uint8_t bEndpointAddress; // Endpoint address. Bit 7 indicates direction (0=OUT, 1=IN). + uint8_t bmAttributes; // Endpoint transfer type. + uint16_t wMaxPacketSize; // Maximum packet size. + uint8_t bInterval; // Polling interval in frames. +} __attribute__((packed)) USB_ENDPOINT_DESCRIPTOR; + +/* HID descriptor */ +typedef struct { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t bcdHID; // HID class specification release + uint8_t bCountryCode; + uint8_t bNumDescriptors; // Number of additional class specific descriptors + uint8_t bDescrType; // Type of class descriptor + uint16_t wDescriptorLength; // Total size of the Report descriptor +} __attribute__((packed)) USB_HID_DESCRIPTOR; + +typedef struct { + uint8_t bDescrType; // Type of class descriptor + uint16_t wDescriptorLength; // Total size of the Report descriptor +} __attribute__((packed)) HID_CLASS_DESCRIPTOR_LEN_AND_TYPE; + +#endif // _ch9_h_ diff --git a/libraries/USB_Host_Shield/usbhost.h b/libraries/USB_Host_Shield/usbhost.h new file mode 100755 index 0000000..7d21dc5 --- /dev/null +++ b/libraries/USB_Host_Shield/usbhost.h @@ -0,0 +1,467 @@ +/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved. + +This software may be distributed and modified under the terms of the GNU +General Public License version 2 (GPL2) as published by the Free Software +Foundation and appearing in the file GPL2.TXT included in the packaging of +this file. Please note that GPL2 Section 2[b] requires that all works based +on this software must also be made publicly available under the terms of +the GPL2 ("Copyleft"). + +Contact information +------------------- + +Circuits At Home, LTD +Web : http://www.circuitsathome.com +e-mail : support@circuitsathome.com + */ +/* MAX3421E-based USB Host Library header file */ + + +#if !defined(_usb_h_) || defined(_USBHOST_H_) +#error "Never include usbhost.h directly; include Usb.h instead" +#else +#define _USBHOST_H_ + +#if USING_SPI4TEENSY3 +#include +#include +#endif + +/* SPI initialization */ +template< typename SPI_CLK, typename SPI_MOSI, typename SPI_MISO, typename SPI_SS > class SPi { +public: +#if USING_SPI4TEENSY3 + static void init() { + // spi4teensy3 inits everything for us, except /SS + // CLK, MOSI and MISO are hard coded for now. + // spi4teensy3::init(0,0,0); // full speed, cpol 0, cpha 0 + spi4teensy3::init(); // full speed, cpol 0, cpha 0 + SPI_SS::SetDirWrite(); + SPI_SS::Set(); + } +#elif !defined(SPDR) + static void init() { + SPI_SS::SetDirWrite(); + SPI_SS::Set(); + SPI.begin(); +#if defined(__MIPSEL__) + SPI.setClockDivider(1); +#else + SPI.setClockDivider(4); // Set speed to 84MHz/4=21MHz - the MAX3421E can handle up to 26MHz +#endif + } +#elif defined(RBL_NRF51822) + static void init() { + SPI_SS::SetDirWrite(); + SPI_SS::Set(); + SPI.begin(); + // SPI.setFrequency(SPI_FREQUENCY_8M); + } +#else + static void init() { + //uint8_t tmp; + SPI_CLK::SetDirWrite(); + SPI_MOSI::SetDirWrite(); + SPI_MISO::SetDirRead(); + SPI_SS::SetDirWrite(); + /* mode 00 (CPOL=0, CPHA=0) master, fclk/2. Mode 11 (CPOL=11, CPHA=11) is also supported by MAX3421E */ + SPCR = 0x50; + SPSR = 0x01; // 0x01 + /**/ + //tmp = SPSR; + //tmp = SPDR; + } +#endif +}; + +/* SPI pin definitions. see avrpins.h */ +#if defined(__AVR_ATmega1280__) || (__AVR_ATmega2560__) || defined(__AVR_ATmega32U4__) || defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__) +typedef SPi< Pb1, Pb2, Pb3, Pb0 > spi; +#elif defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__) +typedef SPi< Pb5, Pb3, Pb4, Pb2 > spi; +#elif defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) +typedef SPi< Pb7, Pb5, Pb6, Pb4 > spi; +#elif defined(CORE_TEENSY) && (defined(__MK20DX128__) || defined(__MK20DX256__)) +typedef SPi< P13, P11, P12, P10 > spi; +#elif defined(ARDUINO_SAM_DUE) && defined(__SAM3X8E__) +typedef SPi< P76, P75, P74, P10 > spi; +#elif defined(RBL_NRF51822) +typedef SPi< P16, P18, P17, P10 > spi; +#elif defined(__MIPSEL__) +typedef SPi< P13, P11, P12, P10 > spi; +#else +#error "No SPI entry in usbhost.h" +#endif + +typedef enum { + vbus_on = 0, + vbus_off = GPX_VBDET +} VBUS_t; + +template< typename SPI_SS, typename INTR > class MAX3421e /* : public spi */ { + static uint8_t vbusState; + +public: + MAX3421e(); + void regWr(uint8_t reg, uint8_t data); + uint8_t* bytesWr(uint8_t reg, uint8_t nbytes, uint8_t* data_p); + void gpioWr(uint8_t data); + uint8_t regRd(uint8_t reg); + uint8_t* bytesRd(uint8_t reg, uint8_t nbytes, uint8_t* data_p); + uint8_t gpioRd(); + uint16_t reset(); + int8_t Init(); + int8_t Init(int mseconds); + + void vbusPower(VBUS_t state) { + regWr(rPINCTL, (bmFDUPSPI | bmINTLEVEL | state)); + } + + uint8_t getVbusState(void) { + return vbusState; + }; + void busprobe(); + uint8_t GpxHandler(); + uint8_t IntHandler(); + uint8_t Task(); +}; + +template< typename SPI_SS, typename INTR > + uint8_t MAX3421e< SPI_SS, INTR >::vbusState = 0; + +/* constructor */ +template< typename SPI_SS, typename INTR > +MAX3421e< SPI_SS, INTR >::MAX3421e() { + // Leaving ADK hardware setup in here, for now. This really belongs with the other parts. +#ifdef BOARD_MEGA_ADK + // For Mega ADK, which has a Max3421e on-board, set MAX_RESET to output mode, and then set it to HIGH + P55::SetDirWrite(); + P55::Set(); +#endif +}; + +/* write single byte into MAX3421 register */ +template< typename SPI_SS, typename INTR > +void MAX3421e< SPI_SS, INTR >::regWr(uint8_t reg, uint8_t data) { + XMEM_ACQUIRE_SPI(); + SPI_SS::Clear(); +#if USING_SPI4TEENSY3 + uint8_t c[2]; + c[0] = reg | 0x02; + c[1] = data; + spi4teensy3::send(c, 2); +#elif !defined(SPDR) + SPI.transfer(reg | 0x02); + SPI.transfer(data); +#else + SPDR = (reg | 0x02); + while(!(SPSR & (1 << SPIF))); + SPDR = data; + while(!(SPSR & (1 << SPIF))); +#endif + SPI_SS::Set(); + XMEM_RELEASE_SPI(); + return; +}; +/* multiple-byte write */ + +/* returns a pointer to memory position after last written */ +template< typename SPI_SS, typename INTR > +uint8_t* MAX3421e< SPI_SS, INTR >::bytesWr(uint8_t reg, uint8_t nbytes, uint8_t* data_p) { + XMEM_ACQUIRE_SPI(); + SPI_SS::Clear(); +#if USING_SPI4TEENSY3 + spi4teensy3::send(reg | 0x02); + spi4teensy3::send(data_p, nbytes); + data_p += nbytes; +#elif !defined(SPDR) + SPI.transfer(reg | 0x02); + while(nbytes) { + SPI.transfer(*data_p); + nbytes--; + data_p++; // advance data pointer + } +#else + SPDR = (reg | 0x02); //set WR bit and send register number + while(nbytes) { + while(!(SPSR & (1 << SPIF))); //check if previous byte was sent + SPDR = (*data_p); // send next data byte + nbytes--; + data_p++; // advance data pointer + } + while(!(SPSR & (1 << SPIF))); +#endif + SPI_SS::Set(); + XMEM_RELEASE_SPI(); + return ( data_p); +} +/* GPIO write */ +/*GPIO byte is split between 2 registers, so two writes are needed to write one byte */ + +/* GPOUT bits are in the low nibble. 0-3 in IOPINS1, 4-7 in IOPINS2 */ +template< typename SPI_SS, typename INTR > +void MAX3421e< SPI_SS, INTR >::gpioWr(uint8_t data) { + regWr(rIOPINS1, data); + data >>= 4; + regWr(rIOPINS2, data); + return; +} + +/* single host register read */ +template< typename SPI_SS, typename INTR > +uint8_t MAX3421e< SPI_SS, INTR >::regRd(uint8_t reg) { + XMEM_ACQUIRE_SPI(); + SPI_SS::Clear(); +#if USING_SPI4TEENSY3 + spi4teensy3::send(reg); + uint8_t rv = spi4teensy3::receive(); + SPI_SS::Set(); +#elif !defined(SPDR) + SPI.transfer(reg); + uint8_t rv = SPI.transfer(0); + SPI_SS::Set(); +#else + SPDR = reg; + while(!(SPSR & (1 << SPIF))); + SPDR = 0; //send empty byte + while(!(SPSR & (1 << SPIF))); + SPI_SS::Set(); + uint8_t rv = SPDR; +#endif + XMEM_RELEASE_SPI(); + return (rv); +} +/* multiple-byte register read */ + +/* returns a pointer to a memory position after last read */ +template< typename SPI_SS, typename INTR > +uint8_t* MAX3421e< SPI_SS, INTR >::bytesRd(uint8_t reg, uint8_t nbytes, uint8_t* data_p) { + XMEM_ACQUIRE_SPI(); + SPI_SS::Clear(); +#if USING_SPI4TEENSY3 + spi4teensy3::send(reg); + spi4teensy3::receive(data_p, nbytes); + data_p += nbytes; +#elif !defined(SPDR) + SPI.transfer(reg); + while(nbytes) { + *data_p++ = SPI.transfer(0); + nbytes--; + } +#else + SPDR = reg; + while(!(SPSR & (1 << SPIF))); //wait + while(nbytes) { + SPDR = 0; //send empty byte + nbytes--; + while(!(SPSR & (1 << SPIF))); +#if 0 + { + *data_p = SPDR; + printf("%2.2x ", *data_p); + } + data_p++; + } + printf("\r\n"); +#else + *data_p++ = SPDR; + } +#endif +#endif + SPI_SS::Set(); + XMEM_RELEASE_SPI(); + return ( data_p); +} +/* GPIO read. See gpioWr for explanation */ + +/* GPIN pins are in high nibbles of IOPINS1, IOPINS2 */ +template< typename SPI_SS, typename INTR > +uint8_t MAX3421e< SPI_SS, INTR >::gpioRd() { + uint8_t gpin = 0; + gpin = regRd(rIOPINS2); //pins 4-7 + gpin &= 0xf0; //clean lower nibble + gpin |= (regRd(rIOPINS1) >> 4); //shift low bits and OR with upper from previous operation. + return ( gpin); +} + +/* reset MAX3421E. Returns number of cycles it took for PLL to stabilize after reset + or zero if PLL haven't stabilized in 65535 cycles */ +template< typename SPI_SS, typename INTR > +uint16_t MAX3421e< SPI_SS, INTR >::reset() { + uint16_t i = 0; + regWr(rUSBCTL, bmCHIPRES); + regWr(rUSBCTL, 0x00); + while(++i) { + if((regRd(rUSBIRQ) & bmOSCOKIRQ)) { + break; + } + } + return ( i); +} + +/* initialize MAX3421E. Set Host mode, pullups, and stuff. Returns 0 if success, -1 if not */ +template< typename SPI_SS, typename INTR > +int8_t MAX3421e< SPI_SS, INTR >::Init() { + XMEM_ACQUIRE_SPI(); + // Moved here. + // you really should not init hardware in the constructor when it involves locks. + // Also avoids the vbus flicker issue confusing some devices. + /* pin and peripheral setup */ + SPI_SS::SetDirWrite(); + SPI_SS::Set(); + spi::init(); + INTR::SetDirRead(); + XMEM_RELEASE_SPI(); + /* MAX3421E - full-duplex SPI, level interrupt */ + // GPX pin on. Moved here, otherwise we flicker the vbus. + regWr(rPINCTL, (bmFDUPSPI | bmINTLEVEL)); + + if(reset() == 0) { //OSCOKIRQ hasn't asserted in time + return ( -1); + } + + regWr(rMODE, bmDPPULLDN | bmDMPULLDN | bmHOST); // set pull-downs, Host + + regWr(rHIEN, bmCONDETIE | bmFRAMEIE); //connection detection + + /* check if device is connected */ + regWr(rHCTL, bmSAMPLEBUS); // sample USB bus + while(!(regRd(rHCTL) & bmSAMPLEBUS)); //wait for sample operation to finish + + busprobe(); //check if anything is connected + + regWr(rHIRQ, bmCONDETIRQ); //clear connection detect interrupt + regWr(rCPUCTL, 0x01); //enable interrupt pin + + return ( 0); +} + +/* initialize MAX3421E. Set Host mode, pullups, and stuff. Returns 0 if success, -1 if not */ +template< typename SPI_SS, typename INTR > +int8_t MAX3421e< SPI_SS, INTR >::Init(int mseconds) { + XMEM_ACQUIRE_SPI(); + // Moved here. + // you really should not init hardware in the constructor when it involves locks. + // Also avoids the vbus flicker issue confusing some devices. + /* pin and peripheral setup */ + SPI_SS::SetDirWrite(); + SPI_SS::Set(); + spi::init(); + INTR::SetDirRead(); + XMEM_RELEASE_SPI(); + /* MAX3421E - full-duplex SPI, level interrupt, vbus off */ + regWr(rPINCTL, (bmFDUPSPI | bmINTLEVEL | GPX_VBDET)); + + if(reset() == 0) { //OSCOKIRQ hasn't asserted in time + return ( -1); + } + + // Delay a minimum of 1 second to ensure any capacitors are drained. + // 1 second is required to make sure we do not smoke a Microdrive! + if(mseconds < 1000) mseconds = 1000; + delay(mseconds); + + regWr(rMODE, bmDPPULLDN | bmDMPULLDN | bmHOST); // set pull-downs, Host + + regWr(rHIEN, bmCONDETIE | bmFRAMEIE); //connection detection + + /* check if device is connected */ + regWr(rHCTL, bmSAMPLEBUS); // sample USB bus + while(!(regRd(rHCTL) & bmSAMPLEBUS)); //wait for sample operation to finish + + busprobe(); //check if anything is connected + + regWr(rHIRQ, bmCONDETIRQ); //clear connection detect interrupt + regWr(rCPUCTL, 0x01); //enable interrupt pin + + // GPX pin on. This is done here so that busprobe will fail if we have a switch connected. + regWr(rPINCTL, (bmFDUPSPI | bmINTLEVEL)); + + return ( 0); +} + +/* probe bus to determine device presence and speed and switch host to this speed */ +template< typename SPI_SS, typename INTR > +void MAX3421e< SPI_SS, INTR >::busprobe() { + uint8_t bus_sample; + bus_sample = regRd(rHRSL); //Get J,K status + bus_sample &= (bmJSTATUS | bmKSTATUS); //zero the rest of the byte + switch(bus_sample) { //start full-speed or low-speed host + case( bmJSTATUS): + if((regRd(rMODE) & bmLOWSPEED) == 0) { + regWr(rMODE, MODE_FS_HOST); //start full-speed host + vbusState = FSHOST; + } else { + regWr(rMODE, MODE_LS_HOST); //start low-speed host + vbusState = LSHOST; + } + break; + case( bmKSTATUS): + if((regRd(rMODE) & bmLOWSPEED) == 0) { + regWr(rMODE, MODE_LS_HOST); //start low-speed host + vbusState = LSHOST; + } else { + regWr(rMODE, MODE_FS_HOST); //start full-speed host + vbusState = FSHOST; + } + break; + case( bmSE1): //illegal state + vbusState = SE1; + break; + case( bmSE0): //disconnected state + regWr(rMODE, bmDPPULLDN | bmDMPULLDN | bmHOST | bmSEPIRQ); + vbusState = SE0; + break; + }//end switch( bus_sample ) +} + +/* MAX3421 state change task and interrupt handler */ +template< typename SPI_SS, typename INTR > +uint8_t MAX3421e< SPI_SS, INTR >::Task(void) { + uint8_t rcode = 0; + uint8_t pinvalue; + //USB_HOST_SERIAL.print("Vbus state: "); + //USB_HOST_SERIAL.println( vbusState, HEX ); + pinvalue = INTR::IsSet(); //Read(); + //pinvalue = digitalRead( MAX_INT ); + if(pinvalue == 0) { + rcode = IntHandler(); + } + // pinvalue = digitalRead( MAX_GPX ); + // if( pinvalue == LOW ) { + // GpxHandler(); + // } + // usbSM(); //USB state machine + return ( rcode); +} + +template< typename SPI_SS, typename INTR > +uint8_t MAX3421e< SPI_SS, INTR >::IntHandler() { + uint8_t HIRQ; + uint8_t HIRQ_sendback = 0x00; + HIRQ = regRd(rHIRQ); //determine interrupt source + //if( HIRQ & bmFRAMEIRQ ) { //->1ms SOF interrupt handler + // HIRQ_sendback |= bmFRAMEIRQ; + //}//end FRAMEIRQ handling + if(HIRQ & bmCONDETIRQ) { + busprobe(); + HIRQ_sendback |= bmCONDETIRQ; + } + /* End HIRQ interrupts handling, clear serviced IRQs */ + regWr(rHIRQ, HIRQ_sendback); + return ( HIRQ_sendback); +} +//template< typename SPI_SS, typename INTR > +//uint8_t MAX3421e< SPI_SS, INTR >::GpxHandler() +//{ +// uint8_t GPINIRQ = regRd( rGPINIRQ ); //read GPIN IRQ register +//// if( GPINIRQ & bmGPINIRQ7 ) { //vbus overload +//// vbusPwr( OFF ); //attempt powercycle +//// delay( 1000 ); +//// vbusPwr( ON ); +//// regWr( rGPINIRQ, bmGPINIRQ7 ); +//// } +// return( GPINIRQ ); +//} + +#endif //_USBHOST_H_ diff --git a/libraries/USB_Host_Shield/usbhub.cpp b/libraries/USB_Host_Shield/usbhub.cpp new file mode 100755 index 0000000..7fed48e --- /dev/null +++ b/libraries/USB_Host_Shield/usbhub.cpp @@ -0,0 +1,425 @@ +/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved. + +This software may be distributed and modified under the terms of the GNU +General Public License version 2 (GPL2) as published by the Free Software +Foundation and appearing in the file GPL2.TXT included in the packaging of +this file. Please note that GPL2 Section 2[b] requires that all works based +on this software must also be made publicly available under the terms of +the GPL2 ("Copyleft"). + +Contact information +------------------- + +Circuits At Home, LTD +Web : http://www.circuitsathome.com +e-mail : support@circuitsathome.com + */ +#include "usbhub.h" + +bool USBHub::bResetInitiated = false; + +USBHub::USBHub(USB *p) : +pUsb(p), +bAddress(0), +bNbrPorts(0), +//bInitState(0), +qNextPollTime(0), +bPollEnable(false) { + epInfo[0].epAddr = 0; + epInfo[0].maxPktSize = 8; + epInfo[0].epAttribs = 0; + epInfo[0].bmNakPower = USB_NAK_MAX_POWER; + + epInfo[1].epAddr = 1; + epInfo[1].maxPktSize = 8; //kludge + epInfo[1].epAttribs = 0; + epInfo[1].bmNakPower = USB_NAK_NOWAIT; + + if(pUsb) + pUsb->RegisterDeviceClass(this); +} + +uint8_t USBHub::Init(uint8_t parent, uint8_t port, bool lowspeed) { + uint8_t buf[32]; + USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast(buf); + HubDescriptor* hd = reinterpret_cast(buf); + USB_CONFIGURATION_DESCRIPTOR * ucd = reinterpret_cast(buf); + uint8_t rcode; + UsbDevice *p = NULL; + EpInfo *oldep_ptr = NULL; + uint8_t len = 0; + uint16_t cd_len = 0; + + //USBTRACE("\r\nHub Init Start "); + //D_PrintHex (bInitState, 0x80); + + AddressPool &addrPool = pUsb->GetAddressPool(); + + //switch (bInitState) { + // case 0: + if(bAddress) + return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE; + + // Get pointer to pseudo device with address 0 assigned + p = addrPool.GetUsbDevicePtr(0); + + if(!p) + return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; + + if(!p->epinfo) + return USB_ERROR_EPINFO_IS_NULL; + + // Save old pointer to EP_RECORD of address 0 + oldep_ptr = p->epinfo; + + // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence + p->epinfo = epInfo; + + p->lowspeed = lowspeed; + + // Get device descriptor + rcode = pUsb->getDevDescr(0, 0, 8, (uint8_t*)buf); + + p->lowspeed = false; + + if(!rcode) + len = (buf[0] > 32) ? 32 : buf[0]; + + if(rcode) { + // Restore p->epinfo + p->epinfo = oldep_ptr; + return rcode; + } + + // Extract device class from device descriptor + // If device class is not a hub return + if(udd->bDeviceClass != 0x09) + return USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED; + + // Allocate new address according to device class + bAddress = addrPool.AllocAddress(parent, (udd->bDeviceClass == 0x09) ? true : false, port); + + if(!bAddress) + return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL; + + // Extract Max Packet Size from the device descriptor + epInfo[0].maxPktSize = udd->bMaxPacketSize0; + + // Assign new address to the device + rcode = pUsb->setAddr(0, 0, bAddress); + + if(rcode) { + // Restore p->epinfo + p->epinfo = oldep_ptr; + addrPool.FreeAddress(bAddress); + bAddress = 0; + return rcode; + } + + //USBTRACE2("\r\nHub address: ", bAddress ); + + // Restore p->epinfo + p->epinfo = oldep_ptr; + + if(len) + rcode = pUsb->getDevDescr(bAddress, 0, len, (uint8_t*)buf); + + if(rcode) + goto FailGetDevDescr; + + // Assign epInfo to epinfo pointer + rcode = pUsb->setEpInfoEntry(bAddress, 2, epInfo); + + if(rcode) + goto FailSetDevTblEntry; + + // bInitState = 1; + + // case 1: + // Get hub descriptor + rcode = GetHubDescriptor(0, 8, buf); + + if(rcode) + goto FailGetHubDescr; + + // Save number of ports for future use + bNbrPorts = hd->bNbrPorts; + + // bInitState = 2; + + // case 2: + // Read configuration Descriptor in Order To Obtain Proper Configuration Value + rcode = pUsb->getConfDescr(bAddress, 0, 8, 0, buf); + + if(!rcode) { + cd_len = ucd->wTotalLength; + rcode = pUsb->getConfDescr(bAddress, 0, cd_len, 0, buf); + } + if(rcode) + goto FailGetConfDescr; + + // The following code is of no practical use in real life applications. + // It only intended for the usb protocol sniffer to properly parse hub-class requests. + { + uint8_t buf2[24]; + + rcode = pUsb->getConfDescr(bAddress, 0, buf[0], 0, buf2); + + if(rcode) + goto FailGetConfDescr; + } + + // Set Configuration Value + rcode = pUsb->setConf(bAddress, 0, buf[5]); + + if(rcode) + goto FailSetConfDescr; + + // bInitState = 3; + + // case 3: + // Power on all ports + for(uint8_t j = 1; j <= bNbrPorts; j++) + SetPortFeature(HUB_FEATURE_PORT_POWER, j, 0); //HubPortPowerOn(j); + + pUsb->SetHubPreMask(); + bPollEnable = true; + // bInitState = 0; + //} + //bInitState = 0; + //USBTRACE("...OK\r\n"); + return 0; + + // Oleg, No debugging?? -- xxxajk +FailGetDevDescr: + goto Fail; + +FailSetDevTblEntry: + goto Fail; + +FailGetHubDescr: + goto Fail; + +FailGetConfDescr: + goto Fail; + +FailSetConfDescr: + goto Fail; + +Fail: + USBTRACE("...FAIL\r\n"); + return rcode; +} + +uint8_t USBHub::Release() { + pUsb->GetAddressPool().FreeAddress(bAddress); + + if(bAddress == 0x41) + pUsb->SetHubPreMask(); + + bAddress = 0; + bNbrPorts = 0; + qNextPollTime = 0; + bPollEnable = false; + return 0; +} + +uint8_t USBHub::Poll() { + uint8_t rcode = 0; + + if(!bPollEnable) + return 0; + + if(((long)(millis() - qNextPollTime) >= 0L)) { + rcode = CheckHubStatus(); + qNextPollTime = millis() + 100; + } + return rcode; +} + +uint8_t USBHub::CheckHubStatus() { + uint8_t rcode; + uint8_t buf[8]; + uint16_t read = 1; + + rcode = pUsb->inTransfer(bAddress, 1, &read, buf); + + if(rcode) + return rcode; + + //if (buf[0] & 0x01) // Hub Status Change + //{ + // pUsb->PrintHubStatus(addr); + // rcode = GetHubStatus(1, 0, 1, 4, buf); + // if (rcode) + // { + // USB_HOST_SERIAL.print("GetHubStatus Error"); + // USB_HOST_SERIAL.println(rcode, HEX); + // return rcode; + // } + //} + for(uint8_t port = 1, mask = 0x02; port < 8; mask <<= 1, port++) { + if(buf[0] & mask) { + HubEvent evt; + evt.bmEvent = 0; + + rcode = GetPortStatus(port, 4, evt.evtBuff); + + if(rcode) + continue; + + rcode = PortStatusChange(port, evt); + + if(rcode == HUB_ERROR_PORT_HAS_BEEN_RESET) + return 0; + + if(rcode) + return rcode; + } + } // for + + for(uint8_t port = 1; port <= bNbrPorts; port++) { + HubEvent evt; + evt.bmEvent = 0; + + rcode = GetPortStatus(port, 4, evt.evtBuff); + + if(rcode) + continue; + + if((evt.bmStatus & bmHUB_PORT_STATE_CHECK_DISABLED) != bmHUB_PORT_STATE_DISABLED) + continue; + + // Emulate connection event for the port + evt.bmChange |= bmHUB_PORT_STATUS_C_PORT_CONNECTION; + + rcode = PortStatusChange(port, evt); + + if(rcode == HUB_ERROR_PORT_HAS_BEEN_RESET) + return 0; + + if(rcode) + return rcode; + } // for + return 0; +} + +void USBHub::ResetHubPort(uint8_t port) { + HubEvent evt; + evt.bmEvent = 0; + uint8_t rcode; + + ClearPortFeature(HUB_FEATURE_C_PORT_ENABLE, port, 0); + ClearPortFeature(HUB_FEATURE_C_PORT_CONNECTION, port, 0); + SetPortFeature(HUB_FEATURE_PORT_RESET, port, 0); + + + for(int i = 0; i < 3; i++) { + rcode = GetPortStatus(port, 4, evt.evtBuff); + if(rcode) break; // Some kind of error, bail. + if(evt.bmEvent == bmHUB_PORT_EVENT_RESET_COMPLETE || evt.bmEvent == bmHUB_PORT_EVENT_LS_RESET_COMPLETE) { + break; + } + delay(100); // simulate polling. + } + ClearPortFeature(HUB_FEATURE_C_PORT_RESET, port, 0); + ClearPortFeature(HUB_FEATURE_C_PORT_CONNECTION, port, 0); + delay(20); +} + +uint8_t USBHub::PortStatusChange(uint8_t port, HubEvent &evt) { + switch(evt.bmEvent) { + // Device connected event + case bmHUB_PORT_EVENT_CONNECT: + case bmHUB_PORT_EVENT_LS_CONNECT: + if(bResetInitiated) + return 0; + + ClearPortFeature(HUB_FEATURE_C_PORT_ENABLE, port, 0); + ClearPortFeature(HUB_FEATURE_C_PORT_CONNECTION, port, 0); + SetPortFeature(HUB_FEATURE_PORT_RESET, port, 0); + bResetInitiated = true; + return HUB_ERROR_PORT_HAS_BEEN_RESET; + + // Device disconnected event + case bmHUB_PORT_EVENT_DISCONNECT: + ClearPortFeature(HUB_FEATURE_C_PORT_ENABLE, port, 0); + ClearPortFeature(HUB_FEATURE_C_PORT_CONNECTION, port, 0); + bResetInitiated = false; + + UsbDeviceAddress a; + a.devAddress = 0; + a.bmHub = 0; + a.bmParent = bAddress; + a.bmAddress = port; + pUsb->ReleaseDevice(a.devAddress); + return 0; + + // Reset complete event + case bmHUB_PORT_EVENT_RESET_COMPLETE: + case bmHUB_PORT_EVENT_LS_RESET_COMPLETE: + ClearPortFeature(HUB_FEATURE_C_PORT_RESET, port, 0); + ClearPortFeature(HUB_FEATURE_C_PORT_CONNECTION, port, 0); + + delay(20); + + a.devAddress = bAddress; + + pUsb->Configuring(a.bmAddress, port, (evt.bmStatus & bmHUB_PORT_STATUS_PORT_LOW_SPEED)); + bResetInitiated = false; + break; + + } // switch (evt.bmEvent) + return 0; +} + +void PrintHubPortStatus(USBHub *hubptr, uint8_t addr, uint8_t port, bool print_changes) { + uint8_t rcode = 0; + HubEvent evt; + + rcode = hubptr->GetPortStatus(port, 4, evt.evtBuff); + + if(rcode) { + USB_HOST_SERIAL.println("ERROR!"); + return; + } + USB_HOST_SERIAL.print("\r\nPort "); + USB_HOST_SERIAL.println(port, DEC); + + USB_HOST_SERIAL.println("Status"); + USB_HOST_SERIAL.print("CONNECTION:\t"); + USB_HOST_SERIAL.println((evt.bmStatus & bmHUB_PORT_STATUS_PORT_CONNECTION) > 0, DEC); + USB_HOST_SERIAL.print("ENABLE:\t\t"); + USB_HOST_SERIAL.println((evt.bmStatus & bmHUB_PORT_STATUS_PORT_ENABLE) > 0, DEC); + USB_HOST_SERIAL.print("SUSPEND:\t"); + USB_HOST_SERIAL.println((evt.bmStatus & bmHUB_PORT_STATUS_PORT_SUSPEND) > 0, DEC); + USB_HOST_SERIAL.print("OVER_CURRENT:\t"); + USB_HOST_SERIAL.println((evt.bmStatus & bmHUB_PORT_STATUS_PORT_OVER_CURRENT) > 0, DEC); + USB_HOST_SERIAL.print("RESET:\t\t"); + USB_HOST_SERIAL.println((evt.bmStatus & bmHUB_PORT_STATUS_PORT_RESET) > 0, DEC); + USB_HOST_SERIAL.print("POWER:\t\t"); + USB_HOST_SERIAL.println((evt.bmStatus & bmHUB_PORT_STATUS_PORT_POWER) > 0, DEC); + USB_HOST_SERIAL.print("LOW_SPEED:\t"); + USB_HOST_SERIAL.println((evt.bmStatus & bmHUB_PORT_STATUS_PORT_LOW_SPEED) > 0, DEC); + USB_HOST_SERIAL.print("HIGH_SPEED:\t"); + USB_HOST_SERIAL.println((evt.bmStatus & bmHUB_PORT_STATUS_PORT_HIGH_SPEED) > 0, DEC); + USB_HOST_SERIAL.print("TEST:\t\t"); + USB_HOST_SERIAL.println((evt.bmStatus & bmHUB_PORT_STATUS_PORT_TEST) > 0, DEC); + USB_HOST_SERIAL.print("INDICATOR:\t"); + USB_HOST_SERIAL.println((evt.bmStatus & bmHUB_PORT_STATUS_PORT_INDICATOR) > 0, DEC); + + if(!print_changes) + return; + + USB_HOST_SERIAL.println("\r\nChange"); + USB_HOST_SERIAL.print("CONNECTION:\t"); + USB_HOST_SERIAL.println((evt.bmChange & bmHUB_PORT_STATUS_C_PORT_CONNECTION) > 0, DEC); + USB_HOST_SERIAL.print("ENABLE:\t\t"); + USB_HOST_SERIAL.println((evt.bmChange & bmHUB_PORT_STATUS_C_PORT_ENABLE) > 0, DEC); + USB_HOST_SERIAL.print("SUSPEND:\t"); + USB_HOST_SERIAL.println((evt.bmChange & bmHUB_PORT_STATUS_C_PORT_SUSPEND) > 0, DEC); + USB_HOST_SERIAL.print("OVER_CURRENT:\t"); + USB_HOST_SERIAL.println((evt.bmChange & bmHUB_PORT_STATUS_C_PORT_OVER_CURRENT) > 0, DEC); + USB_HOST_SERIAL.print("RESET:\t\t"); + USB_HOST_SERIAL.println((evt.bmChange & bmHUB_PORT_STATUS_C_PORT_RESET) > 0, DEC); +} diff --git a/libraries/USB_Host_Shield/usbhub.h b/libraries/USB_Host_Shield/usbhub.h new file mode 100755 index 0000000..1ac9494 --- /dev/null +++ b/libraries/USB_Host_Shield/usbhub.h @@ -0,0 +1,252 @@ +/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved. + +This software may be distributed and modified under the terms of the GNU +General Public License version 2 (GPL2) as published by the Free Software +Foundation and appearing in the file GPL2.TXT included in the packaging of +this file. Please note that GPL2 Section 2[b] requires that all works based +on this software must also be made publicly available under the terms of +the GPL2 ("Copyleft"). + +Contact information +------------------- + +Circuits At Home, LTD +Web : http://www.circuitsathome.com +e-mail : support@circuitsathome.com + */ +#if !defined(__USBHUB_H__) +#define __USBHUB_H__ + +#include "Usb.h" + +#define USB_DESCRIPTOR_HUB 0x09 // Hub descriptor type + +// Hub Requests +#define bmREQ_CLEAR_HUB_FEATURE USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_DEVICE +#define bmREQ_CLEAR_PORT_FEATURE USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_OTHER +#define bmREQ_CLEAR_TT_BUFFER USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_OTHER +#define bmREQ_GET_HUB_DESCRIPTOR USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_DEVICE +#define bmREQ_GET_HUB_STATUS USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_DEVICE +#define bmREQ_GET_PORT_STATUS USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_OTHER +#define bmREQ_RESET_TT USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_OTHER +#define bmREQ_SET_HUB_DESCRIPTOR USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_DEVICE +#define bmREQ_SET_HUB_FEATURE USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_DEVICE +#define bmREQ_SET_PORT_FEATURE USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_OTHER +#define bmREQ_GET_TT_STATE USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_OTHER +#define bmREQ_STOP_TT USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_OTHER + +// Hub Class Requests +#define HUB_REQUEST_CLEAR_TT_BUFFER 8 +#define HUB_REQUEST_RESET_TT 9 +#define HUB_REQUEST_GET_TT_STATE 10 +#define HUB_REQUEST_STOP_TT 11 + +// Hub Features +#define HUB_FEATURE_C_HUB_LOCAL_POWER 0 +#define HUB_FEATURE_C_HUB_OVER_CURRENT 1 +#define HUB_FEATURE_PORT_CONNECTION 0 +#define HUB_FEATURE_PORT_ENABLE 1 +#define HUB_FEATURE_PORT_SUSPEND 2 +#define HUB_FEATURE_PORT_OVER_CURRENT 3 +#define HUB_FEATURE_PORT_RESET 4 +#define HUB_FEATURE_PORT_POWER 8 +#define HUB_FEATURE_PORT_LOW_SPEED 9 +#define HUB_FEATURE_C_PORT_CONNECTION 16 +#define HUB_FEATURE_C_PORT_ENABLE 17 +#define HUB_FEATURE_C_PORT_SUSPEND 18 +#define HUB_FEATURE_C_PORT_OVER_CURRENT 19 +#define HUB_FEATURE_C_PORT_RESET 20 +#define HUB_FEATURE_PORT_TEST 21 +#define HUB_FEATURE_PORT_INDICATOR 22 + +// Hub Port Test Modes +#define HUB_PORT_TEST_MODE_J 1 +#define HUB_PORT_TEST_MODE_K 2 +#define HUB_PORT_TEST_MODE_SE0_NAK 3 +#define HUB_PORT_TEST_MODE_PACKET 4 +#define HUB_PORT_TEST_MODE_FORCE_ENABLE 5 + +// Hub Port Indicator Color +#define HUB_PORT_INDICATOR_AUTO 0 +#define HUB_PORT_INDICATOR_AMBER 1 +#define HUB_PORT_INDICATOR_GREEN 2 +#define HUB_PORT_INDICATOR_OFF 3 + +// Hub Port Status Bitmasks +#define bmHUB_PORT_STATUS_PORT_CONNECTION 0x0001 +#define bmHUB_PORT_STATUS_PORT_ENABLE 0x0002 +#define bmHUB_PORT_STATUS_PORT_SUSPEND 0x0004 +#define bmHUB_PORT_STATUS_PORT_OVER_CURRENT 0x0008 +#define bmHUB_PORT_STATUS_PORT_RESET 0x0010 +#define bmHUB_PORT_STATUS_PORT_POWER 0x0100 +#define bmHUB_PORT_STATUS_PORT_LOW_SPEED 0x0200 +#define bmHUB_PORT_STATUS_PORT_HIGH_SPEED 0x0400 +#define bmHUB_PORT_STATUS_PORT_TEST 0x0800 +#define bmHUB_PORT_STATUS_PORT_INDICATOR 0x1000 + +// Hub Port Status Change Bitmasks (used one byte instead of two) +#define bmHUB_PORT_STATUS_C_PORT_CONNECTION 0x0001 +#define bmHUB_PORT_STATUS_C_PORT_ENABLE 0x0002 +#define bmHUB_PORT_STATUS_C_PORT_SUSPEND 0x0004 +#define bmHUB_PORT_STATUS_C_PORT_OVER_CURRENT 0x0008 +#define bmHUB_PORT_STATUS_C_PORT_RESET 0x0010 + +// Hub Status Bitmasks (used one byte instead of two) +#define bmHUB_STATUS_LOCAL_POWER_SOURCE 0x01 +#define bmHUB_STATUS_OVER_CURRENT 0x12 + +// Hub Status Change Bitmasks (used one byte instead of two) +#define bmHUB_STATUS_C_LOCAL_POWER_SOURCE 0x01 +#define bmHUB_STATUS_C_OVER_CURRENT 0x12 + + +// Hub Port Configuring Substates +#define USB_STATE_HUB_PORT_CONFIGURING 0xb0 +#define USB_STATE_HUB_PORT_POWERED_OFF 0xb1 +#define USB_STATE_HUB_PORT_WAIT_FOR_POWER_GOOD 0xb2 +#define USB_STATE_HUB_PORT_DISCONNECTED 0xb3 +#define USB_STATE_HUB_PORT_DISABLED 0xb4 +#define USB_STATE_HUB_PORT_RESETTING 0xb5 +#define USB_STATE_HUB_PORT_ENABLED 0xb6 + +// Additional Error Codes +#define HUB_ERROR_PORT_HAS_BEEN_RESET 0xb1 + +// The bit mask to check for all necessary state bits +#define bmHUB_PORT_STATUS_ALL_MAIN ((0UL | bmHUB_PORT_STATUS_C_PORT_CONNECTION | bmHUB_PORT_STATUS_C_PORT_ENABLE | bmHUB_PORT_STATUS_C_PORT_SUSPEND | bmHUB_PORT_STATUS_C_PORT_RESET) << 16) | bmHUB_PORT_STATUS_PORT_POWER | bmHUB_PORT_STATUS_PORT_ENABLE | bmHUB_PORT_STATUS_PORT_CONNECTION | bmHUB_PORT_STATUS_PORT_SUSPEND) + +// Bit mask to check for DISABLED state in HubEvent::bmStatus field +#define bmHUB_PORT_STATE_CHECK_DISABLED (0x0000 | bmHUB_PORT_STATUS_PORT_POWER | bmHUB_PORT_STATUS_PORT_ENABLE | bmHUB_PORT_STATUS_PORT_CONNECTION | bmHUB_PORT_STATUS_PORT_SUSPEND) + +// Hub Port States +#define bmHUB_PORT_STATE_DISABLED (0x0000 | bmHUB_PORT_STATUS_PORT_POWER | bmHUB_PORT_STATUS_PORT_CONNECTION) + +// Hub Port Events +#define bmHUB_PORT_EVENT_CONNECT (((0UL | bmHUB_PORT_STATUS_C_PORT_CONNECTION) << 16) | bmHUB_PORT_STATUS_PORT_POWER | bmHUB_PORT_STATUS_PORT_CONNECTION) +#define bmHUB_PORT_EVENT_DISCONNECT (((0UL | bmHUB_PORT_STATUS_C_PORT_CONNECTION) << 16) | bmHUB_PORT_STATUS_PORT_POWER) +#define bmHUB_PORT_EVENT_RESET_COMPLETE (((0UL | bmHUB_PORT_STATUS_C_PORT_RESET) << 16) | bmHUB_PORT_STATUS_PORT_POWER | bmHUB_PORT_STATUS_PORT_ENABLE | bmHUB_PORT_STATUS_PORT_CONNECTION) + +#define bmHUB_PORT_EVENT_LS_CONNECT (((0UL | bmHUB_PORT_STATUS_C_PORT_CONNECTION) << 16) | bmHUB_PORT_STATUS_PORT_POWER | bmHUB_PORT_STATUS_PORT_CONNECTION | bmHUB_PORT_STATUS_PORT_LOW_SPEED) +#define bmHUB_PORT_EVENT_LS_RESET_COMPLETE (((0UL | bmHUB_PORT_STATUS_C_PORT_RESET) << 16) | bmHUB_PORT_STATUS_PORT_POWER | bmHUB_PORT_STATUS_PORT_ENABLE | bmHUB_PORT_STATUS_PORT_CONNECTION | bmHUB_PORT_STATUS_PORT_LOW_SPEED) +#define bmHUB_PORT_EVENT_LS_PORT_ENABLED (((0UL | bmHUB_PORT_STATUS_C_PORT_CONNECTION | bmHUB_PORT_STATUS_C_PORT_ENABLE) << 16) | bmHUB_PORT_STATUS_PORT_POWER | bmHUB_PORT_STATUS_PORT_ENABLE | bmHUB_PORT_STATUS_PORT_CONNECTION | bmHUB_PORT_STATUS_PORT_LOW_SPEED) + +struct HubDescriptor { + uint8_t bDescLength; // descriptor length + uint8_t bDescriptorType; // descriptor type + uint8_t bNbrPorts; // number of ports a hub equiped with + + struct { + uint16_t LogPwrSwitchMode : 2; + uint16_t CompoundDevice : 1; + uint16_t OverCurrentProtectMode : 2; + uint16_t TTThinkTime : 2; + uint16_t PortIndicatorsSupported : 1; + uint16_t Reserved : 8; + } __attribute__((packed)); + + uint8_t bPwrOn2PwrGood; + uint8_t bHubContrCurrent; +} __attribute__((packed)); + +struct HubEvent { + + union { + + struct { + uint16_t bmStatus; // port status bits + uint16_t bmChange; // port status change bits + } __attribute__((packed)); + uint32_t bmEvent; + uint8_t evtBuff[4]; + }; +} __attribute__((packed)); + +class USBHub : USBDeviceConfig { + static bool bResetInitiated; // True when reset is triggered + + USB *pUsb; // USB class instance pointer + + EpInfo epInfo[2]; // interrupt endpoint info structure + + uint8_t bAddress; // address + uint8_t bNbrPorts; // number of ports + // uint8_t bInitState; // initialization state variable + uint32_t qNextPollTime; // next poll time + bool bPollEnable; // poll enable flag + + uint8_t CheckHubStatus(); + uint8_t PortStatusChange(uint8_t port, HubEvent &evt); + +public: + USBHub(USB *p); + + uint8_t ClearHubFeature(uint8_t fid); + uint8_t ClearPortFeature(uint8_t fid, uint8_t port, uint8_t sel = 0); + uint8_t GetHubDescriptor(uint8_t index, uint16_t nbytes, uint8_t *dataptr); + uint8_t GetHubStatus(uint16_t nbytes, uint8_t* dataptr); + uint8_t GetPortStatus(uint8_t port, uint16_t nbytes, uint8_t* dataptr); + uint8_t SetHubDescriptor(uint8_t port, uint16_t nbytes, uint8_t* dataptr); + uint8_t SetHubFeature(uint8_t fid); + uint8_t SetPortFeature(uint8_t fid, uint8_t port, uint8_t sel = 0); + + void PrintHubStatus(); + + uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed); + uint8_t Release(); + uint8_t Poll(); + void ResetHubPort(uint8_t port); + + virtual uint8_t GetAddress() { + return bAddress; + }; + + virtual bool DEVCLASSOK(uint8_t klass) { + return (klass == 0x09); + } + +}; + +// Clear Hub Feature + +inline uint8_t USBHub::ClearHubFeature(uint8_t fid) { + return ( pUsb->ctrlReq(bAddress, 0, bmREQ_CLEAR_HUB_FEATURE, USB_REQUEST_CLEAR_FEATURE, fid, 0, 0, 0, 0, NULL, NULL)); +} +// Clear Port Feature + +inline uint8_t USBHub::ClearPortFeature(uint8_t fid, uint8_t port, uint8_t sel) { + return ( pUsb->ctrlReq(bAddress, 0, bmREQ_CLEAR_PORT_FEATURE, USB_REQUEST_CLEAR_FEATURE, fid, 0, ((0x0000 | port) | (sel << 8)), 0, 0, NULL, NULL)); +} +// Get Hub Descriptor + +inline uint8_t USBHub::GetHubDescriptor(uint8_t index, uint16_t nbytes, uint8_t *dataptr) { + return ( pUsb->ctrlReq(bAddress, 0, bmREQ_GET_HUB_DESCRIPTOR, USB_REQUEST_GET_DESCRIPTOR, index, 0x29, 0, nbytes, nbytes, dataptr, NULL)); +} +// Get Hub Status + +inline uint8_t USBHub::GetHubStatus(uint16_t nbytes, uint8_t* dataptr) { + return ( pUsb->ctrlReq(bAddress, 0, bmREQ_GET_HUB_STATUS, USB_REQUEST_GET_STATUS, 0, 0, 0x0000, nbytes, nbytes, dataptr, NULL)); +} +// Get Port Status + +inline uint8_t USBHub::GetPortStatus(uint8_t port, uint16_t nbytes, uint8_t* dataptr) { + return ( pUsb->ctrlReq(bAddress, 0, bmREQ_GET_PORT_STATUS, USB_REQUEST_GET_STATUS, 0, 0, port, nbytes, nbytes, dataptr, NULL)); +} +// Set Hub Descriptor + +inline uint8_t USBHub::SetHubDescriptor(uint8_t port, uint16_t nbytes, uint8_t* dataptr) { + return ( pUsb->ctrlReq(bAddress, 0, bmREQ_SET_HUB_DESCRIPTOR, USB_REQUEST_SET_DESCRIPTOR, 0, 0, port, nbytes, nbytes, dataptr, NULL)); +} +// Set Hub Feature + +inline uint8_t USBHub::SetHubFeature(uint8_t fid) { + return ( pUsb->ctrlReq(bAddress, 0, bmREQ_SET_HUB_FEATURE, USB_REQUEST_SET_FEATURE, fid, 0, 0, 0, 0, NULL, NULL)); +} +// Set Port Feature + +inline uint8_t USBHub::SetPortFeature(uint8_t fid, uint8_t port, uint8_t sel) { + return ( pUsb->ctrlReq(bAddress, 0, bmREQ_SET_PORT_FEATURE, USB_REQUEST_SET_FEATURE, fid, 0, (((0x0000 | sel) << 8) | port), 0, 0, NULL, NULL)); +} + +void PrintHubPortStatus(USB *usbptr, uint8_t addr, uint8_t port, bool print_changes = false); + +#endif // __USBHUB_H__ diff --git a/libraries/USB_Host_Shield/version_helper.h b/libraries/USB_Host_Shield/version_helper.h new file mode 100755 index 0000000..b018000 --- /dev/null +++ b/libraries/USB_Host_Shield/version_helper.h @@ -0,0 +1,222 @@ +/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved. + +This software may be distributed and modified under the terms of the GNU +General Public License version 2 (GPL2) as published by the Free Software +Foundation and appearing in the file GPL2.TXT included in the packaging of +this file. Please note that GPL2 Section 2[b] requires that all works based +on this software must also be made publicly available under the terms of +the GPL2 ("Copyleft"). + +Contact information +------------------- + +Circuits At Home, LTD +Web : http://www.circuitsathome.com +e-mail : support@circuitsathome.com + */ + +/* + * Universal Arduino(tm) "IDE" fixups. + * Includes fixes for versions as low as 0023, used by Digilent. + */ + +#if defined(ARDUINO) && ARDUINO >=100 +#include +#else +#include +#include +#ifdef __AVR__ +#include +#include +#else +#endif +#endif + +#ifndef __PGMSPACE_H_ +#define __PGMSPACE_H_ 1 + +#include + +#ifndef PROGMEM +#define PROGMEM +#endif +#ifndef PGM_P +#define PGM_P const char * +#endif +#ifndef PSTR +#define PSTR(str) (str) +#endif +#ifndef F +#define F(str) (str) +#endif +#ifndef _SFR_BYTE +#define _SFR_BYTE(n) (n) +#endif + +#ifndef prog_void +typedef void prog_void; +#endif +#ifndef prog_char +typedef char prog_char; +#endif +#ifndef prog_uchar +typedef unsigned char prog_uchar; +#endif +#ifndef prog_int8_t +typedef int8_t prog_int8_t; +#endif +#ifndef prog_uint8_t +typedef uint8_t prog_uint8_t; +#endif +#ifndef prog_int16_t +typedef int16_t prog_int16_t; +#endif +#ifndef prog_uint16_t +typedef uint16_t prog_uint16_t; +#endif +#ifndef prog_int32_t +typedef int32_t prog_int32_t; +#endif +#ifndef prog_uint32_t +typedef uint32_t prog_uint32_t; +#endif + +#ifndef memchr_P +#define memchr_P(str, c, len) memchr((str), (c), (len)) +#endif +#ifndef memcmp_P +#define memcmp_P(a, b, n) memcmp((a), (b), (n)) +#endif +#ifndef memcpy_P +#define memcpy_P(dest, src, num) memcpy((dest), (src), (num)) +#endif +#ifndef memmem_P +#define memmem_P(a, alen, b, blen) memmem((a), (alen), (b), (blen)) +#endif +#ifndef memrchr_P +#define memrchr_P(str, val, len) memrchr((str), (val), (len)) +#endif +#ifndef strcat_P +#define strcat_P(dest, src) strcat((dest), (src)) +#endif +#ifndef strchr_P +#define strchr_P(str, c) strchr((str), (c)) +#endif +#ifndef strchrnul_P +#define strchrnul_P(str, c) strchrnul((str), (c)) +#endif +#ifndef strcmp_P +#define strcmp_P(a, b) strcmp((a), (b)) +#endif +#ifndef strcpy_P +#define strcpy_P(dest, src) strcpy((dest), (src)) +#endif +#ifndef strcasecmp_P +#define strcasecmp_P(a, b) strcasecmp((a), (b)) +#endif +#ifndef strcasestr_P +#define strcasestr_P(a, b) strcasestr((a), (b)) +#endif +#ifndef strlcat_P +#define strlcat_P(dest, src, len) strlcat((dest), (src), (len)) +#endif +#ifndef strlcpy_P +#define strlcpy_P(dest, src, len) strlcpy((dest), (src), (len)) +#endif +#ifndef strlen_P +#define strlen_P(s) strlen((const char *)(s)) +#endif +#ifndef strnlen_P +#define strnlen_P(str, len) strnlen((str), (len)) +#endif +#ifndef strncmp_P +#define strncmp_P(a, b, n) strncmp((a), (b), (n)) +#endif +#ifndef strncasecmp_P +#define strncasecmp_P(a, b, n) strncasecmp((a), (b), (n)) +#endif +#ifndef strncat_P +#define strncat_P(a, b, n) strncat((a), (b), (n)) +#endif +#ifndef strncpy_P +#define strncpy_P(a, b, n) strncmp((a), (b), (n)) +#endif +#ifndef strpbrk_P +#define strpbrk_P(str, chrs) strpbrk((str), (chrs)) +#endif +#ifndef strrchr_P +#define strrchr_P(str, c) strrchr((str), (c)) +#endif +#ifndef strsep_P +#define strsep_P(strp, delim) strsep((strp), (delim)) +#endif +#ifndef strspn_P +#define strspn_P(str, chrs) strspn((str), (chrs)) +#endif +#ifndef strstr_P +#define strstr_P(a, b) strstr((a), (b)) +#endif +#ifndef sprintf_P +#define sprintf_P(s, ...) sprintf((s), __VA_ARGS__) +#endif +#ifndef vfprintf_P +#define vfprintf_P(s, ...) vfprintf((s), __VA_ARGS__) +#endif +#ifndef printf_P +#define printf_P(...) printf(__VA_ARGS__) +#endif +#ifndef snprintf_P +#define snprintf_P(s, n, ...) ((s), (n), __VA_ARGS__) +#endif +#ifndef vsprintf_P +#define vsprintf_P(s, ...) ((s),__VA_ARGS__) +#endif +#ifndef vsnprintf_P +#define vsnprintf_P(s, n, ...) ((s), (n),__VA_ARGS__) +#endif +#ifndef fprintf_P +#define fprintf_P(s, ...) ((s), __VA_ARGS__) +#endif + +#ifndef pgm_read_byte +#define pgm_read_byte(addr) (*(const unsigned char *)(addr)) +#endif +#ifndef pgm_read_word +#define pgm_read_word(addr) (*(const unsigned short *)(addr)) +#endif +#ifndef pgm_read_dword +#define pgm_read_dword(addr) (*(const unsigned long *)(addr)) +#endif +#ifndef pgm_read_float +#define pgm_read_float(addr) (*(const float *)(addr)) +#endif + +#ifndef pgm_read_byte_near +#define pgm_read_byte_near(addr) pgm_read_byte(addr) +#endif +#ifndef pgm_read_word_near +#define pgm_read_word_near(addr) pgm_read_word(addr) +#endif +#ifndef pgm_read_dword_near +#define pgm_read_dword_near(addr) pgm_read_dword(addr) +#endif +#ifndef pgm_read_float_near +#define pgm_read_float_near(addr) pgm_read_float(addr) +#endif +#ifndef pgm_read_byte_far +#define pgm_read_byte_far(addr) pgm_read_byte(addr) +#endif +#ifndef pgm_read_word_far +#define pgm_read_word_far(addr) pgm_read_word(addr) +#endif +#ifndef pgm_read_dword_far +#define pgm_read_dword_far(addr) pgm_read_dword(addr) +#endif +#ifndef pgm_read_float_far +#define pgm_read_float_far(addr) pgm_read_float(addr) +#endif + +#ifndef pgm_read_pointer +#define pgm_read_pointer +#endif +#endif diff --git a/libraries/USB_Host_Shield/xboxEnums.h b/libraries/USB_Host_Shield/xboxEnums.h new file mode 100755 index 0000000..84b137b --- /dev/null +++ b/libraries/USB_Host_Shield/xboxEnums.h @@ -0,0 +1,65 @@ +/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved. + + This software may be distributed and modified under the terms of the GNU + General Public License version 2 (GPL2) as published by the Free Software + Foundation and appearing in the file GPL2.TXT included in the packaging of + this file. Please note that GPL2 Section 2[b] requires that all works based + on this software must also be made publicly available under the terms of + the GPL2 ("Copyleft"). + + Contact information + ------------------- + + Kristian Lauszus, TKJ Electronics + Web : http://www.tkjelectronics.com + e-mail : kristianl@tkjelectronics.com + */ + +#ifndef _xboxenums_h +#define _xboxenums_h + +#include "controllerEnums.h" + +/** Enum used to set special LED modes supported by the Xbox controller. */ +enum LEDModeEnum { + ROTATING = 0x0A, + FASTBLINK = 0x0B, + SLOWBLINK = 0x0C, + ALTERNATING = 0x0D, +}; + +/** Used to set the LEDs on the controllers */ +const uint8_t XBOX_LEDS[] PROGMEM = { + 0x00, // OFF + 0x02, // LED1 + 0x03, // LED2 + 0x04, // LED3 + 0x05, // LED4 + 0x01, // ALL - Used to blink all LEDs +}; +/** Buttons on the controllers */ +const uint16_t XBOX_BUTTONS[] PROGMEM = { + 0x0100, // UP + 0x0800, // RIGHT + 0x0200, // DOWN + 0x0400, // LEFT + + 0x2000, // BACK + 0x1000, // START + 0x4000, // L3 + 0x8000, // R3 + + 0, 0, // Skip L2 and R2 as these are analog buttons + 0x0001, // L1 + 0x0002, // R1 + + 0x0020, // B + 0x0010, // A + 0x0040, // X + 0x0080, // Y + + 0x0004, // XBOX + 0x0008, // SYNC +}; + +#endif diff --git a/pcb.png b/pcb.png new file mode 100755 index 0000000000000000000000000000000000000000..35609e24a8aeb7cbca4c5d3b013df2a6d64a8ac9 GIT binary patch literal 124206 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

l0B`+T@5L&Wx+v;qj$%#XA8FKc}Z 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

    e5X`US?$ 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 Ai2wiq literal 0 HcmV?d00001 diff --git a/preconfigurations/YaaCWk/so1r/PS2_WINKEY_Atmega644P/keyer_features_and_options_yaacwk.h b/preconfigurations/YaaCWk/so1r/PS2_WINKEY_Atmega644P/keyer_features_and_options_yaacwk.h new file mode 100755 index 0000000..3adb33f --- /dev/null +++ b/preconfigurations/YaaCWk/so1r/PS2_WINKEY_Atmega644P/keyer_features_and_options_yaacwk.h @@ -0,0 +1,102 @@ +// This file is for the Yaacwk interface http://i1cra.briata.org/yaacwk/ +// YAACWK stands for Yet Another Arduino CW Keyer, it's based on AtMega 644p +// see http://i1cra.briata.org/yaacwk/ for more info + +// compile time features and options - comment or uncomment to add or delete features +// FEATURES add more bytes to the compiled binary, OPTIONS change code behavior + +#define FEATURE_COMMAND_BUTTONS +#define FEATURE_COMMAND_LINE_INTERFACE // (this no longer requires FEATURE_SERIAL) +#define FEATURE_MEMORIES +//#define FEATURE_MEMORY_MACROS +#define FEATURE_WINKEY_EMULATION // disabling Automatic Software Reset is highly recommended (see documentation) (this no longer requires FEATURE_SERIAL) +//#define FEATURE_BEACON +//#define FEATURE_TRAINING_COMMAND_LINE_INTERFACE +#define FEATURE_POTENTIOMETER // do not enable unless you have a potentiometer connected, otherwise noise will falsely trigger wpm changes +//#define FEATURE_SIDETONE_SWITCH // adds switch control for the sidetone output. requires an external toggle switch (assigned to an arduino pin - see keyer_pin_settings.h). +//#define FEATURE_SERIAL_HELP +//#define FEATURE_HELL +#define FEATURE_PS2_KEYBOARD // Use a PS2 keyboard to send code - Change keyboard layout (non-US) in K3NG_PS2Keyboard.h. Additional options below. +//#define FEATURE_USB_KEYBOARD // Use a USB keyboard to send code - Uncomment three lines in k3ng_keyer.ino (search for note_usb_uncomment_lines) +//#define FEATURE_DEAD_OP_WATCHDOG +//#define FEATURE_AUTOSPACE +//#define FEATURE_FARNSWORTH +//#define FEATURE_DL2SBA_BANKSWITCH // Switch memory banks feature as described here: http://dl2sba.com/index.php?option=com_content&view=article&id=131:nanokeyer&catid=15:shack&Itemid=27#english +#define FEATURE_LCD_4BIT // classic LCD disidefplay using 4 I/O lines +//#define FEATURE_LCD_ADAFRUIT_I2C // Adafruit I2C LCD display using MCP23017 at addr 0x20 +//#define FEATURE_LCD_YDv1 // YourDuino I2C LCD display with old LCM 1602 V1 ic +//#define FEATURE_LCD_FABO_PCF8574 // https://github.com/FaBoPlatform/FaBoLCD-PCF8574-Library +#define FEATURE_CW_DECODER +//#define FEATURE_SLEEP // go to sleep after x minutes to conserve battery power +//#define FEATURE_ROTARY_ENCODER // rotary encoder speed control +//#define FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING +//#define FEATURE_USB_MOUSE // Uncomment three lines in k3ng_keyer.ino (search for note_usb_uncomment_lines) +//#define FEATURE_CAPACITIVE_PADDLE_PINS // remove the bypass capacitors on the paddle_left and paddle_right lines when using capactive paddles +//#define FEATURE_LED_RING // Mayhew Labs Led Ring support +#define FEATURE_ALPHABET_SEND_PRACTICE // enables command mode S command - created by Ryan, KC2ZWM +//#define FEATURE_COMMAND_MODE_PROGRESSIVE_5_CHAR_ECHO_PRACTICE // enables command mode U +//#define FEATURE_PTT_INTERLOCK +//#define FEATURE_QLF +//#define FEATURE_EEPROM_E24C1024 +//#define FEATURE_STRAIGHT_KEY +//#define FEATURE_DYNAMIC_DAH_TO_DIT_RATIO +//#define FEATURE_PADDLE_ECHO +//#define FEATURE_STRAIGHT_KEY_ECHO +//#define FEATURE_AMERICAN_MORSE +//#define FEATURE_4x4_KEYPAD // code contributed by Jack, W0XR - documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/380-Feature:-Keypad +//#define FEATURE_3x4_KEYPAD // code contributed by Jack, W0XR - documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/380-Feature:-Keypad +//#define FEATURE_SEQUENCER + +//#define FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT // Activate the Command Line interface on the secondary serial port +#define OPTION_PRIMARY_SERIAL_PORT_DEFAULT_WINKEY_EMULATION // Use when activating both FEATURE_WINKEY_EMULATION and FEATURE_COMMAND_LINE_INTERFACE + // simultaneously. This will make Winkey emulation be the default at boot up; + // hold command button down at boot up to activate CLI mode + +//#define OPTION_SUPPRESS_SERIAL_BOOT_MSG +#define OPTION_INCLUDE_PTT_TAIL_FOR_MANUAL_SENDING +#define OPTION_EXCLUDE_PTT_HANG_TIME_FOR_MANUAL_SENDING +#define OPTION_SERIAL_PORT_DEFAULT_WINKEY_EMULATION // Use when activating both FEATURE_WINKEY_EMULATION and FEATURE_COMMAND_LINE_INTERFACE simultaneously. This will make Winkey emulation be the default at boot up; hold command button down at boot up to activate CLI mode +//#define OPTION_WINKEY_DISCARD_BYTES_AT_STARTUP // if ASR is not disabled, you may need this to discard errant serial port bytes at startup +//#define OPTION_WINKEY_STRICT_EEPROM_WRITES_MAY_WEAR_OUT_EEPROM // with this activated the unit will write non-volatile settings to EEPROM when set by Winkey commands +//#define OPTION_WINKEY_SEND_WORDSPACE_AT_END_OF_BUFFER +#define OPTION_WINKEY_STRICT_HOST_OPEN // require an admin host open Winkey command before doing any other commands +#define OPTION_WINKEY_2_SUPPORT // comment out to revert to Winkey version 1 emulation +#define OPTION_WINKEY_INTERRUPTS_MEMORY_REPEAT +//#define OPTION_WINKEY_UCXLOG_9600_BAUD // use this only with UCXLog configured for Winkey 9600 baud mode +#define OPTION_WINKEY_2_HOST_CLOSE_NO_SERIAL_PORT_RESET // activate this when using Winkey 2 emulation and Win-Test +#define OPTION_WINKEY_FREQUENT_STATUS_REPORT // activate this to make Winkey emulation play better with RUMlog and RUMped +#define OPTION_WINKEY_IGNORE_LOWERCASE // Enable for typical K1EL Winkeyer behavior (use for SkookumLogger version 1.10.14 and prior to workaround bug) +//#define OPTION_REVERSE_BUTTON_ORDER // This is mainly for the DJ0MY NanoKeyer http://nanokeyer.wordpress.com/ +#define OPTION_PROG_MEM_TRIM_TRAILING_SPACES // trim trailing spaces from memory when programming in command mode +#define OPTION_DIT_PADDLE_NO_SEND_ON_MEM_RPT // this makes dit paddle memory interruption a little smoother +//#define OPTION_MORE_DISPLAY_MSGS // additional optional display messages - comment out to save memory +//#define OPTION_WATCHDOG_TIMER // this enables a four second ATmega48/88/168/328 watchdog timer; use for unattended/remote operation only +//#define OPTION_MOUSE_MOVEMENT_PADDLE // experimental (just fooling around) - mouse movement will act like a paddle +//#define OPTION_NON_ENGLISH_EXTENSIONS // add support for additional CW characters (i.e. À, Å, Þ, etc.) +//#define OPTION_KEEP_PTT_KEYED_WHEN_CHARS_BUFFERED // this option keeps PTT high if there are characters buffered from the keyboard, the serial interface, or Winkey +//#define OPTION_DISPLAY_NON_ENGLISH_EXTENSIONS // LCD display suport for non-English (NO/DK/DE) characters - Courtesy of OZ1JHM +//#define OPTION_UNKNOWN_CHARACTER_ERROR_TONE +//#define OPTION_DO_NOT_SAY_HI +//#define OPTION_PS2_NON_ENGLISH_CHAR_LCD_DISPLAY_SUPPORT // makes some non-English characters from the PS2 keyboard display correctly in the LCD display (donated by Marcin sp5iou) +//#define OPTION_PS2_KEYBOARD_RESET // reset the PS2 keyboard upon startup with 0xFF (contributed by Bill, W9BEL) +//#define OPTION_SAVE_MEMORY_NANOKEYER +//#define OPTION_CW_KEYBOARD_CAPSLOCK_BEEP +#define OPTION_CW_KEYBOARD_ITALIAN +//#define OPTION_INVERT_PADDLE_PIN_LOGIC +#define OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR +//#define OPTION_ADVANCED_SPEED_DISPLAY //enables "nerd" speed visualization on display: wpm, cpm (char per min), duration of dit and dah in milliseconds and ratio (contributed by Giorgio, IZ2XBZ) +//#define OPTION_RUSSIAN_LANGUAGE_SEND_CLI // Russian language CLI sending support (contributed by Павел Бирюков, UA1AQC) +//#define OPTION_DO_NOT_SEND_UNKNOWN_CHAR_QUESTION +//#define OPTION_CMOS_SUPER_KEYER_IAMBIC_B_TIMING_ON_BY_DEFAULT +//#define OPTION_SIDETONE_DIGITAL_OUTPUT_NO_SQUARE_WAVE + +// #define OPTION_WORDSWORTH_CZECH +// #define OPTION_WORDSWORTH_DEUTSCH +// #define OPTION_WORDSWORTH_NORSK + +#define OPTION_EXCLUDE_EXTENDED_CLI_COMMANDS + +// #define OPTION_DFROBOT_LCD_COMMAND_BUTTONS + +#define OPTION_EXCLUDE_MILL_MODE + diff --git a/preconfigurations/YaaCWk/so1r/PS2_WINKEY_Atmega644P/keyer_pin_settings_yaacwk.h b/preconfigurations/YaaCWk/so1r/PS2_WINKEY_Atmega644P/keyer_pin_settings_yaacwk.h new file mode 100755 index 0000000..f8fa53d --- /dev/null +++ b/preconfigurations/YaaCWk/so1r/PS2_WINKEY_Atmega644P/keyer_pin_settings_yaacwk.h @@ -0,0 +1,143 @@ +#ifndef keyer_pin_settings_h +#define keyer_pin_settings_h + +#define paddle_left 21 +#define paddle_right 22 +#define tx_key_line_1 28 // (high = key down/tx on) +#define tx_key_line_2 0 +#define tx_key_line_3 0 +#define tx_key_line_4 0 +#define tx_key_line_5 0 +#define tx_key_line_6 0 +#define sidetone_line 23 // connect a speaker for sidetone +#define potentiometer A0 // Speed potentiometer (0 to 5 V) Use pot from 1k to 10k +#define ptt_tx_1 31 // PTT ("push to talk") lines +#define ptt_tx_2 0 // Can be used for keying fox transmitter, T/R switch, or keying slow boatanchors +#define ptt_tx_3 0 // These are optional - set to 0 if unused +#define ptt_tx_4 0 +#define ptt_tx_5 0 +#define ptt_tx_6 0 +#define tx_key_dit 0 // if defined, goes active for dit (any transmitter) - customized with tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state +#define tx_key_dah 0 // if defined, goes active for dah (any transmitter) - customized with tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state + +#define potentiometer_enable_pin 0 // if defined, the potentiometer will be enabled only when this pin is held low; set to 0 to ignore this pin + +#ifdef FEATURE_COMMAND_BUTTONS + #define analog_buttons_pin A1 + #define command_mode_active_led 0 +#endif //FEATURE_COMMAND_BUTTONS + +/* +FEATURE_SIDETONE_SWITCH + Enabling this feature and an external toggle switch adds switch control for playing cw sidetone. + ST Switch status is displayed in the status command. This feature will override the software control of the sidetone (\o). + Arduino pin is assigned by SIDETONE_SWITCH +*/ + +#ifdef FEATURE_SIDETONE_SWITCH + #define SIDETONE_SWITCH 0 +#endif //FEATURE_SIDETONE_SWITCH + + +//lcd pins +#ifdef FEATURE_LCD_4BIT + #define lcd_rs 26 // A2 + #define lcd_enable 20 + #define lcd_d4 12 + #define lcd_d5 13 + #define lcd_d6 14 + #define lcd_d7 15 +#endif //FEATURE_LCD_4BIT + +//ps2 keyboard pins +#ifdef FEATURE_PS2_KEYBOARD + #define ps2_keyboard_data A3 + #define ps2_keyboard_clock 2 // this must be on an interrupt capable pin! +#endif //FEATURE_PS2_KEYBOARD + +// rotary encoder pins and options - rotary encoder code from Jim Balls M0CKE +#ifdef FEATURE_ROTARY_ENCODER + #define OPTION_ENCODER_HALF_STEP_MODE // Half-step mode? + #define rotary_pin1 0 // CW Encoder Pin + #define rotary_pin2 0 // CCW Encoder Pin + #define OPTION_ENCODER_ENABLE_PULLUPS // define to enable weak pullups. +#endif //FEATURE_ROTARY_ENCODER + +#ifdef FEATURE_LED_RING + #define led_ring_sdi A10 //2 //Data + #define led_ring_clk A9 //3 //Clock + #define led_ring_le A8 //4 //Latch +#endif //FEATURE_LED_RING + +#ifdef FEATURE_ALPHABET_SEND_PRACTICE + #define correct_answer_led 0 + #define wrong_answer_led 0 +#endif //FEATURE_ALPHABET_SEND_PRACTICE + +#ifdef FEATURE_PTT_INTERLOCK + #define ptt_interlock 0 // this pin disables PTT and TX KEY +#endif //FEATURE_PTT_INTERLOCK + +#ifdef FEATURE_STRAIGHT_KEY + #define pin_straight_key 0 +#endif //FEATURE_STRAIGHT_KEY + +#ifdef FEATURE_CW_DECODER + #define cw_decoder_pin A5 //A11 //A3 + #ifdef OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR + #define cw_decoder_audio_input_pin A5 // this must be an analog pin! + #endif //OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR + #define cw_decoder_indicator 30 //A6 +#endif //FEATURE_CW_DECODER + +#if defined(FEATURE_COMPETITION_COMPRESSION_DETECTION) + #define compression_detection_pin 0 +#endif //FEATURE_COMPETITION_COMPRESSION_DETECTION + +#if defined(FEATURE_SLEEP) + #define keyer_awake 0 // Goes active when keyer is awake, inactive when in sleep mode; change active and inactive states in keyer_settings file +#endif + +#if defined(FEATURE_CAPACITIVE_PADDLE_PINS) + #define capactive_paddle_pin_inhibit_pin 0 // if this pin is defined and is set high, the capacitive paddle pins will switch to normal (non-capacitive) sensing mode +#endif + +#ifdef FEATURE_4x4_KEYPAD + #define Row3 33 + #define Row2 32 + #define Row1 31 + #define Row0 30 + #define Col3 37 + #define Col2 36 + #define Col1 35 + #define Col0 34 +#endif + +#ifdef FEATURE_3x4_KEYPAD + #define Row3 33 + #define Row2 32 + #define Row1 31 + #define Row0 30 + #define Col2 36 + #define Col1 35 + #define Col0 34 +#endif + +#ifdef FEATURE_SEQUENCER + #define sequencer_1_pin 0 + #define sequencer_2_pin 0 + #define sequencer_3_pin 0 + #define sequencer_4_pin 0 + #define sequencer_5_pin 0 +#endif //FEATURE_SEQUENCER + +#define ptt_input_pin 0 + +#define tx_inhibit_pin 0 +#define tx_pause_pin 0 + +#else + + #error "Multiple pin_settings.h files included somehow..." + +#endif //keyer_pin_settings_h diff --git a/preconfigurations/YaaCWk/so2r/PS2_WINKEY_Atmega644P/default.txt b/preconfigurations/YaaCWk/so2r/PS2_WINKEY_Atmega644P/default.txt new file mode 100755 index 0000000..d44c766 --- /dev/null +++ b/preconfigurations/YaaCWk/so2r/PS2_WINKEY_Atmega644P/default.txt @@ -0,0 +1 @@ +this is the default profile diff --git a/preconfigurations/YaaCWk/so2r/PS2_WINKEY_Atmega644P/keyer_features_and_options_yaacwk.h b/preconfigurations/YaaCWk/so2r/PS2_WINKEY_Atmega644P/keyer_features_and_options_yaacwk.h new file mode 100755 index 0000000..3adb33f --- /dev/null +++ b/preconfigurations/YaaCWk/so2r/PS2_WINKEY_Atmega644P/keyer_features_and_options_yaacwk.h @@ -0,0 +1,102 @@ +// This file is for the Yaacwk interface http://i1cra.briata.org/yaacwk/ +// YAACWK stands for Yet Another Arduino CW Keyer, it's based on AtMega 644p +// see http://i1cra.briata.org/yaacwk/ for more info + +// compile time features and options - comment or uncomment to add or delete features +// FEATURES add more bytes to the compiled binary, OPTIONS change code behavior + +#define FEATURE_COMMAND_BUTTONS +#define FEATURE_COMMAND_LINE_INTERFACE // (this no longer requires FEATURE_SERIAL) +#define FEATURE_MEMORIES +//#define FEATURE_MEMORY_MACROS +#define FEATURE_WINKEY_EMULATION // disabling Automatic Software Reset is highly recommended (see documentation) (this no longer requires FEATURE_SERIAL) +//#define FEATURE_BEACON +//#define FEATURE_TRAINING_COMMAND_LINE_INTERFACE +#define FEATURE_POTENTIOMETER // do not enable unless you have a potentiometer connected, otherwise noise will falsely trigger wpm changes +//#define FEATURE_SIDETONE_SWITCH // adds switch control for the sidetone output. requires an external toggle switch (assigned to an arduino pin - see keyer_pin_settings.h). +//#define FEATURE_SERIAL_HELP +//#define FEATURE_HELL +#define FEATURE_PS2_KEYBOARD // Use a PS2 keyboard to send code - Change keyboard layout (non-US) in K3NG_PS2Keyboard.h. Additional options below. +//#define FEATURE_USB_KEYBOARD // Use a USB keyboard to send code - Uncomment three lines in k3ng_keyer.ino (search for note_usb_uncomment_lines) +//#define FEATURE_DEAD_OP_WATCHDOG +//#define FEATURE_AUTOSPACE +//#define FEATURE_FARNSWORTH +//#define FEATURE_DL2SBA_BANKSWITCH // Switch memory banks feature as described here: http://dl2sba.com/index.php?option=com_content&view=article&id=131:nanokeyer&catid=15:shack&Itemid=27#english +#define FEATURE_LCD_4BIT // classic LCD disidefplay using 4 I/O lines +//#define FEATURE_LCD_ADAFRUIT_I2C // Adafruit I2C LCD display using MCP23017 at addr 0x20 +//#define FEATURE_LCD_YDv1 // YourDuino I2C LCD display with old LCM 1602 V1 ic +//#define FEATURE_LCD_FABO_PCF8574 // https://github.com/FaBoPlatform/FaBoLCD-PCF8574-Library +#define FEATURE_CW_DECODER +//#define FEATURE_SLEEP // go to sleep after x minutes to conserve battery power +//#define FEATURE_ROTARY_ENCODER // rotary encoder speed control +//#define FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING +//#define FEATURE_USB_MOUSE // Uncomment three lines in k3ng_keyer.ino (search for note_usb_uncomment_lines) +//#define FEATURE_CAPACITIVE_PADDLE_PINS // remove the bypass capacitors on the paddle_left and paddle_right lines when using capactive paddles +//#define FEATURE_LED_RING // Mayhew Labs Led Ring support +#define FEATURE_ALPHABET_SEND_PRACTICE // enables command mode S command - created by Ryan, KC2ZWM +//#define FEATURE_COMMAND_MODE_PROGRESSIVE_5_CHAR_ECHO_PRACTICE // enables command mode U +//#define FEATURE_PTT_INTERLOCK +//#define FEATURE_QLF +//#define FEATURE_EEPROM_E24C1024 +//#define FEATURE_STRAIGHT_KEY +//#define FEATURE_DYNAMIC_DAH_TO_DIT_RATIO +//#define FEATURE_PADDLE_ECHO +//#define FEATURE_STRAIGHT_KEY_ECHO +//#define FEATURE_AMERICAN_MORSE +//#define FEATURE_4x4_KEYPAD // code contributed by Jack, W0XR - documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/380-Feature:-Keypad +//#define FEATURE_3x4_KEYPAD // code contributed by Jack, W0XR - documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/380-Feature:-Keypad +//#define FEATURE_SEQUENCER + +//#define FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT // Activate the Command Line interface on the secondary serial port +#define OPTION_PRIMARY_SERIAL_PORT_DEFAULT_WINKEY_EMULATION // Use when activating both FEATURE_WINKEY_EMULATION and FEATURE_COMMAND_LINE_INTERFACE + // simultaneously. This will make Winkey emulation be the default at boot up; + // hold command button down at boot up to activate CLI mode + +//#define OPTION_SUPPRESS_SERIAL_BOOT_MSG +#define OPTION_INCLUDE_PTT_TAIL_FOR_MANUAL_SENDING +#define OPTION_EXCLUDE_PTT_HANG_TIME_FOR_MANUAL_SENDING +#define OPTION_SERIAL_PORT_DEFAULT_WINKEY_EMULATION // Use when activating both FEATURE_WINKEY_EMULATION and FEATURE_COMMAND_LINE_INTERFACE simultaneously. This will make Winkey emulation be the default at boot up; hold command button down at boot up to activate CLI mode +//#define OPTION_WINKEY_DISCARD_BYTES_AT_STARTUP // if ASR is not disabled, you may need this to discard errant serial port bytes at startup +//#define OPTION_WINKEY_STRICT_EEPROM_WRITES_MAY_WEAR_OUT_EEPROM // with this activated the unit will write non-volatile settings to EEPROM when set by Winkey commands +//#define OPTION_WINKEY_SEND_WORDSPACE_AT_END_OF_BUFFER +#define OPTION_WINKEY_STRICT_HOST_OPEN // require an admin host open Winkey command before doing any other commands +#define OPTION_WINKEY_2_SUPPORT // comment out to revert to Winkey version 1 emulation +#define OPTION_WINKEY_INTERRUPTS_MEMORY_REPEAT +//#define OPTION_WINKEY_UCXLOG_9600_BAUD // use this only with UCXLog configured for Winkey 9600 baud mode +#define OPTION_WINKEY_2_HOST_CLOSE_NO_SERIAL_PORT_RESET // activate this when using Winkey 2 emulation and Win-Test +#define OPTION_WINKEY_FREQUENT_STATUS_REPORT // activate this to make Winkey emulation play better with RUMlog and RUMped +#define OPTION_WINKEY_IGNORE_LOWERCASE // Enable for typical K1EL Winkeyer behavior (use for SkookumLogger version 1.10.14 and prior to workaround bug) +//#define OPTION_REVERSE_BUTTON_ORDER // This is mainly for the DJ0MY NanoKeyer http://nanokeyer.wordpress.com/ +#define OPTION_PROG_MEM_TRIM_TRAILING_SPACES // trim trailing spaces from memory when programming in command mode +#define OPTION_DIT_PADDLE_NO_SEND_ON_MEM_RPT // this makes dit paddle memory interruption a little smoother +//#define OPTION_MORE_DISPLAY_MSGS // additional optional display messages - comment out to save memory +//#define OPTION_WATCHDOG_TIMER // this enables a four second ATmega48/88/168/328 watchdog timer; use for unattended/remote operation only +//#define OPTION_MOUSE_MOVEMENT_PADDLE // experimental (just fooling around) - mouse movement will act like a paddle +//#define OPTION_NON_ENGLISH_EXTENSIONS // add support for additional CW characters (i.e. À, Å, Þ, etc.) +//#define OPTION_KEEP_PTT_KEYED_WHEN_CHARS_BUFFERED // this option keeps PTT high if there are characters buffered from the keyboard, the serial interface, or Winkey +//#define OPTION_DISPLAY_NON_ENGLISH_EXTENSIONS // LCD display suport for non-English (NO/DK/DE) characters - Courtesy of OZ1JHM +//#define OPTION_UNKNOWN_CHARACTER_ERROR_TONE +//#define OPTION_DO_NOT_SAY_HI +//#define OPTION_PS2_NON_ENGLISH_CHAR_LCD_DISPLAY_SUPPORT // makes some non-English characters from the PS2 keyboard display correctly in the LCD display (donated by Marcin sp5iou) +//#define OPTION_PS2_KEYBOARD_RESET // reset the PS2 keyboard upon startup with 0xFF (contributed by Bill, W9BEL) +//#define OPTION_SAVE_MEMORY_NANOKEYER +//#define OPTION_CW_KEYBOARD_CAPSLOCK_BEEP +#define OPTION_CW_KEYBOARD_ITALIAN +//#define OPTION_INVERT_PADDLE_PIN_LOGIC +#define OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR +//#define OPTION_ADVANCED_SPEED_DISPLAY //enables "nerd" speed visualization on display: wpm, cpm (char per min), duration of dit and dah in milliseconds and ratio (contributed by Giorgio, IZ2XBZ) +//#define OPTION_RUSSIAN_LANGUAGE_SEND_CLI // Russian language CLI sending support (contributed by Павел Бирюков, UA1AQC) +//#define OPTION_DO_NOT_SEND_UNKNOWN_CHAR_QUESTION +//#define OPTION_CMOS_SUPER_KEYER_IAMBIC_B_TIMING_ON_BY_DEFAULT +//#define OPTION_SIDETONE_DIGITAL_OUTPUT_NO_SQUARE_WAVE + +// #define OPTION_WORDSWORTH_CZECH +// #define OPTION_WORDSWORTH_DEUTSCH +// #define OPTION_WORDSWORTH_NORSK + +#define OPTION_EXCLUDE_EXTENDED_CLI_COMMANDS + +// #define OPTION_DFROBOT_LCD_COMMAND_BUTTONS + +#define OPTION_EXCLUDE_MILL_MODE + diff --git a/preconfigurations/YaaCWk/so2r/PS2_WINKEY_Atmega644P/keyer_pin_settings_yaacwk.h b/preconfigurations/YaaCWk/so2r/PS2_WINKEY_Atmega644P/keyer_pin_settings_yaacwk.h new file mode 100755 index 0000000..3481f4c --- /dev/null +++ b/preconfigurations/YaaCWk/so2r/PS2_WINKEY_Atmega644P/keyer_pin_settings_yaacwk.h @@ -0,0 +1,143 @@ +#ifndef keyer_pin_settings_h +#define keyer_pin_settings_h + +#define paddle_left 21 +#define paddle_right 22 +#define tx_key_line_1 28 // (high = key down/tx on) +#define tx_key_line_2 31 +#define tx_key_line_3 0 +#define tx_key_line_4 0 +#define tx_key_line_5 0 +#define tx_key_line_6 0 +#define sidetone_line 23 // connect a speaker for sidetone +#define potentiometer A0 // Speed potentiometer (0 to 5 V) Use pot from 1k to 10k +#define ptt_tx_1 0 // PTT ("push to talk") lines +#define ptt_tx_2 0 // Can be used for keying fox transmitter, T/R switch, or keying slow boatanchors +#define ptt_tx_3 0 // These are optional - set to 0 if unused +#define ptt_tx_4 0 +#define ptt_tx_5 0 +#define ptt_tx_6 0 +#define tx_key_dit 0 // if defined, goes active for dit (any transmitter) - customized with tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state +#define tx_key_dah 0 // if defined, goes active for dah (any transmitter) - customized with tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state + +#define potentiometer_enable_pin 0 // if defined, the potentiometer will be enabled only when this pin is held low; set to 0 to ignore this pin + +#ifdef FEATURE_COMMAND_BUTTONS + #define analog_buttons_pin A1 + #define command_mode_active_led 0 +#endif //FEATURE_COMMAND_BUTTONS + +/* +FEATURE_SIDETONE_SWITCH + Enabling this feature and an external toggle switch adds switch control for playing cw sidetone. + ST Switch status is displayed in the status command. This feature will override the software control of the sidetone (\o). + Arduino pin is assigned by SIDETONE_SWITCH +*/ + +#ifdef FEATURE_SIDETONE_SWITCH + #define SIDETONE_SWITCH 0 +#endif //FEATURE_SIDETONE_SWITCH + + +//lcd pins +#ifdef FEATURE_LCD_4BIT + #define lcd_rs 26 // A2 + #define lcd_enable 20 + #define lcd_d4 12 + #define lcd_d5 13 + #define lcd_d6 14 + #define lcd_d7 15 +#endif //FEATURE_LCD_4BIT + +//ps2 keyboard pins +#ifdef FEATURE_PS2_KEYBOARD + #define ps2_keyboard_data A3 + #define ps2_keyboard_clock 2 // this must be on an interrupt capable pin! +#endif //FEATURE_PS2_KEYBOARD + +// rotary encoder pins and options - rotary encoder code from Jim Balls M0CKE +#ifdef FEATURE_ROTARY_ENCODER + #define OPTION_ENCODER_HALF_STEP_MODE // Half-step mode? + #define rotary_pin1 0 // CW Encoder Pin + #define rotary_pin2 0 // CCW Encoder Pin + #define OPTION_ENCODER_ENABLE_PULLUPS // define to enable weak pullups. +#endif //FEATURE_ROTARY_ENCODER + +#ifdef FEATURE_LED_RING + #define led_ring_sdi A10 //2 //Data + #define led_ring_clk A9 //3 //Clock + #define led_ring_le A8 //4 //Latch +#endif //FEATURE_LED_RING + +#ifdef FEATURE_ALPHABET_SEND_PRACTICE + #define correct_answer_led 0 + #define wrong_answer_led 0 +#endif //FEATURE_ALPHABET_SEND_PRACTICE + +#ifdef FEATURE_PTT_INTERLOCK + #define ptt_interlock 0 // this pin disables PTT and TX KEY +#endif //FEATURE_PTT_INTERLOCK + +#ifdef FEATURE_STRAIGHT_KEY + #define pin_straight_key 0 +#endif //FEATURE_STRAIGHT_KEY + +#ifdef FEATURE_CW_DECODER + #define cw_decoder_pin A5 //A11 //A3 + #ifdef OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR + #define cw_decoder_audio_input_pin A5 // this must be an analog pin! + #endif //OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR + #define cw_decoder_indicator 30 //A6 +#endif //FEATURE_CW_DECODER + +#if defined(FEATURE_COMPETITION_COMPRESSION_DETECTION) + #define compression_detection_pin 0 +#endif //FEATURE_COMPETITION_COMPRESSION_DETECTION + +#if defined(FEATURE_SLEEP) + #define keyer_awake 0 // Goes active when keyer is awake, inactive when in sleep mode; change active and inactive states in keyer_settings file +#endif + +#if defined(FEATURE_CAPACITIVE_PADDLE_PINS) + #define capactive_paddle_pin_inhibit_pin 0 // if this pin is defined and is set high, the capacitive paddle pins will switch to normal (non-capacitive) sensing mode +#endif + +#ifdef FEATURE_4x4_KEYPAD + #define Row3 33 + #define Row2 32 + #define Row1 31 + #define Row0 30 + #define Col3 37 + #define Col2 36 + #define Col1 35 + #define Col0 34 +#endif + +#ifdef FEATURE_3x4_KEYPAD + #define Row3 33 + #define Row2 32 + #define Row1 31 + #define Row0 30 + #define Col2 36 + #define Col1 35 + #define Col0 34 +#endif + +#ifdef FEATURE_SEQUENCER + #define sequencer_1_pin 0 + #define sequencer_2_pin 0 + #define sequencer_3_pin 0 + #define sequencer_4_pin 0 + #define sequencer_5_pin 0 +#endif //FEATURE_SEQUENCER + +#define ptt_input_pin 0 + +#define tx_inhibit_pin 0 +#define tx_pause_pin 0 + +#else + + #error "Multiple pin_settings.h files included somehow..." + +#endif //keyer_pin_settings_h diff --git a/preconfigurations/YaaCWk/so2r/USB_Atmega644P/keyer_features_and_options_yaacwk.h b/preconfigurations/YaaCWk/so2r/USB_Atmega644P/keyer_features_and_options_yaacwk.h new file mode 100755 index 0000000..8916860 --- /dev/null +++ b/preconfigurations/YaaCWk/so2r/USB_Atmega644P/keyer_features_and_options_yaacwk.h @@ -0,0 +1,103 @@ +// This file is for the Yaacwk interface http://i1cra.briata.org/yaacwk/ +// YAACWK stands for Yet Another Arduino CW Keyer, it's based on AtMega 644p +// see http://i1cra.briata.org/yaacwk/ for more info + +// compile time features and options - comment or uncomment to add or delete features +// FEATURES add more bytes to the compiled binary, OPTIONS change code behavior + +#define FEATURE_COMMAND_BUTTONS +#define FEATURE_COMMAND_LINE_INTERFACE // (this no longer requires FEATURE_SERIAL) +#define FEATURE_MEMORIES +//#define FEATURE_MEMORY_MACROS +//#define FEATURE_WINKEY_EMULATION // disabling Automatic Software Reset is highly recommended (see documentation) (this no longer requires FEATURE_SERIAL) +//#define FEATURE_BEACON +//#define FEATURE_TRAINING_COMMAND_LINE_INTERFACE +#define FEATURE_POTENTIOMETER // do not enable unless you have a potentiometer connected, otherwise noise will falsely trigger wpm changes +//#define FEATURE_SIDETONE_SWITCH // adds switch control for the sidetone output. requires an external toggle switch (assigned to an arduino pin - see keyer_pin_settings.h). +//#define FEATURE_SERIAL_HELP +//#define FEATURE_HELL +//#define FEATURE_PS2_KEYBOARD // Use a PS2 keyboard to send code - Change keyboard layout (non-US) in K3NG_PS2Keyboard.h. Additional options below. +#define FEATURE_USB_KEYBOARD // Use a USB keyboard to send code - Uncomment three lines in k3ng_keyer.ino (search for note_usb_uncomment_lines) +//#define FEATURE_DEAD_OP_WATCHDOG +//#define FEATURE_AUTOSPACE +//#define FEATURE_FARNSWORTH +//#define FEATURE_DL2SBA_BANKSWITCH // Switch memory banks feature as described here: http://dl2sba.com/index.php?option=com_content&view=article&id=131:nanokeyer&catid=15:shack&Itemid=27#english +#define FEATURE_LCD_4BIT // classic LCD disidefplay using 4 I/O lines +//#define FEATURE_LCD_ADAFRUIT_I2C // Adafruit I2C LCD display using MCP23017 at addr 0x20 +//#define FEATURE_LCD_YDv1 // YourDuino I2C LCD display with old LCM 1602 V1 ic +//#define FEATURE_LCD_FABO_PCF8574 // https://github.com/FaBoPlatform/FaBoLCD-PCF8574-Library +#define FEATURE_CW_DECODER +//#define FEATURE_SLEEP // go to sleep after x minutes to conserve battery power +//#define FEATURE_ROTARY_ENCODER // rotary encoder speed control +//#define FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING +//#define FEATURE_USB_MOUSE // Uncomment three lines in k3ng_keyer.ino (search for note_usb_uncomment_lines) +//#define FEATURE_CAPACITIVE_PADDLE_PINS // remove the bypass capacitors on the paddle_left and paddle_right lines when using capactive paddles +//#define FEATURE_LED_RING // Mayhew Labs Led Ring support +#define FEATURE_ALPHABET_SEND_PRACTICE // enables command mode S command - created by Ryan, KC2ZWM +//#define FEATURE_COMMAND_MODE_PROGRESSIVE_5_CHAR_ECHO_PRACTICE // enables command mode U +//#define FEATURE_PTT_INTERLOCK +//#define FEATURE_QLF +//#define FEATURE_EEPROM_E24C1024 +//#define FEATURE_STRAIGHT_KEY +//#define FEATURE_DYNAMIC_DAH_TO_DIT_RATIO +//#define FEATURE_PADDLE_ECHO +//#define FEATURE_STRAIGHT_KEY_ECHO +//#define FEATURE_AMERICAN_MORSE +//#define FEATURE_4x4_KEYPAD // code contributed by Jack, W0XR - documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/380-Feature:-Keypad +//#define FEATURE_3x4_KEYPAD // code contributed by Jack, W0XR - documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/380-Feature:-Keypad +//#define FEATURE_SEQUENCER + +//#define FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT // Activate the Command Line interface on the secondary serial port +#define OPTION_PRIMARY_SERIAL_PORT_DEFAULT_WINKEY_EMULATION // Use when activating both FEATURE_WINKEY_EMULATION and FEATURE_COMMAND_LINE_INTERFACE + // simultaneously. This will make Winkey emulation be the default at boot up; + // hold command button down at boot up to activate CLI mode + +//#define OPTION_SUPPRESS_SERIAL_BOOT_MSG +#define OPTION_INCLUDE_PTT_TAIL_FOR_MANUAL_SENDING +#define OPTION_EXCLUDE_PTT_HANG_TIME_FOR_MANUAL_SENDING +//#define OPTION_SERIAL_PORT_DEFAULT_WINKEY_EMULATION // Use when activating both FEATURE_WINKEY_EMULATION and FEATURE_COMMAND_LINE_INTERFACE simultaneously. This will make Winkey emulation be the default at boot up; hold command button down at boot up to activate CLI mode +//#define OPTION_WINKEY_DISCARD_BYTES_AT_STARTUP // if ASR is not disabled, you may need this to discard errant serial port bytes at startup +//#define OPTION_WINKEY_STRICT_EEPROM_WRITES_MAY_WEAR_OUT_EEPROM // with this activated the unit will write non-volatile settings to EEPROM when set by Winkey commands +//#define OPTION_WINKEY_SEND_WORDSPACE_AT_END_OF_BUFFER +//#define OPTION_WINKEY_STRICT_HOST_OPEN // require an admin host open Winkey command before doing any other commands +//#define OPTION_WINKEY_2_SUPPORT // comment out to revert to Winkey version 1 emulation +//#define OPTION_WINKEY_INTERRUPTS_MEMORY_REPEAT +//#define OPTION_WINKEY_UCXLOG_9600_BAUD // use this only with UCXLog configured for Winkey 9600 baud mode +//#define OPTION_WINKEY_2_HOST_CLOSE_NO_SERIAL_PORT_RESET // (Required for Win-Test to function) +//#define OPTION_WINKEY_FREQUENT_STATUS_REPORT // activate this to make Winkey emulation play better with RUMlog and RUMped +//#define OPTION_WINKEY_IGNORE_LOWERCASE // Enable for typical K1EL Winkeyer behavior (use for SkookumLogger version 1.10.14 and prior to workaround bug) +//#define OPTION_WINKEY_BLINK_PTT_ON_HOST_OPEN +//#define OPTION_REVERSE_BUTTON_ORDER // This is mainly for the DJ0MY NanoKeyer http://nanokeyer.wordpress.com/ +#define OPTION_PROG_MEM_TRIM_TRAILING_SPACES // trim trailing spaces from memory when programming in command mode +#define OPTION_DIT_PADDLE_NO_SEND_ON_MEM_RPT // this makes dit paddle memory interruption a little smoother +//#define OPTION_MORE_DISPLAY_MSGS // additional optional display messages - comment out to save memory +//#define OPTION_WATCHDOG_TIMER // this enables a four second ATmega48/88/168/328 watchdog timer; use for unattended/remote operation only +//#define OPTION_MOUSE_MOVEMENT_PADDLE // experimental (just fooling around) - mouse movement will act like a paddle +//#define OPTION_NON_ENGLISH_EXTENSIONS // add support for additional CW characters (i.e. À, Å, Þ, etc.) +//#define OPTION_KEEP_PTT_KEYED_WHEN_CHARS_BUFFERED // this option keeps PTT high if there are characters buffered from the keyboard, the serial interface, or Winkey +//#define OPTION_DISPLAY_NON_ENGLISH_EXTENSIONS // LCD display suport for non-English (NO/DK/DE) characters - Courtesy of OZ1JHM +//#define OPTION_UNKNOWN_CHARACTER_ERROR_TONE +//#define OPTION_DO_NOT_SAY_HI +//#define OPTION_PS2_NON_ENGLISH_CHAR_LCD_DISPLAY_SUPPORT // makes some non-English characters from the PS2 keyboard display correctly in the LCD display (donated by Marcin sp5iou) +//#define OPTION_PS2_KEYBOARD_RESET // reset the PS2 keyboard upon startup with 0xFF (contributed by Bill, W9BEL) +//#define OPTION_SAVE_MEMORY_NANOKEYER +//#define OPTION_CW_KEYBOARD_CAPSLOCK_BEEP +#define OPTION_CW_KEYBOARD_ITALIAN +//#define OPTION_INVERT_PADDLE_PIN_LOGIC +#define OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR +//#define OPTION_ADVANCED_SPEED_DISPLAY //enables "nerd" speed visualization on display: wpm, cpm (char per min), duration of dit and dah in milliseconds and ratio (contributed by Giorgio, IZ2XBZ) +//#define OPTION_RUSSIAN_LANGUAGE_SEND_CLI // Russian language CLI sending support (contributed by Павел Бирюков, UA1AQC) +//#define OPTION_DO_NOT_SEND_UNKNOWN_CHAR_QUESTION +//#define OPTION_CMOS_SUPER_KEYER_IAMBIC_B_TIMING_ON_BY_DEFAULT +//#define OPTION_SIDETONE_DIGITAL_OUTPUT_NO_SQUARE_WAVE + +// #define OPTION_WORDSWORTH_CZECH +// #define OPTION_WORDSWORTH_DEUTSCH +// #define OPTION_WORDSWORTH_NORSK + +#define OPTION_EXCLUDE_EXTENDED_CLI_COMMANDS + +// #define OPTION_DFROBOT_LCD_COMMAND_BUTTONS + +#define OPTION_EXCLUDE_MILL_MODE + diff --git a/preconfigurations/YaaCWk/so2r/USB_Atmega644P/keyer_pin_settings_yaacwk.h b/preconfigurations/YaaCWk/so2r/USB_Atmega644P/keyer_pin_settings_yaacwk.h new file mode 100755 index 0000000..3481f4c --- /dev/null +++ b/preconfigurations/YaaCWk/so2r/USB_Atmega644P/keyer_pin_settings_yaacwk.h @@ -0,0 +1,143 @@ +#ifndef keyer_pin_settings_h +#define keyer_pin_settings_h + +#define paddle_left 21 +#define paddle_right 22 +#define tx_key_line_1 28 // (high = key down/tx on) +#define tx_key_line_2 31 +#define tx_key_line_3 0 +#define tx_key_line_4 0 +#define tx_key_line_5 0 +#define tx_key_line_6 0 +#define sidetone_line 23 // connect a speaker for sidetone +#define potentiometer A0 // Speed potentiometer (0 to 5 V) Use pot from 1k to 10k +#define ptt_tx_1 0 // PTT ("push to talk") lines +#define ptt_tx_2 0 // Can be used for keying fox transmitter, T/R switch, or keying slow boatanchors +#define ptt_tx_3 0 // These are optional - set to 0 if unused +#define ptt_tx_4 0 +#define ptt_tx_5 0 +#define ptt_tx_6 0 +#define tx_key_dit 0 // if defined, goes active for dit (any transmitter) - customized with tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state +#define tx_key_dah 0 // if defined, goes active for dah (any transmitter) - customized with tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state + +#define potentiometer_enable_pin 0 // if defined, the potentiometer will be enabled only when this pin is held low; set to 0 to ignore this pin + +#ifdef FEATURE_COMMAND_BUTTONS + #define analog_buttons_pin A1 + #define command_mode_active_led 0 +#endif //FEATURE_COMMAND_BUTTONS + +/* +FEATURE_SIDETONE_SWITCH + Enabling this feature and an external toggle switch adds switch control for playing cw sidetone. + ST Switch status is displayed in the status command. This feature will override the software control of the sidetone (\o). + Arduino pin is assigned by SIDETONE_SWITCH +*/ + +#ifdef FEATURE_SIDETONE_SWITCH + #define SIDETONE_SWITCH 0 +#endif //FEATURE_SIDETONE_SWITCH + + +//lcd pins +#ifdef FEATURE_LCD_4BIT + #define lcd_rs 26 // A2 + #define lcd_enable 20 + #define lcd_d4 12 + #define lcd_d5 13 + #define lcd_d6 14 + #define lcd_d7 15 +#endif //FEATURE_LCD_4BIT + +//ps2 keyboard pins +#ifdef FEATURE_PS2_KEYBOARD + #define ps2_keyboard_data A3 + #define ps2_keyboard_clock 2 // this must be on an interrupt capable pin! +#endif //FEATURE_PS2_KEYBOARD + +// rotary encoder pins and options - rotary encoder code from Jim Balls M0CKE +#ifdef FEATURE_ROTARY_ENCODER + #define OPTION_ENCODER_HALF_STEP_MODE // Half-step mode? + #define rotary_pin1 0 // CW Encoder Pin + #define rotary_pin2 0 // CCW Encoder Pin + #define OPTION_ENCODER_ENABLE_PULLUPS // define to enable weak pullups. +#endif //FEATURE_ROTARY_ENCODER + +#ifdef FEATURE_LED_RING + #define led_ring_sdi A10 //2 //Data + #define led_ring_clk A9 //3 //Clock + #define led_ring_le A8 //4 //Latch +#endif //FEATURE_LED_RING + +#ifdef FEATURE_ALPHABET_SEND_PRACTICE + #define correct_answer_led 0 + #define wrong_answer_led 0 +#endif //FEATURE_ALPHABET_SEND_PRACTICE + +#ifdef FEATURE_PTT_INTERLOCK + #define ptt_interlock 0 // this pin disables PTT and TX KEY +#endif //FEATURE_PTT_INTERLOCK + +#ifdef FEATURE_STRAIGHT_KEY + #define pin_straight_key 0 +#endif //FEATURE_STRAIGHT_KEY + +#ifdef FEATURE_CW_DECODER + #define cw_decoder_pin A5 //A11 //A3 + #ifdef OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR + #define cw_decoder_audio_input_pin A5 // this must be an analog pin! + #endif //OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR + #define cw_decoder_indicator 30 //A6 +#endif //FEATURE_CW_DECODER + +#if defined(FEATURE_COMPETITION_COMPRESSION_DETECTION) + #define compression_detection_pin 0 +#endif //FEATURE_COMPETITION_COMPRESSION_DETECTION + +#if defined(FEATURE_SLEEP) + #define keyer_awake 0 // Goes active when keyer is awake, inactive when in sleep mode; change active and inactive states in keyer_settings file +#endif + +#if defined(FEATURE_CAPACITIVE_PADDLE_PINS) + #define capactive_paddle_pin_inhibit_pin 0 // if this pin is defined and is set high, the capacitive paddle pins will switch to normal (non-capacitive) sensing mode +#endif + +#ifdef FEATURE_4x4_KEYPAD + #define Row3 33 + #define Row2 32 + #define Row1 31 + #define Row0 30 + #define Col3 37 + #define Col2 36 + #define Col1 35 + #define Col0 34 +#endif + +#ifdef FEATURE_3x4_KEYPAD + #define Row3 33 + #define Row2 32 + #define Row1 31 + #define Row0 30 + #define Col2 36 + #define Col1 35 + #define Col0 34 +#endif + +#ifdef FEATURE_SEQUENCER + #define sequencer_1_pin 0 + #define sequencer_2_pin 0 + #define sequencer_3_pin 0 + #define sequencer_4_pin 0 + #define sequencer_5_pin 0 +#endif //FEATURE_SEQUENCER + +#define ptt_input_pin 0 + +#define tx_inhibit_pin 0 +#define tx_pause_pin 0 + +#else + + #error "Multiple pin_settings.h files included somehow..." + +#endif //keyer_pin_settings_h diff --git a/preconfigurations/YaaCWk/so2r/USB_WINKEY_Atmega1284P/keyer_features_and_options_yaacwk.h b/preconfigurations/YaaCWk/so2r/USB_WINKEY_Atmega1284P/keyer_features_and_options_yaacwk.h new file mode 100755 index 0000000..e25be79 --- /dev/null +++ b/preconfigurations/YaaCWk/so2r/USB_WINKEY_Atmega1284P/keyer_features_and_options_yaacwk.h @@ -0,0 +1,102 @@ +// This file is for the Yaacwk interface http://i1cra.briata.org/yaacwk/ +// YAACWK stands for Yet Another Arduino CW Keyer, it's based on AtMega 644p +// see http://i1cra.briata.org/yaacwk/ for more info + +// compile time features and options - comment or uncomment to add or delete features +// FEATURES add more bytes to the compiled binary, OPTIONS change code behavior + +#define FEATURE_COMMAND_BUTTONS +#define FEATURE_COMMAND_LINE_INTERFACE // (this no longer requires FEATURE_SERIAL) +#define FEATURE_MEMORIES +//#define FEATURE_MEMORY_MACROS +#define FEATURE_WINKEY_EMULATION // disabling Automatic Software Reset is highly recommended (see documentation) (this no longer requires FEATURE_SERIAL) +//#define FEATURE_BEACON +//#define FEATURE_TRAINING_COMMAND_LINE_INTERFACE +#define FEATURE_POTENTIOMETER // do not enable unless you have a potentiometer connected, otherwise noise will falsely trigger wpm changes +//#define FEATURE_SIDETONE_SWITCH // adds switch control for the sidetone output. requires an external toggle switch (assigned to an arduino pin - see keyer_pin_settings.h). +//#define FEATURE_SERIAL_HELP +//#define FEATURE_HELL +//#define FEATURE_PS2_KEYBOARD // Use a PS2 keyboard to send code - Change keyboard layout (non-US) in K3NG_PS2Keyboard.h. Additional options below. +#define FEATURE_USB_KEYBOARD // Use a USB keyboard to send code - Uncomment three lines in k3ng_keyer.ino (search for note_usb_uncomment_lines) +//#define FEATURE_DEAD_OP_WATCHDOG +//#define FEATURE_AUTOSPACE +//#define FEATURE_FARNSWORTH +//#define FEATURE_DL2SBA_BANKSWITCH // Switch memory banks feature as described here: http://dl2sba.com/index.php?option=com_content&view=article&id=131:nanokeyer&catid=15:shack&Itemid=27#english +#define FEATURE_LCD_4BIT // classic LCD disidefplay using 4 I/O lines +//#define FEATURE_LCD_ADAFRUIT_I2C // Adafruit I2C LCD display using MCP23017 at addr 0x20 +//#define FEATURE_LCD_YDv1 // YourDuino I2C LCD display with old LCM 1602 V1 ic +//#define FEATURE_LCD_FABO_PCF8574 // https://github.com/FaBoPlatform/FaBoLCD-PCF8574-Library +#define FEATURE_CW_DECODER +//#define FEATURE_SLEEP // go to sleep after x minutes to conserve battery power +//#define FEATURE_ROTARY_ENCODER // rotary encoder speed control +//#define FEATURE_CMOS_SUPER_KEYER_IAMBIC_B_TIMING +//#define FEATURE_USB_MOUSE // Uncomment three lines in k3ng_keyer.ino (search for note_usb_uncomment_lines) +//#define FEATURE_CAPACITIVE_PADDLE_PINS // remove the bypass capacitors on the paddle_left and paddle_right lines when using capactive paddles +//#define FEATURE_LED_RING // Mayhew Labs Led Ring support +#define FEATURE_ALPHABET_SEND_PRACTICE // enables command mode S command - created by Ryan, KC2ZWM +//#define FEATURE_COMMAND_MODE_PROGRESSIVE_5_CHAR_ECHO_PRACTICE // enables command mode U +//#define FEATURE_PTT_INTERLOCK +//#define FEATURE_QLF +//#define FEATURE_EEPROM_E24C1024 +//#define FEATURE_STRAIGHT_KEY +//#define FEATURE_DYNAMIC_DAH_TO_DIT_RATIO +//#define FEATURE_PADDLE_ECHO +//#define FEATURE_STRAIGHT_KEY_ECHO +//#define FEATURE_AMERICAN_MORSE +//#define FEATURE_4x4_KEYPAD // code contributed by Jack, W0XR - documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/380-Feature:-Keypad +//#define FEATURE_3x4_KEYPAD // code contributed by Jack, W0XR - documentation: https://github.com/k3ng/k3ng_cw_keyer/wiki/380-Feature:-Keypad +//#define FEATURE_SEQUENCER + +//#define FEATURE_COMMAND_LINE_INTERFACE_ON_SECONDARY_PORT // Activate the Command Line interface on the secondary serial port +#define OPTION_PRIMARY_SERIAL_PORT_DEFAULT_WINKEY_EMULATION // Use when activating both FEATURE_WINKEY_EMULATION and FEATURE_COMMAND_LINE_INTERFACE + // simultaneously. This will make Winkey emulation be the default at boot up; + // hold command button down at boot up to activate CLI mode + +//#define OPTION_SUPPRESS_SERIAL_BOOT_MSG +#define OPTION_INCLUDE_PTT_TAIL_FOR_MANUAL_SENDING +#define OPTION_EXCLUDE_PTT_HANG_TIME_FOR_MANUAL_SENDING +#define OPTION_SERIAL_PORT_DEFAULT_WINKEY_EMULATION // Use when activating both FEATURE_WINKEY_EMULATION and FEATURE_COMMAND_LINE_INTERFACE simultaneously. This will make Winkey emulation be the default at boot up; hold command button down at boot up to activate CLI mode +//#define OPTION_WINKEY_DISCARD_BYTES_AT_STARTUP // if ASR is not disabled, you may need this to discard errant serial port bytes at startup +//#define OPTION_WINKEY_STRICT_EEPROM_WRITES_MAY_WEAR_OUT_EEPROM // with this activated the unit will write non-volatile settings to EEPROM when set by Winkey commands +//#define OPTION_WINKEY_SEND_WORDSPACE_AT_END_OF_BUFFER +#define OPTION_WINKEY_STRICT_HOST_OPEN // require an admin host open Winkey command before doing any other commands +#define OPTION_WINKEY_2_SUPPORT // comment out to revert to Winkey version 1 emulation +#define OPTION_WINKEY_INTERRUPTS_MEMORY_REPEAT +//#define OPTION_WINKEY_UCXLOG_9600_BAUD // use this only with UCXLog configured for Winkey 9600 baud mode +#define OPTION_WINKEY_2_HOST_CLOSE_NO_SERIAL_PORT_RESET // activate this when using Winkey 2 emulation and Win-Test +#define OPTION_WINKEY_FREQUENT_STATUS_REPORT // activate this to make Winkey emulation play better with RUMlog and RUMped +#define OPTION_WINKEY_IGNORE_LOWERCASE // Enable for typical K1EL Winkeyer behavior (use for SkookumLogger version 1.10.14 and prior to workaround bug) +//#define OPTION_REVERSE_BUTTON_ORDER // This is mainly for the DJ0MY NanoKeyer http://nanokeyer.wordpress.com/ +#define OPTION_PROG_MEM_TRIM_TRAILING_SPACES // trim trailing spaces from memory when programming in command mode +#define OPTION_DIT_PADDLE_NO_SEND_ON_MEM_RPT // this makes dit paddle memory interruption a little smoother +//#define OPTION_MORE_DISPLAY_MSGS // additional optional display messages - comment out to save memory +//#define OPTION_WATCHDOG_TIMER // this enables a four second ATmega48/88/168/328 watchdog timer; use for unattended/remote operation only +//#define OPTION_MOUSE_MOVEMENT_PADDLE // experimental (just fooling around) - mouse movement will act like a paddle +//#define OPTION_NON_ENGLISH_EXTENSIONS // add support for additional CW characters (i.e. À, Å, Þ, etc.) +//#define OPTION_KEEP_PTT_KEYED_WHEN_CHARS_BUFFERED // this option keeps PTT high if there are characters buffered from the keyboard, the serial interface, or Winkey +//#define OPTION_DISPLAY_NON_ENGLISH_EXTENSIONS // LCD display suport for non-English (NO/DK/DE) characters - Courtesy of OZ1JHM +//#define OPTION_UNKNOWN_CHARACTER_ERROR_TONE +//#define OPTION_DO_NOT_SAY_HI +//#define OPTION_PS2_NON_ENGLISH_CHAR_LCD_DISPLAY_SUPPORT // makes some non-English characters from the PS2 keyboard display correctly in the LCD display (donated by Marcin sp5iou) +//#define OPTION_PS2_KEYBOARD_RESET // reset the PS2 keyboard upon startup with 0xFF (contributed by Bill, W9BEL) +//#define OPTION_SAVE_MEMORY_NANOKEYER +//#define OPTION_CW_KEYBOARD_CAPSLOCK_BEEP +#define OPTION_CW_KEYBOARD_ITALIAN +//#define OPTION_INVERT_PADDLE_PIN_LOGIC +#define OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR +//#define OPTION_ADVANCED_SPEED_DISPLAY //enables "nerd" speed visualization on display: wpm, cpm (char per min), duration of dit and dah in milliseconds and ratio (contributed by Giorgio, IZ2XBZ) +//#define OPTION_RUSSIAN_LANGUAGE_SEND_CLI // Russian language CLI sending support (contributed by Павел Бирюков, UA1AQC) +//#define OPTION_DO_NOT_SEND_UNKNOWN_CHAR_QUESTION +//#define OPTION_CMOS_SUPER_KEYER_IAMBIC_B_TIMING_ON_BY_DEFAULT +//#define OPTION_SIDETONE_DIGITAL_OUTPUT_NO_SQUARE_WAVE + +// #define OPTION_WORDSWORTH_CZECH +// #define OPTION_WORDSWORTH_DEUTSCH +// #define OPTION_WORDSWORTH_NORSK + +#define OPTION_EXCLUDE_EXTENDED_CLI_COMMANDS + +// #define OPTION_DFROBOT_LCD_COMMAND_BUTTONS + +#define OPTION_EXCLUDE_MILL_MODE + diff --git a/preconfigurations/YaaCWk/so2r/USB_WINKEY_Atmega1284P/keyer_pin_settings_yaacwk.h b/preconfigurations/YaaCWk/so2r/USB_WINKEY_Atmega1284P/keyer_pin_settings_yaacwk.h new file mode 100755 index 0000000..3481f4c --- /dev/null +++ b/preconfigurations/YaaCWk/so2r/USB_WINKEY_Atmega1284P/keyer_pin_settings_yaacwk.h @@ -0,0 +1,143 @@ +#ifndef keyer_pin_settings_h +#define keyer_pin_settings_h + +#define paddle_left 21 +#define paddle_right 22 +#define tx_key_line_1 28 // (high = key down/tx on) +#define tx_key_line_2 31 +#define tx_key_line_3 0 +#define tx_key_line_4 0 +#define tx_key_line_5 0 +#define tx_key_line_6 0 +#define sidetone_line 23 // connect a speaker for sidetone +#define potentiometer A0 // Speed potentiometer (0 to 5 V) Use pot from 1k to 10k +#define ptt_tx_1 0 // PTT ("push to talk") lines +#define ptt_tx_2 0 // Can be used for keying fox transmitter, T/R switch, or keying slow boatanchors +#define ptt_tx_3 0 // These are optional - set to 0 if unused +#define ptt_tx_4 0 +#define ptt_tx_5 0 +#define ptt_tx_6 0 +#define tx_key_dit 0 // if defined, goes active for dit (any transmitter) - customized with tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state +#define tx_key_dah 0 // if defined, goes active for dah (any transmitter) - customized with tx_key_dit_and_dah_pins_active_state and tx_key_dit_and_dah_pins_inactive_state + +#define potentiometer_enable_pin 0 // if defined, the potentiometer will be enabled only when this pin is held low; set to 0 to ignore this pin + +#ifdef FEATURE_COMMAND_BUTTONS + #define analog_buttons_pin A1 + #define command_mode_active_led 0 +#endif //FEATURE_COMMAND_BUTTONS + +/* +FEATURE_SIDETONE_SWITCH + Enabling this feature and an external toggle switch adds switch control for playing cw sidetone. + ST Switch status is displayed in the status command. This feature will override the software control of the sidetone (\o). + Arduino pin is assigned by SIDETONE_SWITCH +*/ + +#ifdef FEATURE_SIDETONE_SWITCH + #define SIDETONE_SWITCH 0 +#endif //FEATURE_SIDETONE_SWITCH + + +//lcd pins +#ifdef FEATURE_LCD_4BIT + #define lcd_rs 26 // A2 + #define lcd_enable 20 + #define lcd_d4 12 + #define lcd_d5 13 + #define lcd_d6 14 + #define lcd_d7 15 +#endif //FEATURE_LCD_4BIT + +//ps2 keyboard pins +#ifdef FEATURE_PS2_KEYBOARD + #define ps2_keyboard_data A3 + #define ps2_keyboard_clock 2 // this must be on an interrupt capable pin! +#endif //FEATURE_PS2_KEYBOARD + +// rotary encoder pins and options - rotary encoder code from Jim Balls M0CKE +#ifdef FEATURE_ROTARY_ENCODER + #define OPTION_ENCODER_HALF_STEP_MODE // Half-step mode? + #define rotary_pin1 0 // CW Encoder Pin + #define rotary_pin2 0 // CCW Encoder Pin + #define OPTION_ENCODER_ENABLE_PULLUPS // define to enable weak pullups. +#endif //FEATURE_ROTARY_ENCODER + +#ifdef FEATURE_LED_RING + #define led_ring_sdi A10 //2 //Data + #define led_ring_clk A9 //3 //Clock + #define led_ring_le A8 //4 //Latch +#endif //FEATURE_LED_RING + +#ifdef FEATURE_ALPHABET_SEND_PRACTICE + #define correct_answer_led 0 + #define wrong_answer_led 0 +#endif //FEATURE_ALPHABET_SEND_PRACTICE + +#ifdef FEATURE_PTT_INTERLOCK + #define ptt_interlock 0 // this pin disables PTT and TX KEY +#endif //FEATURE_PTT_INTERLOCK + +#ifdef FEATURE_STRAIGHT_KEY + #define pin_straight_key 0 +#endif //FEATURE_STRAIGHT_KEY + +#ifdef FEATURE_CW_DECODER + #define cw_decoder_pin A5 //A11 //A3 + #ifdef OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR + #define cw_decoder_audio_input_pin A5 // this must be an analog pin! + #endif //OPTION_CW_DECODER_GOERTZEL_AUDIO_DETECTOR + #define cw_decoder_indicator 30 //A6 +#endif //FEATURE_CW_DECODER + +#if defined(FEATURE_COMPETITION_COMPRESSION_DETECTION) + #define compression_detection_pin 0 +#endif //FEATURE_COMPETITION_COMPRESSION_DETECTION + +#if defined(FEATURE_SLEEP) + #define keyer_awake 0 // Goes active when keyer is awake, inactive when in sleep mode; change active and inactive states in keyer_settings file +#endif + +#if defined(FEATURE_CAPACITIVE_PADDLE_PINS) + #define capactive_paddle_pin_inhibit_pin 0 // if this pin is defined and is set high, the capacitive paddle pins will switch to normal (non-capacitive) sensing mode +#endif + +#ifdef FEATURE_4x4_KEYPAD + #define Row3 33 + #define Row2 32 + #define Row1 31 + #define Row0 30 + #define Col3 37 + #define Col2 36 + #define Col1 35 + #define Col0 34 +#endif + +#ifdef FEATURE_3x4_KEYPAD + #define Row3 33 + #define Row2 32 + #define Row1 31 + #define Row0 30 + #define Col2 36 + #define Col1 35 + #define Col0 34 +#endif + +#ifdef FEATURE_SEQUENCER + #define sequencer_1_pin 0 + #define sequencer_2_pin 0 + #define sequencer_3_pin 0 + #define sequencer_4_pin 0 + #define sequencer_5_pin 0 +#endif //FEATURE_SEQUENCER + +#define ptt_input_pin 0 + +#define tx_inhibit_pin 0 +#define tx_pause_pin 0 + +#else + + #error "Multiple pin_settings.h files included somehow..." + +#endif //keyer_pin_settings_h diff --git a/schema.png b/schema.png new file mode 100755 index 0000000000000000000000000000000000000000..1c0b02da1a61d6c0cc8a4f8af085532a676e9310 GIT binary patch literal 136999 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

    `O}xgS!c!U=kSTkFpA{l)W@n8zSV32O?8iz{@F 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�JnG+?7Ngq_U0JW1V}SS}AaOXk%X#FF&%iRotDkKpZW ztgA-zpH=7_C>%_-6iw;)Bi9V?OgSI%L`|A?w~UJtko)BFL6(6>T>LZO?%+7F(0b6sKWthK=?2Fq|Dv03E1f7S`8}`fyTMS`>|UjgW88lM&5i7{ literal 0 HcmV?d00001