Teensy USB driver

Hello all,

this is a question/feature request.

I have built a Lighthack-Box with 8 encoders for my element. This box uses a teensy 3.5 to send OSC via USB.

Unfortunately EOS does not recognize my teensy - eos on my PC does recognize it.

Only after I install the teensy serial driver from the teensy Homepage on my console, I can use the Box.

However, when I updated to 2.8 this driver seems to have been removed...

I will install it again before the next programming session starts and hope it works again.

It would be very helpful, if not only the arduino usb driver but also the teensy usb driver would be included into the eos software...

Or am I missing something?

Parents
  • /*
      // Copyright (c) 2017 Electronic Theatre Controls, Inc., http://www.etcconnect.com
      //
      // Permission is hereby granted, free of charge, to any person obtaining a copy
      // of this software and associated documentation files (the "Software"), to deal
      // in the Software without restriction, including without limitation the rights
      // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      // copies of the Software, and to permit persons to whom the Software is
      // furnished to do so, subject to the following conditions:
      //
      // The above copyright notice and this permission notice shall be included in
      // all copies or substantial portions of the Software.
      //
      // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
      // THE SOFTWARE.
    */
    
    /*******************************************************************************
    
         lighthack - Box 8
    
         (c) 2018 by Roland Menzel
    
     *******************************************************************************
    
        NOTE: UPDATE VERSION_STRING IN DEFINITIONS BELOW WHEN VERSION NUMBER CHANGES
    
        Revision History
    
        yyyy-mm-dd   Vxx      By_Who                 Comment
    
        2017-07-21   1.0.0.1  Ethan Oswald Massey    Original creation
    
        2017-10-19   1.0.0.2  Sam Kearney            Fix build errors on some
                                                     Arduino platforms. Change
                                                     OSC subscribe parameters
    
        2017-10-24   1.0.0.3  Sam Kearney            Add ability to scale encoder
                                                     output
    
        2017-11-22   1.0.0.4  Hans Hinrichsen        Add splash msg before Eos
                                                     connects
    
        2017-12-07   1.0.0.5  Hans Hinrichsen        Added timeout to disconnect
                                                     and show splash screen again
    
        2018-06-27   1.0.0.6  Roland Menzel          Modded for 8 encoders, 10 buttons
                                                     and a 4x40 LCD
    
     ******************************************************************************/
    
    /*******************************************************************************
       Includes
     ******************************************************************************/
    #include <OSCBoards.h>
    #include <OSCBundle.h>
    #include <OSCData.h>
    #include <OSCMatch.h>
    #include <OSCMessage.h>
    #include <OSCTiming.h>
    
    
    #ifdef BOARD_HAS_USB_SERIAL
    #include <SLIPEncodedUSBSerial.h>
    SLIPEncodedUSBSerial SLIPSerial(thisBoardsSerialUSB);
    #else
    #include <SLIPEncodedSerial.h>
    SLIPEncodedSerial SLIPSerial(Serial);
    #endif
    
    
    //#include <LiquidCrystal.h>
    #include <LiquidCrystalFast.h>
    #include <string.h>
    #include <Bounce2.h>
    
    /*******************************************************************************
       Macros and Constants
     ******************************************************************************/
    #define LCD_CHARS           40
    #define LCD_LINES           4   // Currently assume at least 4 lines
    
    #define SHIFT_BTN           6
    #define FOCUS_BTN           7
    #define SHUTTER_BTN         8
    #define COLOR_BTN           24
    #define IMAGE_BTN           25
    #define EFFEKTOFF_BTN       26
    #define CH91_BTN            27
    #define CH92_BTN            28
    #define LAST_BTN            30
    #define JOY_ENABLE_BTN      29
    #define JOYSTICK_BTN        53
    
    #define ENC1_BTN            31
    #define ENC2_BTN            32
    #define ENC3_BTN            40
    #define ENC4_BTN            41
    #define ENC5_BTN            42
    #define ENC6_BTN            43
    #define ENC7_BTN            46
    #define ENC8_BTN            57
    
    #define XPIN                A11
    #define YPIN                A10
    
    
    #define SUBSCRIBE           ((int32_t)1)
    #define UNSUBSCRIBE         ((int32_t)0)
    
    #define EDGE_DOWN           ((int32_t)1)
    #define EDGE_UP             ((int32_t)0)
    
    #define FORWARD             0
    #define REVERSE             1
    
    // Change these values to switch which direction increase/decrease pan/tilt
    #define PAN_DIR             FORWARD
    #define TILT_DIR            FORWARD
    #define IRIS_DIR            REVERSE
    #define EDGE_DIR            REVERSE
    #define ZOOM_DIR            REVERSE
    #define THRUST_DIR          REVERSE
    #define ANGLE_DIR           REVERSE
    #define COLOR_DIR           REVERSE
    #define ROT_DIR             REVERSE
    #define DIFF_DIR            REVERSE
    
    // Use these values to make the encoder more coarse or fine. This controls
    // the number of wheel "ticks" the device sends to Eos for each tick of the
    // encoder. 1 is the default and the most fine setting. Must be an integer.
    #define PAN_SCALE           3
    #define TILT_SCALE          3
    
    #define OSC_BUF_MAX_SIZE    512
    
    const String HANDSHAKE_QUERY = "ETCOSC?";
    const String HANDSHAKE_REPLY = "OK";
    
    //See displayScreen() below - limited to 10 chars (after 6 prefix chars)
    const String VERSION_STRING = "1.0.0.6";
    
    // Change these values to alter how long we wait before sending an OSC ping
    // to see if Eos is still there, and then finally how long before we
    // disconnect and show the splash screen
    // Values are in milliseconds
    #define PING_AFTER_IDLE_INTERVAL    5000
    #define TIMEOUT_AFTER_IDLE_INTERVAL 10000
    
    /*******************************************************************************
       Local Types
     ******************************************************************************/
    enum WHEEL_TYPE { TILT, PAN, CYAN, MAGENTA, YELLOW, COLORWHEEL, HUE, SATURATION, ANGLEA, THRUSTA, ANGLEB, THRUSTB, ANGLEC, THRUSTC, ANGLED, THRUSTD,
                      IRIS, EDGE, ZOOM, DIFFUSION, FRAMEROT, CTO, PRISM, ANIMATION, GOBO, STROBE
                    };
    enum WHEEL_MODE { COARSE, FINE };
    
    
    struct Encoder
    {
      uint8_t pinA;
      uint8_t pinB;
      int pinAPrevious;
      int pinBPrevious;
      float pos;
      uint8_t direction;
    };
    struct Encoder panWheel;
    struct Encoder tiltWheel;
    
    struct Encoder cyanWheel;
    struct Encoder magentaWheel;
    struct Encoder yellowWheel;
    struct Encoder colorWheel;
    struct Encoder ctoWheel;
    struct Encoder hueWheel;
    struct Encoder saturationWheel;
    
    struct Encoder angleaWheel;
    struct Encoder thrustaWheel;
    struct Encoder anglebWheel;
    struct Encoder thrustbWheel;
    struct Encoder anglecWheel;
    struct Encoder thrustcWheel;
    struct Encoder angledWheel;
    struct Encoder thrustdWheel;
    struct Encoder framerotWheel;
    
    struct Encoder irisWheel;
    struct Encoder edgeWheel;
    struct Encoder zoomWheel;
    struct Encoder diffusionWheel;
    
    struct Encoder gobospdWheel;
    struct Encoder animationWheel;
    struct Encoder prismWheel;
    struct Encoder strobeWheel;
    
    Bounce  bouncer1  = Bounce();
    Bounce  bouncer2  = Bounce();
    Bounce  bouncer3  = Bounce();
    Bounce  bouncer4  = Bounce();
    Bounce  bouncer5  = Bounce();
    Bounce  bouncer6  = Bounce();
    Bounce  bouncer7  = Bounce();
    Bounce  bouncer8  = Bounce();
    Bounce  bouncer9  = Bounce();
    
    /*******************************************************************************
       Global Variables
     ******************************************************************************/
    int ENC_MODE = 1;
    int JOYSTICK_MODE = 0;
    int32_t val;
    String par1name;
    double par1val;
    String par2name;
    double par2val;
    String par3name;
    float par3val;
    String par4name;
    float par4val;
    String par5name;
    float par5val;
    String par6name;
    float par6val;
    String par7name;
    float par7val;
    String par8name;
    float par8val;
    
    float gobomode;
    float animationmode;
    
    int32_t panMotion;
    int32_t tiltMotion;
    int32_t framerotMotion;
    int32_t ctoMotion;
    int32_t irisMotion;
    int32_t edgeMotion;
    int32_t zoomMotion;
    int32_t diffusionMotion;
    
    
    int32_t thrustaMotion;
    int32_t angleaMotion;
    int32_t thrustbMotion;
    int32_t anglebMotion;
    int32_t thrustcMotion;
    int32_t anglecMotion;
    int32_t thrustdMotion;
    int32_t angledMotion;
    
    int32_t cyanMotion;
    int32_t magentaMotion;
    int32_t yellowMotion;
    int32_t colorMotion;
    int32_t hueMotion;
    int32_t saturationMotion;
    
    int32_t gobospdMotion;
    int32_t animationMotion;
    int32_t prismMotion;
    int32_t strobeMotion;
    
    
    // initialize the library with the numbers of the interface pins
    
    // LiquidCrystal lcd(12, 11, 10, 9, 5, 4);
    
    
    LiquidCrystalFast lcd(12, 11, 10, 9, 5, 4, 3, 2);     // LCD pins: RS  RW  EN1 EN2 D4 D5 D6 D7
    
    bool updateDisplay = false;
    bool connectedToEos = false;
    unsigned long lastMessageRxTime = 0;
    bool timeoutPingSent = false;
    
    
    
    /*******************************************************************************
       Local Functions
     ******************************************************************************/
    
    /*******************************************************************************
       Issues all our subscribes to Eos. When subscribed, Eos will keep us updated
       with the latest values for a given parameter.
    
       Parameters:  none
    
       Return Value: void
    
     ******************************************************************************/
    void issueSubscribes()
    {
      // Add a filter so we don't get spammed with unwanted OSC messages from Eos
      OSCMessage filter("/eos/filter/add");
      filter.add("/eos/out/param/*");
      filter.add("/eos/out/ping");
      SLIPSerial.beginPacket();
      filter.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      // subscribe to Eos pan & tilt updates
      OSCMessage subPan("/eos/subscribe/param/pan");
      subPan.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subPan.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subTilt("/eos/subscribe/param/tilt");
      subTilt.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subTilt.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subCyan("/eos/subscribe/param/cyan");
      subCyan.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subCyan.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subMagenta("/eos/subscribe/param/magenta");
      subMagenta.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subMagenta.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subYellow("/eos/subscribe/param/yellow");
      subYellow.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subYellow.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subColorwheel("/eos/subscribe/param/color select");
      subColorwheel.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subColorwheel.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subHue("/eos/subscribe/param/hue");
      subHue.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subHue.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subSaturation("/eos/subscribe/param/saturatn");
      subSaturation.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subSaturation.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subThrustA("/eos/subscribe/param/thrust a");
      subThrustA.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subThrustA.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subAngleA("/eos/subscribe/param/angle a");
      subAngleA.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subAngleA.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subThrustB("/eos/subscribe/param/thrust b");
      subThrustB.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subThrustB.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subAngleB("/eos/subscribe/param/angle b");
      subAngleB.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subAngleB.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subThrustC("/eos/subscribe/param/thrust c");
      subThrustC.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subThrustC.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subAngleC("/eos/subscribe/param/angle c");
      subAngleC.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subAngleC.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subThrustD("/eos/subscribe/param/thrust d");
      subThrustD.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subThrustD.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subAngleD("/eos/subscribe/param/angle d");
      subAngleD.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subAngleD.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subFrameRot("/eos/subscribe/param/frame assembly");
      subFrameRot.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subFrameRot.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subIris("/eos/subscribe/param/iris");
      subIris.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subIris.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subEdge("/eos/subscribe/param/edge");
      subEdge.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subEdge.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subZoom("/eos/subscribe/param/zoom");
      subZoom.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subZoom.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subCTO("/eos/subscribe/param/cto");
      subCTO.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subCTO.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subDiffusion("/eos/subscribe/param/diffusn");
      subDiffusion.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subDiffusion.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subGoboSpd("/eos/subscribe/param/gobo ind\\spd");
      subGoboSpd.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subGoboSpd.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subAnimationSpd("/eos/subscribe/param/animation select");
      subAnimationSpd.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subAnimationSpd.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subPrism("/eos/subscribe/param/beam fx ind\\spd");
      subPrism.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subPrism.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subStrobe("/eos/subscribe/param/shutter strobe");
      subStrobe.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subStrobe.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subGoboMode("/eos/subscribe/param/gobo_mode");
      subGoboMode.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subGoboMode.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subAnimationMode("/eos/subscribe/param/animation_wheel_mode");
      subAnimationMode.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subAnimationMode.send(SLIPSerial);
      SLIPSerial.endPacket();
    
    }
    
    /*******************************************************************************
       Given a valid OSCMessage (relevant to Pan/Tilt), we update our Encoder struct
       with the new position information.
    
       Parameters:
        msg - The OSC message we will use to update our internal data
        addressOffset - Unused (allows for multiple nested roots)
    
       Return Value: void
    
     ******************************************************************************/
    void parsePanUpdate(OSCMessage& msg, int addressOffset)
    {
      panWheel.pos = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    
    void parseTiltUpdate(OSCMessage& msg, int addressOffset)
    {
      tiltWheel.pos = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    
    void parseCyanUpdate(OSCMessage& msg, int addressOffset)
    {
      cyanWheel.pos = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    
    void parseMagentaUpdate(OSCMessage& msg, int addressOffset)
    {
      magentaWheel.pos = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    
    void parseYellowUpdate(OSCMessage& msg, int addressOffset)
    {
      yellowWheel.pos = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    
    void parseColorUpdate(OSCMessage& msg, int addressOffset)
    {
      colorWheel.pos = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    
    void parseHueUpdate(OSCMessage& msg, int addressOffset)
    {
      hueWheel.pos = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    
    void parseSaturationUpdate(OSCMessage& msg, int addressOffset)
    {
      saturationWheel.pos = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    
    
    void parseAngleAUpdate(OSCMessage& msg, int addressOffset)
    {
      angleaWheel.pos = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    
    void parseThrustAUpdate(OSCMessage& msg, int addressOffset)
    {
      thrustaWheel.pos = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    
    void parseAngleBUpdate(OSCMessage& msg, int addressOffset)
    {
      anglebWheel.pos = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    
    void parseThrustBUpdate(OSCMessage& msg, int addressOffset)
    {
      thrustbWheel.pos = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    void parseAngleCUpdate(OSCMessage& msg, int addressOffset)
    {
      anglecWheel.pos = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    
    void parseThrustCUpdate(OSCMessage& msg, int addressOffset)
    {
      thrustcWheel.pos = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    void parseAngleDUpdate(OSCMessage& msg, int addressOffset)
    {
      angledWheel.pos = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    
    void parseThrustDUpdate(OSCMessage& msg, int addressOffset)
    {
      thrustdWheel.pos = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    
    void parseFrameRotUpdate(OSCMessage& msg, int addressOffset)
    {
      framerotWheel.pos = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    
    void parseIrisUpdate(OSCMessage& msg, int addressOffset)
    {
      irisWheel.pos = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    
    void parseEdgeUpdate(OSCMessage& msg, int addressOffset)
    {
      edgeWheel.pos = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    
    void parseZoomUpdate(OSCMessage& msg, int addressOffset)
    {
      zoomWheel.pos = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    
    void parseDiffusionUpdate(OSCMessage& msg, int addressOffset)
    {
      diffusionWheel.pos = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    
    void parseCTOUpdate(OSCMessage& msg, int addressOffset)
    {
      ctoWheel.pos = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    
    void parseGoboSpdUpdate(OSCMessage& msg, int addressOffset)
    {
      gobospdWheel.pos = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    
    void parseAnimationUpdate(OSCMessage& msg, int addressOffset)
    {
      animationWheel.pos = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    
    void parsePrismUpdate(OSCMessage& msg, int addressOffset)
    {
      prismWheel.pos = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    
    void parseStrobeUpdate(OSCMessage& msg, int addressOffset)
    {
      strobeWheel.pos = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    
    void parseGoboModeUpdate(OSCMessage& msg, int addressOffset)
    {
      gobomode = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    
    void parseAnimationModeUpdate(OSCMessage& msg, int addressOffset)
    {
      animationmode = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    
    
    /*******************************************************************************
       Given an unknown OSC message we check to see if it's a handshake message.
       If it's a handshake we issue a subscribe, otherwise we begin route the OSC
       message to the appropriate function.
    
       Parameters:
        msg - The OSC message of unknown importance
    
       Return Value: void
    
     ******************************************************************************/
    void parseOSCMessage(String& msg)
    {
      // check to see if this is the handshake string
      if (msg.indexOf(HANDSHAKE_QUERY) != -1)
      {
        // handshake string found!
        SLIPSerial.beginPacket();
        SLIPSerial.write((const uint8_t*)HANDSHAKE_REPLY.c_str(), (size_t)HANDSHAKE_REPLY.length());
        SLIPSerial.endPacket();
    
        // Let Eos know we want updates on some things
        issueSubscribes();
    
        // Make our splash screen go away
        connectedToEos = true;
        updateDisplay = true;
      }
      else
      {
        // prepare the message for routing by filling an OSCMessage object with our message string
        OSCMessage oscmsg;
        oscmsg.fill((uint8_t*)msg.c_str(), (int)msg.length());
    
        // route pan/tilt messages to the relevant update function
        oscmsg.route("/eos/out/param/pan", parsePanUpdate);
        oscmsg.route("/eos/out/param/tilt", parseTiltUpdate);
    
        // route color messages to the relevant update function
        oscmsg.route("/eos/out/param/cyan", parseCyanUpdate);
        oscmsg.route("/eos/out/param/magenta", parseMagentaUpdate);
        oscmsg.route("/eos/out/param/yellow", parseYellowUpdate);
        oscmsg.route("/eos/out/param/color select", parseColorUpdate);
        oscmsg.route("/eos/out/param/hue", parseHueUpdate);
        oscmsg.route("/eos/out/param/saturatn", parseSaturationUpdate);
        // route shutter messages to the relevant update function
        oscmsg.route("/eos/out/param/thrust a", parseThrustAUpdate);
        oscmsg.route("/eos/out/param/angle a", parseAngleAUpdate);
        oscmsg.route("/eos/out/param/thrust b", parseThrustBUpdate);
        oscmsg.route("/eos/out/param/angle b", parseAngleBUpdate);
        oscmsg.route("/eos/out/param/thrust c", parseThrustCUpdate);
        oscmsg.route("/eos/out/param/angle c", parseAngleCUpdate);
        oscmsg.route("/eos/out/param/thrust d", parseThrustDUpdate);
        oscmsg.route("/eos/out/param/angle d", parseAngleDUpdate);
        oscmsg.route("/eos/out/param/frame assembly", parseFrameRotUpdate);
        // route iris/edge/zoom/diffusion/cto messages to the relevant update function
        oscmsg.route("/eos/out/param/iris", parseIrisUpdate);
        oscmsg.route("/eos/out/param/edge", parseEdgeUpdate);
        oscmsg.route("/eos/out/param/zoom", parseZoomUpdate);
        oscmsg.route("/eos/out/param/diffusn", parseDiffusionUpdate);
        oscmsg.route("/eos/out/param/cto", parseCTOUpdate);
        // route rot/beam/anmation/strobe messages to the relevant update function
        oscmsg.route("/eos/out/param/gobo ind/spd", parseGoboSpdUpdate);
        oscmsg.route("/eos/out/param/animation select", parseAnimationUpdate);
        oscmsg.route("/eos/out/param/beam fx ind/spd", parsePrismUpdate);
        oscmsg.route("/eos/out/param/shutter strobe", parseStrobeUpdate);
        oscmsg.route("/eos/out/param/gobo_mode", parseGoboModeUpdate);
        oscmsg.route("/eos/out/param/animation_wheel_mode", parseAnimationModeUpdate);
    
      }
    }
    
    /*******************************************************************************
       Updates the display with the latest parameters
    
       Parameters:  none
    
       Return Value: void
    
     ******************************************************************************/
    void displayStatus()
    {
      lcd.clear();
    
      if (!connectedToEos)
      {
        // display a splash message before the Eos connection is open
        lcd.setCursor(0, 0);
        lcd.print(String("Box8 v" + VERSION_STRING).c_str());
        lcd.setCursor(0, 1);
        lcd.print("waiting for element");
        lcd.setCursor(20, 0);
        lcd.print("Theater an der Uni");
        lcd.setCursor(28, 1);
        lcd.print("Regensburg");
        lcd.setCursor(0, 2);
        lcd.print("Programm/Elektronik: Orlando");
        lcd.setCursor(0, 3);
        lcd.print("Gehaeuse: Daniel");
      }
      else
      {
        switch (ENC_MODE)
        {
          case 1:
            par1name = "Pan";
            par1val = panWheel.pos;
            par2name = "Tilt";
            par2val = tiltWheel.pos;
            par3name = "FrameRtn";
            par3val = framerotWheel.pos;
            par4name = "";
            par4val = 9999;
            par5name = "Iris";
            par5val = irisWheel.pos;
            par6name = "Edge";
            par6val = edgeWheel.pos;
            par7name = "Zoom";
            par7val = zoomWheel.pos;
            par8name = "Diffusion";
            par8val = diffusionWheel.pos;
            break;
    
          case 2:
            par1name = "Thrust D";
            par1val = thrustdWheel.pos;
            par2name = "Angle D";
            par2val = angledWheel.pos;
            par3name = "Thrust B";
            par3val = thrustbWheel.pos;
            par4name = "Angle B";
            par4val = anglebWheel.pos;
            par5name = "Thrust A";
            par5val = thrustaWheel.pos;
            par6name = "Angle A";
            par6val = angleaWheel.pos;
            par7name = "Thrust C";
            par7val = thrustcWheel.pos;
            par8name = "Angle C";
            par8val = anglecWheel.pos;
            break;
    
          case 3:
            par1name = "Cyan";
            par1val = cyanWheel.pos;
            par2name = "Magenta";
            par2val = magentaWheel.pos;
            par3name = "Yellow";
            par3val = yellowWheel.pos;
            par4name = "Colorwheel";
            par4val = colorWheel.pos;
            par5name = "Hue";
            par5val = hueWheel.pos;
            par6name = "Sat";
            par6val = saturationWheel.pos;
            par7name = "CTO";
            par7val = ctoWheel.pos;
            par8name = "";
            par8val = 9999;
            break;
    
          case 4:
            par1name = "GobIndSpd";
            par1val = gobospdWheel.pos;
            par2name = "Animation";
            par2val = animationWheel.pos;
            par3name = "BeamFXSpd";
            par3val = prismWheel.pos;
            par4name = "Strobe";
            par4val = strobeWheel.pos;
            par5name = "GoboRot";
            par5val = 8888;
            par6name = "AnimatnRt";
            par6val = 8888;
            par7name = "";
            par7val = 9999;
            par8name = "";
            par8val = 9999;
            break;
        }
    
        // put the cursor at the begining of the first line
        lcd.setCursor(0, 0);
        lcd.print(par1name);
        // put the cursor at the begining of the second line
        lcd.setCursor(0, 1);
        if (par1val != 9999)
          lcd.print(par1val);
    
        // put the cursor at Pos 10 of the first line
        lcd.setCursor(10, 0);
        lcd.print(par2name);
        // put the cursor at Pos 10 of the second line
        lcd.setCursor(10, 1);
        if (par2val != 9999)
          lcd.print(par2val);
    
        // put the cursor at Pos 20 of the first line
        lcd.setCursor(20, 0);
        lcd.print(par3name);
        // put the cursor at Pos 20 of the second line
        lcd.setCursor(20, 1);
        if (par3val != 9999)
          lcd.print(par3val);
    
        // put the cursor at Pos 30 of the first line
        lcd.setCursor(30, 0);
        lcd.print(par4name);
        // put the cursor at Pos 30 of the second line
        lcd.setCursor(30, 1);
        if (par4val != 9999)
          lcd.print(par4val);
    
        // put the cursor at Pos 0 of the third line
        lcd.setCursor(0, 2);
        lcd.print(par5name);
        // put the cursor at Pos 0 of the fourth line
        lcd.setCursor(0, 3);
        if (par5val != 9999)
        {
          if  (par5val == 8888)
          {
            if (gobomode >= 0 && gobomode <= 15) lcd.print("Index");
            else if (gobomode >= 16 && gobomode <= 31) lcd.print("Rot +");
            else if (gobomode >= 32 && gobomode <= 47) lcd.print("Rot -");
            else if (gobomode >= 48 && gobomode <= 63) lcd.print("Animate +");
            else if (gobomode >= 64 && gobomode <= 79) lcd.print("Animate -");
          }
          else lcd.print(par5val);
        };
    
        // put the cursor at Pos 10 of the third line
        lcd.setCursor(10, 2);
        lcd.print(par6name);
        // put the cursor at Pos 10 of the fourth line
        lcd.setCursor(10, 3);
        if (par6val != 9999)
        {
          if   (par6val == 8888)
          {
            if (animationmode >= 0 && animationmode <= 50) lcd.print("Open");
            else if (animationmode >= 51 && animationmode <= 101) lcd.print("Rot +");
            else if (animationmode >= 102 && animationmode <= 152) lcd.print("Rot -");
            else if (animationmode >= 153 && animationmode <= 203) lcd.print("StRo -");
            else if (animationmode >= 204 && animationmode <= 255) lcd.print("StRo +");
          }
          else lcd.print(par6val);
        };
    
        // put the cursor at Pos 20 of the third line
        lcd.setCursor(20, 2);
        lcd.print(par7name);
        // put the cursor at Pos 20 of the fourth line
        lcd.setCursor(20, 3);
        if (par7val != 9999)
          lcd.print(par7val);
    
        // put the cursor at Pos 30 of the third line
        lcd.setCursor(30, 2);
        lcd.print(par8name);
        // put the cursor at Pos 30 of the fourth line
        lcd.setCursor(30, 3);
        if (par8val != 9999)
          lcd.print(par8val);
    
    
        if (JOYSTICK_MODE == 1)
        {
          //put the cursor at Pos 39 of 2nd line
          lcd.setCursor(39, 1);
          lcd.print("J");
        }
    
    
    
    
      }
    
      updateDisplay = false;
    }
    
    /*******************************************************************************
       Initializes a given encoder struct to the requested parameters.
    
       Parameters:
        encoder - Pointer to the encoder we will be initializing
        pinA - Where the A pin is connected to the Arduino
        pinB - Where the B pin is connected to the Arduino
        direction - Determines if clockwise or counterclockwise is "forward"
    
       Return Value: void
    
     ******************************************************************************/
    void initEncoder(struct Encoder* encoder, uint8_t pinA, uint8_t pinB, uint8_t direction)
    {
    
      encoder->pinA = pinA;
      encoder->pinB = pinB;
      encoder->pos = 0;
      encoder->direction = direction;
    
      pinMode(pinA, INPUT_PULLUP);
      pinMode(pinB, INPUT_PULLUP);
    
      encoder->pinAPrevious = digitalRead(pinA);
      encoder->pinBPrevious = digitalRead(pinB);
    }
    
    /*******************************************************************************
       Checks if the encoder has moved by comparing the previous state of the pins
       with the current state. If they are different, we know there is movement.
       In the event of movement we update the current state of our pins.
    
       Parameters:
        encoder - Pointer to the encoder we will be checking for motion
    
       Return Value:
        encoderMotion - Returns the 0 if the encoder has not moved
                                    1 for forward motion
                                   -1 for reverse motion
    
     ******************************************************************************/
    int8_t updateEncoder(struct Encoder* encoder)
    {
      int8_t encoderMotion = 0;
      int pinACurrent = digitalRead(encoder->pinA);
      int pinBCurrent = digitalRead(encoder->pinB);
    
      // has the encoder moved at all?
      if (encoder->pinAPrevious != pinACurrent)
      {
        // Since it has moved, we must determine if the encoder has moved forwards or backwards
        encoderMotion = (encoder->pinAPrevious == encoder->pinBPrevious) ? -1 : 1;
    
        // If we are in reverse mode, flip the direction of the encoder motion
        if (encoder->direction == REVERSE)
          encoderMotion = -encoderMotion;
      }
      encoder->pinAPrevious = pinACurrent;
      encoder->pinBPrevious = pinBCurrent;
    
      return encoderMotion;
    }
    
    /*******************************************************************************
       Checks if the Joystick has moved and how far.
    
       Parameters:
        dir
    
       Return Value:
        joystickMotion
    
     ******************************************************************************/
    
    int32_t updateJoystick(String dir)
    {
      if (dir == "TILT")
        val = (512 - analogRead(XPIN)) / 100;
      else if (dir == "PAN")
        val = (analogRead(YPIN) - 512) / 100;
    
      if (digitalRead(JOYSTICK_BTN) == LOW) val *= 5;
      return val;
    }
    
    
    
    /*******************************************************************************
       Sends a message to Eos informing them of a wheel movement.
    
       Parameters:
        type - the type of wheel that's moving (i.e. pan or tilt)
        ticks - the direction and intensity of the movement
    
       Return Value: void
    
     ******************************************************************************/
    void sendWheelMove(WHEEL_TYPE type, float ticks)
    {
      String wheelMsg("/eos/wheel");
    
      if (type == PAN)
      {
        if (digitalRead(SHIFT_BTN) == LOW || JOYSTICK_MODE == 1)
          wheelMsg.concat("/fine");
        else
          wheelMsg.concat("/coarse");
        wheelMsg.concat("/pan");
      }
      else if (type == TILT)
      {
        if (digitalRead(SHIFT_BTN) == LOW || JOYSTICK_MODE == 1)
          wheelMsg.concat("/fine");
        else
          wheelMsg.concat("/coarse");
        wheelMsg.concat("/tilt");
      }
    
      else if (type == THRUSTA)
      {
        wheelMsg.concat("/thrust a");
      }
      else if (type == ANGLEA)
      {
        wheelMsg.concat("/angle a");
      }
      else if (type == THRUSTB)
      {
        wheelMsg.concat("/thrust b");
      }
      else if (type == ANGLEB)
      {
        wheelMsg.concat("/angle b");
      }
      else if (type == THRUSTC)
      {
        wheelMsg.concat("/thrust c");
      }
      else if (type == ANGLEC)
      {
        wheelMsg.concat("/angle c");
      }
      else if (type == THRUSTD)
      {
        wheelMsg.concat("/thrust d");
      }
      else if (type == ANGLED)
      {
        wheelMsg.concat("/angle d");
      }
    
      else if (type == IRIS)
      {
        wheelMsg.concat("/iris");
      }
      else if (type == EDGE)
      {
        wheelMsg.concat("/edge");
      }
      else if (type == ZOOM)
      {
        wheelMsg.concat("/zoom");
      }
      else if (type == DIFFUSION)
      {
        wheelMsg.concat("/diffusn");
      }
    
      else if (type == CYAN)
      {
        wheelMsg.concat("/cyan");
      }
      else if (type == MAGENTA)
      {
        wheelMsg.concat("/magenta");
      }
      else if (type == YELLOW)
      {
        wheelMsg.concat("/yellow");
      }
      else if (type == COLORWHEEL)
      {
        wheelMsg.concat("/color select");
      }
      else if (type == HUE)
      {
        wheelMsg.concat("/hue");
      }
      else if (type == SATURATION)
      {
        wheelMsg.concat("/saturatn");
      }
      else if (type == CTO)
      {
        wheelMsg.concat("/cto");
      }
      else if (type == PRISM)
      {
        wheelMsg.concat("/beam fx ind\\spd");
      }
      else if (type == ANIMATION)
      {
        wheelMsg.concat("/animation select");
      }
      else if (type == STROBE)
      {
        wheelMsg.concat("/shutter strobe");
      }
      else if (type == GOBO)
      {
        wheelMsg.concat("/gobo ind\\spd");
      }
      else if (type == FRAMEROT)
      {
        wheelMsg.concat("/frame assembly");
      }
      else
        // something has gone very wrong
        return;
    
      OSCMessage wheelUpdate(wheelMsg.c_str());
      wheelUpdate.add(ticks);
      SLIPSerial.beginPacket();
      wheelUpdate.send(SLIPSerial);
      SLIPSerial.endPacket();
    }
    
    /*******************************************************************************
       Sends a message to Eos informing them of a key press.
    
       Parameters:
        down - whether a key has been pushed down (true) or released (false)
        key - the key that has moved
    
       Return Value: void
    
     ******************************************************************************/
    void sendKeyPress(bool down, String key)
    {
      key = "/eos/key/" + key;
      OSCMessage keyMsg(key.c_str());
    
      if (down)
        keyMsg.add(EDGE_DOWN);
      else
        keyMsg.add(EDGE_UP);
    
      SLIPSerial.beginPacket();
      keyMsg.send(SLIPSerial);
      SLIPSerial.endPacket();
      keyMsg.empty();
    }
    /*******************************************************************************
       Sends a message to Eos informing them of a parameter change
    
       Parameters:
        key - the parameter that has changed
    
       Return Value: void
    
     ******************************************************************************/
    void sendParameter(String key, float key2)
    {
      key = "/eos/param/" + key;
      OSCMessage keyMsg(key.c_str());
      if (key2 != 9999 ) keyMsg.add(key2);
      SLIPSerial.beginPacket();
      keyMsg.send(SLIPSerial);
      SLIPSerial.endPacket();
      keyMsg.empty();
    }
    /*******************************************************************************
       Sends a message to Eos informing them of a channel change
    
       Parameters:
        key - the channel that has changed
    
       Return Value: void
    
     ******************************************************************************/
    void sendChannel(float key2)
    {
      OSCMessage keyMsg("/eos/chan");
      keyMsg.add(key2);
      SLIPSerial.beginPacket();
      keyMsg.send(SLIPSerial);
      SLIPSerial.endPacket();
      keyMsg.empty();
    }
    
    /*******************************************************************************
       Checks the status of all the buttons
    
       NOTE: This does not check the shift key. The shift key is used in tandem with
       the encoder to determine coarse/fine mode and thus does not report to Eos
       directly.
    
       Parameters: none
    
       Return Value: ENC_MODE
    
     ******************************************************************************/
    int checkButtons()
    {
      static int lastKeyState = HIGH;
      static int joystickenableKeyState = HIGH;
      static int focusKeyState = HIGH;
      static int shutterKeyState = HIGH;
      static int colorKeyState = HIGH;
      static int imageKeyState = HIGH;
      static int effektoffKeyState = HIGH;
      static int ch91KeyState = HIGH;
      static int ch92KeyState = HIGH;
      static int enc1KeyState = HIGH;
      static int enc2KeyState = HIGH;
      static int enc3KeyState = HIGH;
      static int enc4KeyState = HIGH;
      static int enc5KeyState = HIGH;
      static int enc6KeyState = HIGH;
      static int enc7KeyState = HIGH;
      static int enc8KeyState = HIGH;
    
      // Has the button state changed
      if (bouncer2.read() != lastKeyState)
      {
        // Notify Eos of this key press
        if (lastKeyState == LOW)
        {
          //sendKeyPress(false, "NEXT");
          lastKeyState = HIGH;
          updateDisplay = true;
        }
        else
        {
          //  sendKeyPress(true, "NEXT");
          lastKeyState = LOW;
        }
      }
    
      if (bouncer1.read() != joystickenableKeyState)
      {
        if (joystickenableKeyState == LOW)
        {
          if (JOYSTICK_MODE == 0)
          {
            lcd.clear();
            while (digitalRead(JOYSTICK_BTN) == HIGH)
            {
              lcd.setCursor(0, 1);
              lcd.print("Press Joystick Trigger to activate");
              if (digitalRead(JOY_ENABLE_BTN) == LOW) break;
            }
            JOYSTICK_MODE = 1;
          }
          else JOYSTICK_MODE = 0;
          joystickenableKeyState = HIGH;
          updateDisplay = true;
        }
        else
        {
          joystickenableKeyState = LOW;
        }
      }
    
      if (bouncer3.read() != focusKeyState)
      {
        if (focusKeyState == LOW)
        {
          ENC_MODE = 1;
          focusKeyState = HIGH;
          updateDisplay = true;
        }
        else
        {
          // ENC_MODE = 1;
          focusKeyState = LOW;
        }
      }
    
      if (bouncer4.read() != shutterKeyState)
      {
        if (shutterKeyState == LOW)
        {
          ENC_MODE = 2;
          shutterKeyState = HIGH;
          updateDisplay = true;
        }
        else
        {
          // ENC_MODE = 2;
          shutterKeyState = LOW;
        }
      }
    
      if (bouncer5.read() != colorKeyState)
      {
        if (colorKeyState == LOW)
        {
          ENC_MODE = 3;
          colorKeyState = HIGH;
          updateDisplay = true;
        }
        else
        {
          //  ENC_MODE = 3;
          colorKeyState = LOW;
        }
      }
    
      if (bouncer6.read() != imageKeyState)
      {
        if (imageKeyState == LOW)
        {
          ENC_MODE = 4;
          imageKeyState = HIGH;
          updateDisplay = true;
        }
        else
        {
          // ENC_MODE = 4;
          imageKeyState = LOW;
        }
      }
    
      if (bouncer7.read() != effektoffKeyState)
      {
        if (effektoffKeyState == LOW)
        {
          sendParameter("shutter strobe/home", 9999);
          sendParameter("gobo ind\\spd/home", 9999);
          sendParameter("beam fx select/home", 9999);
          sendParameter("animation select/home", 9999);
          sendParameter("gobo_select/home", 9999);
          sendParameter("gobo_select_2/home", 9999);
          sendParameter("gobo_mode/home", 9999);
          sendParameter("animation_wheel_mode/home", 9999);
    
          updateDisplay = true;
          effektoffKeyState = HIGH;
        }
        else
        {
          effektoffKeyState = LOW;
        }
      }
    
      if (bouncer8.read() != ch91KeyState)
      {
        if (ch91KeyState == LOW)
        {
          sendChannel(91);
          updateDisplay = true;
          ch91KeyState = HIGH;
    
        }
        else
        {
          ch91KeyState = LOW;
        }
      }
    
      if (bouncer9.read() != ch92KeyState)
      {
        if (ch92KeyState == LOW)
        {
          sendChannel(92);
          updateDisplay = true;
          ch92KeyState = HIGH;
    
        }
        else
        {
          //   sendKeyPress(true, "MACRO");
          ch92KeyState = LOW;
        }
      }
    
      if (digitalRead(ENC1_BTN) != enc1KeyState)
      {
        if (enc1KeyState == LOW)
        {
          if (ENC_MODE == 1)
          {
          }
          else if (ENC_MODE == 2)
          {
            sendParameter("thrust d/home", 9999);
          }
          else if (ENC_MODE == 3)
          {
            sendParameter("cyan/home", 9999);
    
          }
          else if (ENC_MODE == 4)
          {
            sendParameter("gobo ind\\spd/home", 9999);
    
          };
          updateDisplay = true;
          enc1KeyState = HIGH;
        }
        else
        {
          enc1KeyState = LOW;
        }
      }
    
      if (digitalRead(ENC2_BTN) != enc2KeyState)
      {
        if (enc2KeyState == LOW)
        {
          if (ENC_MODE == 1)
          {
    
          }
          else if (ENC_MODE == 2)
          {
            sendParameter("angle d/home", 9999);
    
          }
          else if (ENC_MODE == 3)
          {
            sendParameter("magenta/home", 9999);
    
          }
          else if (ENC_MODE == 4)
          {
            sendParameter("animation select/home", 9999);
          };
          updateDisplay = true;
          enc2KeyState = HIGH;
        }
        else
        {
          enc2KeyState = LOW;
        }
      }
      if (digitalRead(ENC3_BTN) != enc3KeyState)
      {
        if (enc3KeyState == LOW)
        {
          if (ENC_MODE == 1)
          {
            sendParameter("frame assembly/home", 9999);
    
          }
          else if (ENC_MODE == 2)
          {
            sendParameter("thrust b/home", 9999);
    
          }
          else if (ENC_MODE == 3)
          {
            sendParameter("yellow/home", 9999);
          }
          else if (ENC_MODE == 4)
          {
            sendParameter("beam fx ind\\spd/home", 9999);
    
          };
          updateDisplay = true;
          enc3KeyState = HIGH;
        }
        else
        {
          enc3KeyState = LOW;
        }
      }
      if (digitalRead(ENC4_BTN) != enc4KeyState)
      {
        if (enc4KeyState == LOW)
        {
          if (ENC_MODE == 1)
          {
            // sendParameter("cto/home", 9999);
    
          }
          else if (ENC_MODE == 2)
          {
            sendParameter("angle b/home", 9999);
    
          }
          else if (ENC_MODE == 3)
          {
            sendParameter("color select/home", 9999);
    
          }
          else if (ENC_MODE == 4)
          {
            sendParameter("shutter strobe/home", 9999);
          };
          updateDisplay = true;
          enc4KeyState = HIGH;
        }
        else
        {
          enc4KeyState = LOW;
        }
      }
      if (digitalRead(ENC5_BTN) != enc5KeyState)
      {
        if (enc5KeyState == LOW)
        {
          if (ENC_MODE == 1)
          {
            sendParameter("iris/home", 9999);
          }
          else if (ENC_MODE == 2)
          {
            sendParameter("thrust a/home", 9999);
          }
          else if (ENC_MODE == 3)
          {
          }
          else if (ENC_MODE == 4)
          {
            if (gobomode >= 0 && gobomode <= 15) sendParameter("gobo_mode", 24);
            else if (gobomode >= 16 && gobomode <= 31) sendParameter("gobo_mode", 40);
            else if (gobomode >= 32 && gobomode <= 47) sendParameter("gobo_mode", 56);
            else if (gobomode >= 48 && gobomode <= 63) sendParameter("gobo_mode", 72);
            else if (gobomode >= 64 && gobomode <= 79) sendParameter("gobo_mode", 7);
          };
          updateDisplay = true;
          enc5KeyState = HIGH;
        }
        else
        {
          enc5KeyState = LOW;
        }
      }
      if (digitalRead(ENC6_BTN) != enc6KeyState)
      {
        if (enc6KeyState == LOW)
        {
          if (ENC_MODE == 1)
          {
            sendParameter("edge/home", 9999);
          }
          else if (ENC_MODE == 2)
          {
            sendParameter("angle a/home", 9999);
    
          }
          else if (ENC_MODE == 3)
          {
          }
          else if (ENC_MODE == 4)
          {
            if (animationmode >= 0 && animationmode <= 50) sendParameter("animation_wheel_mode", 76);
            else  if (animationmode >= 51 && animationmode <= 101) sendParameter("animation_wheel_mode", 127);
            else if (animationmode >= 102 && animationmode <= 152) sendParameter("animation_wheel_mode", 178);
            else if (animationmode >= 153 && animationmode <= 203) sendParameter("animation_wheel_mode", 230);
            else if (animationmode >= 204 && animationmode <= 255) sendParameter("animation_wheel_mode", 25);
          };
          updateDisplay = true;
          enc6KeyState = HIGH;
        }
        else
        {
          enc6KeyState = LOW;
        }
      }
      if (digitalRead(ENC7_BTN) != enc7KeyState)
      {
        if (enc7KeyState == LOW)
        {
          if (ENC_MODE == 1)
          {
            sendParameter("zoom/home", 9999);
    
          }
          else if (ENC_MODE == 2)
          {
            sendParameter("thrust c/home", 9999);
    
          }
          else if (ENC_MODE == 3)
          {
            sendParameter("cto/home", 9999);
    
          }
          else if (ENC_MODE == 4)
          {
          };
          updateDisplay = true;
          enc7KeyState = HIGH;
        }
        else
        {
          enc7KeyState = LOW;
        }
      }
      if (digitalRead(ENC8_BTN) != enc8KeyState)
      {
        if (enc8KeyState == LOW)
        {
          if (ENC_MODE == 1)
          {
            sendParameter("diffusn/home", 9999);
    
          }
          else if (ENC_MODE == 2)
          {
            sendParameter("angle c/home", 9999);
    
          }
          else if (ENC_MODE == 3)
          {
          }
          else if (ENC_MODE == 4)
          {
          };
          updateDisplay = true;
          enc8KeyState = HIGH;
        }
        else
        {
          enc8KeyState = LOW;
        }
      }
      return ENC_MODE;
    }
    
    /*******************************************************************************
       Here we define our encoders per page.
       Depending on the Encoder Page, the different parameters are loaded onto the
       encoders
       Encoder 1 = A0, A1
       Encoder 2 = A2, A3
       Encoder 3 = A4, A5
       Encoder 4 = A6, A7
       Encoder 5 = A8, A9
       Encoder 6 = A20, A19
       Encoder 7 = A14, A15
       Encoder 8 = A16, A17
    
       Parameters: none
    
       Return Value: void
    
     ******************************************************************************/
    void initEncoderPage()
    {
      initEncoder(&panWheel, A0, A1, PAN_DIR);
      initEncoder(&tiltWheel, A2, A3, TILT_DIR);
      initEncoder(&framerotWheel, A4, A5, ROT_DIR);
      initEncoder(&irisWheel, A8, A9, IRIS_DIR);
      initEncoder(&edgeWheel, A20, A19, EDGE_DIR);
      initEncoder(&zoomWheel, A14, A15, ZOOM_DIR);
      initEncoder(&diffusionWheel, A16, A17, DIFF_DIR);
      initEncoder(&thrustdWheel, A0, A1, THRUST_DIR);
      initEncoder(&angledWheel, A2, A3, ANGLE_DIR);
      initEncoder(&thrustbWheel, A4, A5, THRUST_DIR);
      initEncoder(&anglebWheel, A6, A7, ANGLE_DIR);
      initEncoder(&thrustaWheel, A8, A9, THRUST_DIR);
      initEncoder(&angleaWheel, A20, A19, ANGLE_DIR);
      initEncoder(&thrustcWheel, A14, A15, THRUST_DIR);
      initEncoder(&anglecWheel, A16, A17, ANGLE_DIR);
      initEncoder(&cyanWheel, A0, A1, COLOR_DIR);
      initEncoder(&magentaWheel, A2, A3, COLOR_DIR);
      initEncoder(&yellowWheel, A4, A5, COLOR_DIR);
      initEncoder(&colorWheel, A6, A7, COLOR_DIR);
      initEncoder(&hueWheel, A8, A9, COLOR_DIR);
      initEncoder(&saturationWheel, A20, A19, COLOR_DIR);
      initEncoder(&ctoWheel, A14, A15, COLOR_DIR);
      initEncoder(&gobospdWheel, A0, A1, ROT_DIR);
      initEncoder(&animationWheel, A2, A3, ROT_DIR);
      initEncoder(&prismWheel, A4, A5, ROT_DIR);
      initEncoder(&strobeWheel, A6, A7, ROT_DIR);
    }
    
    
    /*******************************************************************************
       Here we setup our encoder, lcd, and various input devices. We also prepare
       to communicate OSC with Eos by setting up SLIPSerial. Once we are done with
       setup() we pass control over to loop() and never call setup() again.
    
       NOTE: This function is the entry function. This is where control over the
       Arduino is passed to us (the end user).
    
       Parameters: none
    
       Return Value: void
    
     ******************************************************************************/
    void setup()
    {
      lcd.begin(LCD_CHARS, LCD_LINES);
      lcd.clear();
      // display a splash message before the Eos connection is open
      lcd.setCursor(0, 0);
      lcd.print(String("Box8 v" + VERSION_STRING).c_str());
      lcd.setCursor(0, 1);
      lcd.print("waiting for element");
      lcd.setCursor(20, 0);
      lcd.print("Theater an der Uni");
      lcd.setCursor(28, 1);
      lcd.print("Regensburg");
      lcd.setCursor(0, 2);
      lcd.print("Programm/Elektronik: Orlando");
      lcd.setCursor(0, 3);
      lcd.print("Gehaeuse: Daniel");
    
    
      SLIPSerial.begin(115200);
      // This is a hack around an Arduino bug. It was taken from the OSC library
      //examples
    #ifdef BOARD_HAS_USB_SERIAL
      while (!SerialUSB);
    #else
      while (!Serial);
    #endif
    
    
    
      // This is necessary for reconnecting a device because it needs some time
      // for the serial port to open, but meanwhile the handshake message was
      // sent from Eos
      SLIPSerial.beginPacket();
      SLIPSerial.write((const uint8_t*)HANDSHAKE_REPLY.c_str(), (size_t)HANDSHAKE_REPLY.length());
      SLIPSerial.endPacket();
      // Let Eos know we want updates on some things
      issueSubscribes();
    
      pinMode(JOY_ENABLE_BTN, INPUT_PULLUP);
      bouncer1.attach( JOY_ENABLE_BTN );
      bouncer1.interval(5);
      pinMode(LAST_BTN, INPUT_PULLUP);
      bouncer2.attach( LAST_BTN );
      bouncer2.interval(5);
      pinMode(SHIFT_BTN, INPUT_PULLUP);
      pinMode(FOCUS_BTN, INPUT_PULLUP);
      bouncer3.attach( FOCUS_BTN );
      bouncer3.interval(5);
      pinMode(SHUTTER_BTN, INPUT_PULLUP);
      bouncer4.attach( SHUTTER_BTN );
      bouncer4.interval(5);
      pinMode(COLOR_BTN, INPUT_PULLUP);
      bouncer5.attach( COLOR_BTN );
      bouncer5.interval(5);
      pinMode(IMAGE_BTN, INPUT_PULLUP);
      bouncer6.attach( IMAGE_BTN );
      bouncer6.interval(5);
      pinMode(EFFEKTOFF_BTN, INPUT_PULLUP);
      bouncer7.attach( EFFEKTOFF_BTN );
      bouncer7.interval(5);
      pinMode(CH91_BTN, INPUT_PULLUP);
      bouncer8.attach( CH91_BTN );
      bouncer8.interval(5);
      pinMode(CH92_BTN, INPUT_PULLUP);
      bouncer9.attach( CH92_BTN );
      bouncer9.interval(5);
      pinMode(ENC1_BTN, INPUT_PULLUP);
      pinMode(ENC2_BTN, INPUT_PULLUP);
      pinMode(ENC3_BTN, INPUT_PULLUP);
      pinMode(ENC4_BTN, INPUT_PULLUP);
      pinMode(ENC5_BTN, INPUT_PULLUP);
      pinMode(ENC6_BTN, INPUT_PULLUP);
      pinMode(ENC7_BTN, INPUT_PULLUP);
      pinMode(ENC8_BTN, INPUT_PULLUP);
      pinMode(JOYSTICK_BTN, INPUT_PULLUP);
    
    
      initEncoderPage();
      displayStatus();
    }
    
    /*******************************************************************************
       Here we service, monitor, and otherwise control all our peripheral devices.
       First, we retrieve the status of our encoders and buttons and update Eos.
       Next, we check if there are any OSC messages for us.
       Finally, we update our display (if an update is necessary)
    
       NOTE: This function is our main loop and thus this function will be called
       repeatedly forever
    
       Parameters: none
    
       Return Value: void
    
     ******************************************************************************/
    void loop()
    {
    
      static String curMsg;
      int size;
      // get the updated state of each encoder
    
      switch (ENC_MODE)
      {
        case 1:
          if (JOYSTICK_MODE == 1) panMotion = updateJoystick("PAN");
          else panMotion = updateEncoder(&panWheel);
          if (JOYSTICK_MODE == 1) tiltMotion = updateJoystick("TILT");
          else tiltMotion = updateEncoder(&tiltWheel);
          framerotMotion = updateEncoder(&framerotWheel);
          irisMotion = updateEncoder(&irisWheel);
          edgeMotion = updateEncoder(&edgeWheel);
          zoomMotion = updateEncoder(&zoomWheel);
          diffusionMotion = updateEncoder(&diffusionWheel);
          // Scale the result by a scaling factor
          if (JOYSTICK_MODE == 0) panMotion *= PAN_SCALE;
          if (JOYSTICK_MODE == 0)  tiltMotion *= TILT_SCALE;
          break;
    
        case 2: //else if (ENC_MODE == 2)
          if (JOYSTICK_MODE == 1) panMotion = updateJoystick("PAN");
          if (JOYSTICK_MODE == 1) tiltMotion = updateJoystick("TILT");
          thrustaMotion = updateEncoder(&thrustaWheel);
          angleaMotion = updateEncoder(&angleaWheel);
          thrustbMotion = updateEncoder(&thrustbWheel);
          anglebMotion = updateEncoder(&anglebWheel);
          thrustcMotion = updateEncoder(&thrustcWheel);
          anglecMotion = updateEncoder(&anglecWheel);
          thrustdMotion = updateEncoder(&thrustdWheel);
          angledMotion = updateEncoder(&angledWheel);
          break;
    
        case 3: //      else if (ENC_MODE == 3)
          if (JOYSTICK_MODE == 1) panMotion = updateJoystick("PAN");
          if (JOYSTICK_MODE == 1) tiltMotion = updateJoystick("TILT");
          cyanMotion = updateEncoder(&cyanWheel);
          magentaMotion = updateEncoder(&magentaWheel);
          yellowMotion = updateEncoder(&yellowWheel);
          colorMotion = updateEncoder(&colorWheel);
          hueMotion = updateEncoder(&hueWheel);
          saturationMotion = updateEncoder(&saturationWheel);
          ctoMotion = updateEncoder(&ctoWheel);
          break;
    
        case 4: //  else if (ENC_MODE == 4)
          if (JOYSTICK_MODE == 1) panMotion = updateJoystick("PAN");
          if (JOYSTICK_MODE == 1) tiltMotion = updateJoystick("TILT");
          gobospdMotion = updateEncoder(&gobospdWheel);
          animationMotion = updateEncoder(&animationWheel);
          prismMotion = updateEncoder(&prismWheel);
          strobeMotion = updateEncoder(&strobeWheel);
          break;
      }
    
    bouncer1.update();
    bouncer2.update();
    bouncer3.update();
    bouncer4.update();
    bouncer5.update();
    bouncer6.update();
    bouncer7.update();
    bouncer8.update();
    bouncer9.update();
    
    
      // check for buttons
      ENC_MODE = checkButtons();
    
      // now update our wheels
    
      if (tiltMotion != 0)
        sendWheelMove(TILT, tiltMotion);
      if (panMotion != 0)
        sendWheelMove(PAN, panMotion);
      if (framerotMotion != 0)
        sendWheelMove(FRAMEROT, framerotMotion);
      if (ctoMotion != 0)
        sendWheelMove(CTO, ctoMotion);
      if (irisMotion != 0)
        sendWheelMove(IRIS, irisMotion);
      if (edgeMotion != 0)
        sendWheelMove(EDGE, edgeMotion);
      if (zoomMotion != 0)
        sendWheelMove(ZOOM, zoomMotion);
      if (diffusionMotion != 0)
        sendWheelMove(DIFFUSION, diffusionMotion);
    
      if (thrustaMotion != 0)
        sendWheelMove(THRUSTA, thrustaMotion);
      if (angleaMotion != 0)
        sendWheelMove(ANGLEA, angleaMotion);
      if (thrustbMotion != 0)
        sendWheelMove(THRUSTB, thrustbMotion);
      if (anglebMotion != 0)
        sendWheelMove(ANGLEB, anglebMotion);
      if (thrustcMotion != 0)
        sendWheelMove(THRUSTC, thrustcMotion);
      if (anglecMotion != 0)
        sendWheelMove(ANGLEC, anglecMotion);
      if (thrustdMotion != 0)
        sendWheelMove(THRUSTD, thrustdMotion);
      if (angledMotion != 0)
        sendWheelMove(ANGLED, angledMotion);
    
      if (cyanMotion != 0)
        sendWheelMove(CYAN, cyanMotion);
      if (magentaMotion != 0)
        sendWheelMove(MAGENTA, magentaMotion);
      if (yellowMotion != 0)
        sendWheelMove(YELLOW, yellowMotion);
      if (colorMotion != 0)
        sendWheelMove(COLORWHEEL, colorMotion);
      if (hueMotion != 0)
        sendWheelMove(HUE, hueMotion);
      if (saturationMotion != 0)
        sendWheelMove(SATURATION, saturationMotion);
      if (ctoMotion != 0)
        sendWheelMove(CTO, ctoMotion);
    
      if (gobospdMotion != 0)
        sendWheelMove(GOBO, gobospdMotion);
      if (animationMotion != 0)
        sendWheelMove(ANIMATION, animationMotion);
      if (prismMotion != 0)
        sendWheelMove(PRISM, prismMotion);
      if (strobeMotion != 0)
        sendWheelMove(STROBE, strobeMotion);
    
    
    
      // Then we check to see if any OSC commands have come from Eos
      // and update the display accordingly.
      size = SLIPSerial.available();
      if (size > 0)
      {
        // Fill the msg with all of the available bytes
        while (size--)
          curMsg += (char)(SLIPSerial.read());
      }
      if (SLIPSerial.endofPacket())
      {
        parseOSCMessage(curMsg);
        lastMessageRxTime = millis();
        // We only care about the ping if we haven't heard recently
        // Clear flag when we get any traffic
        timeoutPingSent = false;
        curMsg = String();
      }
    
      if (lastMessageRxTime > 0)
      {
        unsigned long diff = millis() - lastMessageRxTime;
        //We first check if it's been too long and we need to time out
        if (diff > TIMEOUT_AFTER_IDLE_INTERVAL)
        {
          connectedToEos = false;
          lastMessageRxTime = 0;
          updateDisplay = true;
          timeoutPingSent = false;
        }
    
        //It could be the console is sitting idle. Send a ping once to
        // double check that it's still there, but only once after 2.5s have passed
        if (!timeoutPingSent && diff > PING_AFTER_IDLE_INTERVAL)
        {
          OSCMessage ping("/eos/ping");
          ping.add("box8_hello"); // This way we know who is sending the ping
          SLIPSerial.beginPacket();
          ping.send(SLIPSerial);
          SLIPSerial.endPacket();
          timeoutPingSent = true;
        }
      }
    
      if (updateDisplay)
        displayStatus();
    }

    my last post was deleted (spam - seriously?)
    Here is my code - which still works 100% in 2.7.4 but not in 2.8 (no handshake - driver is ok)

Reply
  • /*
      // Copyright (c) 2017 Electronic Theatre Controls, Inc., http://www.etcconnect.com
      //
      // Permission is hereby granted, free of charge, to any person obtaining a copy
      // of this software and associated documentation files (the "Software"), to deal
      // in the Software without restriction, including without limitation the rights
      // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      // copies of the Software, and to permit persons to whom the Software is
      // furnished to do so, subject to the following conditions:
      //
      // The above copyright notice and this permission notice shall be included in
      // all copies or substantial portions of the Software.
      //
      // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
      // THE SOFTWARE.
    */
    
    /*******************************************************************************
    
         lighthack - Box 8
    
         (c) 2018 by Roland Menzel
    
     *******************************************************************************
    
        NOTE: UPDATE VERSION_STRING IN DEFINITIONS BELOW WHEN VERSION NUMBER CHANGES
    
        Revision History
    
        yyyy-mm-dd   Vxx      By_Who                 Comment
    
        2017-07-21   1.0.0.1  Ethan Oswald Massey    Original creation
    
        2017-10-19   1.0.0.2  Sam Kearney            Fix build errors on some
                                                     Arduino platforms. Change
                                                     OSC subscribe parameters
    
        2017-10-24   1.0.0.3  Sam Kearney            Add ability to scale encoder
                                                     output
    
        2017-11-22   1.0.0.4  Hans Hinrichsen        Add splash msg before Eos
                                                     connects
    
        2017-12-07   1.0.0.5  Hans Hinrichsen        Added timeout to disconnect
                                                     and show splash screen again
    
        2018-06-27   1.0.0.6  Roland Menzel          Modded for 8 encoders, 10 buttons
                                                     and a 4x40 LCD
    
     ******************************************************************************/
    
    /*******************************************************************************
       Includes
     ******************************************************************************/
    #include <OSCBoards.h>
    #include <OSCBundle.h>
    #include <OSCData.h>
    #include <OSCMatch.h>
    #include <OSCMessage.h>
    #include <OSCTiming.h>
    
    
    #ifdef BOARD_HAS_USB_SERIAL
    #include <SLIPEncodedUSBSerial.h>
    SLIPEncodedUSBSerial SLIPSerial(thisBoardsSerialUSB);
    #else
    #include <SLIPEncodedSerial.h>
    SLIPEncodedSerial SLIPSerial(Serial);
    #endif
    
    
    //#include <LiquidCrystal.h>
    #include <LiquidCrystalFast.h>
    #include <string.h>
    #include <Bounce2.h>
    
    /*******************************************************************************
       Macros and Constants
     ******************************************************************************/
    #define LCD_CHARS           40
    #define LCD_LINES           4   // Currently assume at least 4 lines
    
    #define SHIFT_BTN           6
    #define FOCUS_BTN           7
    #define SHUTTER_BTN         8
    #define COLOR_BTN           24
    #define IMAGE_BTN           25
    #define EFFEKTOFF_BTN       26
    #define CH91_BTN            27
    #define CH92_BTN            28
    #define LAST_BTN            30
    #define JOY_ENABLE_BTN      29
    #define JOYSTICK_BTN        53
    
    #define ENC1_BTN            31
    #define ENC2_BTN            32
    #define ENC3_BTN            40
    #define ENC4_BTN            41
    #define ENC5_BTN            42
    #define ENC6_BTN            43
    #define ENC7_BTN            46
    #define ENC8_BTN            57
    
    #define XPIN                A11
    #define YPIN                A10
    
    
    #define SUBSCRIBE           ((int32_t)1)
    #define UNSUBSCRIBE         ((int32_t)0)
    
    #define EDGE_DOWN           ((int32_t)1)
    #define EDGE_UP             ((int32_t)0)
    
    #define FORWARD             0
    #define REVERSE             1
    
    // Change these values to switch which direction increase/decrease pan/tilt
    #define PAN_DIR             FORWARD
    #define TILT_DIR            FORWARD
    #define IRIS_DIR            REVERSE
    #define EDGE_DIR            REVERSE
    #define ZOOM_DIR            REVERSE
    #define THRUST_DIR          REVERSE
    #define ANGLE_DIR           REVERSE
    #define COLOR_DIR           REVERSE
    #define ROT_DIR             REVERSE
    #define DIFF_DIR            REVERSE
    
    // Use these values to make the encoder more coarse or fine. This controls
    // the number of wheel "ticks" the device sends to Eos for each tick of the
    // encoder. 1 is the default and the most fine setting. Must be an integer.
    #define PAN_SCALE           3
    #define TILT_SCALE          3
    
    #define OSC_BUF_MAX_SIZE    512
    
    const String HANDSHAKE_QUERY = "ETCOSC?";
    const String HANDSHAKE_REPLY = "OK";
    
    //See displayScreen() below - limited to 10 chars (after 6 prefix chars)
    const String VERSION_STRING = "1.0.0.6";
    
    // Change these values to alter how long we wait before sending an OSC ping
    // to see if Eos is still there, and then finally how long before we
    // disconnect and show the splash screen
    // Values are in milliseconds
    #define PING_AFTER_IDLE_INTERVAL    5000
    #define TIMEOUT_AFTER_IDLE_INTERVAL 10000
    
    /*******************************************************************************
       Local Types
     ******************************************************************************/
    enum WHEEL_TYPE { TILT, PAN, CYAN, MAGENTA, YELLOW, COLORWHEEL, HUE, SATURATION, ANGLEA, THRUSTA, ANGLEB, THRUSTB, ANGLEC, THRUSTC, ANGLED, THRUSTD,
                      IRIS, EDGE, ZOOM, DIFFUSION, FRAMEROT, CTO, PRISM, ANIMATION, GOBO, STROBE
                    };
    enum WHEEL_MODE { COARSE, FINE };
    
    
    struct Encoder
    {
      uint8_t pinA;
      uint8_t pinB;
      int pinAPrevious;
      int pinBPrevious;
      float pos;
      uint8_t direction;
    };
    struct Encoder panWheel;
    struct Encoder tiltWheel;
    
    struct Encoder cyanWheel;
    struct Encoder magentaWheel;
    struct Encoder yellowWheel;
    struct Encoder colorWheel;
    struct Encoder ctoWheel;
    struct Encoder hueWheel;
    struct Encoder saturationWheel;
    
    struct Encoder angleaWheel;
    struct Encoder thrustaWheel;
    struct Encoder anglebWheel;
    struct Encoder thrustbWheel;
    struct Encoder anglecWheel;
    struct Encoder thrustcWheel;
    struct Encoder angledWheel;
    struct Encoder thrustdWheel;
    struct Encoder framerotWheel;
    
    struct Encoder irisWheel;
    struct Encoder edgeWheel;
    struct Encoder zoomWheel;
    struct Encoder diffusionWheel;
    
    struct Encoder gobospdWheel;
    struct Encoder animationWheel;
    struct Encoder prismWheel;
    struct Encoder strobeWheel;
    
    Bounce  bouncer1  = Bounce();
    Bounce  bouncer2  = Bounce();
    Bounce  bouncer3  = Bounce();
    Bounce  bouncer4  = Bounce();
    Bounce  bouncer5  = Bounce();
    Bounce  bouncer6  = Bounce();
    Bounce  bouncer7  = Bounce();
    Bounce  bouncer8  = Bounce();
    Bounce  bouncer9  = Bounce();
    
    /*******************************************************************************
       Global Variables
     ******************************************************************************/
    int ENC_MODE = 1;
    int JOYSTICK_MODE = 0;
    int32_t val;
    String par1name;
    double par1val;
    String par2name;
    double par2val;
    String par3name;
    float par3val;
    String par4name;
    float par4val;
    String par5name;
    float par5val;
    String par6name;
    float par6val;
    String par7name;
    float par7val;
    String par8name;
    float par8val;
    
    float gobomode;
    float animationmode;
    
    int32_t panMotion;
    int32_t tiltMotion;
    int32_t framerotMotion;
    int32_t ctoMotion;
    int32_t irisMotion;
    int32_t edgeMotion;
    int32_t zoomMotion;
    int32_t diffusionMotion;
    
    
    int32_t thrustaMotion;
    int32_t angleaMotion;
    int32_t thrustbMotion;
    int32_t anglebMotion;
    int32_t thrustcMotion;
    int32_t anglecMotion;
    int32_t thrustdMotion;
    int32_t angledMotion;
    
    int32_t cyanMotion;
    int32_t magentaMotion;
    int32_t yellowMotion;
    int32_t colorMotion;
    int32_t hueMotion;
    int32_t saturationMotion;
    
    int32_t gobospdMotion;
    int32_t animationMotion;
    int32_t prismMotion;
    int32_t strobeMotion;
    
    
    // initialize the library with the numbers of the interface pins
    
    // LiquidCrystal lcd(12, 11, 10, 9, 5, 4);
    
    
    LiquidCrystalFast lcd(12, 11, 10, 9, 5, 4, 3, 2);     // LCD pins: RS  RW  EN1 EN2 D4 D5 D6 D7
    
    bool updateDisplay = false;
    bool connectedToEos = false;
    unsigned long lastMessageRxTime = 0;
    bool timeoutPingSent = false;
    
    
    
    /*******************************************************************************
       Local Functions
     ******************************************************************************/
    
    /*******************************************************************************
       Issues all our subscribes to Eos. When subscribed, Eos will keep us updated
       with the latest values for a given parameter.
    
       Parameters:  none
    
       Return Value: void
    
     ******************************************************************************/
    void issueSubscribes()
    {
      // Add a filter so we don't get spammed with unwanted OSC messages from Eos
      OSCMessage filter("/eos/filter/add");
      filter.add("/eos/out/param/*");
      filter.add("/eos/out/ping");
      SLIPSerial.beginPacket();
      filter.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      // subscribe to Eos pan & tilt updates
      OSCMessage subPan("/eos/subscribe/param/pan");
      subPan.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subPan.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subTilt("/eos/subscribe/param/tilt");
      subTilt.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subTilt.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subCyan("/eos/subscribe/param/cyan");
      subCyan.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subCyan.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subMagenta("/eos/subscribe/param/magenta");
      subMagenta.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subMagenta.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subYellow("/eos/subscribe/param/yellow");
      subYellow.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subYellow.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subColorwheel("/eos/subscribe/param/color select");
      subColorwheel.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subColorwheel.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subHue("/eos/subscribe/param/hue");
      subHue.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subHue.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subSaturation("/eos/subscribe/param/saturatn");
      subSaturation.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subSaturation.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subThrustA("/eos/subscribe/param/thrust a");
      subThrustA.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subThrustA.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subAngleA("/eos/subscribe/param/angle a");
      subAngleA.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subAngleA.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subThrustB("/eos/subscribe/param/thrust b");
      subThrustB.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subThrustB.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subAngleB("/eos/subscribe/param/angle b");
      subAngleB.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subAngleB.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subThrustC("/eos/subscribe/param/thrust c");
      subThrustC.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subThrustC.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subAngleC("/eos/subscribe/param/angle c");
      subAngleC.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subAngleC.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subThrustD("/eos/subscribe/param/thrust d");
      subThrustD.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subThrustD.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subAngleD("/eos/subscribe/param/angle d");
      subAngleD.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subAngleD.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subFrameRot("/eos/subscribe/param/frame assembly");
      subFrameRot.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subFrameRot.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subIris("/eos/subscribe/param/iris");
      subIris.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subIris.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subEdge("/eos/subscribe/param/edge");
      subEdge.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subEdge.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subZoom("/eos/subscribe/param/zoom");
      subZoom.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subZoom.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subCTO("/eos/subscribe/param/cto");
      subCTO.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subCTO.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subDiffusion("/eos/subscribe/param/diffusn");
      subDiffusion.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subDiffusion.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subGoboSpd("/eos/subscribe/param/gobo ind\\spd");
      subGoboSpd.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subGoboSpd.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subAnimationSpd("/eos/subscribe/param/animation select");
      subAnimationSpd.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subAnimationSpd.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subPrism("/eos/subscribe/param/beam fx ind\\spd");
      subPrism.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subPrism.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subStrobe("/eos/subscribe/param/shutter strobe");
      subStrobe.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subStrobe.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subGoboMode("/eos/subscribe/param/gobo_mode");
      subGoboMode.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subGoboMode.send(SLIPSerial);
      SLIPSerial.endPacket();
    
      OSCMessage subAnimationMode("/eos/subscribe/param/animation_wheel_mode");
      subAnimationMode.add(SUBSCRIBE);
      SLIPSerial.beginPacket();
      subAnimationMode.send(SLIPSerial);
      SLIPSerial.endPacket();
    
    }
    
    /*******************************************************************************
       Given a valid OSCMessage (relevant to Pan/Tilt), we update our Encoder struct
       with the new position information.
    
       Parameters:
        msg - The OSC message we will use to update our internal data
        addressOffset - Unused (allows for multiple nested roots)
    
       Return Value: void
    
     ******************************************************************************/
    void parsePanUpdate(OSCMessage& msg, int addressOffset)
    {
      panWheel.pos = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    
    void parseTiltUpdate(OSCMessage& msg, int addressOffset)
    {
      tiltWheel.pos = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    
    void parseCyanUpdate(OSCMessage& msg, int addressOffset)
    {
      cyanWheel.pos = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    
    void parseMagentaUpdate(OSCMessage& msg, int addressOffset)
    {
      magentaWheel.pos = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    
    void parseYellowUpdate(OSCMessage& msg, int addressOffset)
    {
      yellowWheel.pos = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    
    void parseColorUpdate(OSCMessage& msg, int addressOffset)
    {
      colorWheel.pos = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    
    void parseHueUpdate(OSCMessage& msg, int addressOffset)
    {
      hueWheel.pos = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    
    void parseSaturationUpdate(OSCMessage& msg, int addressOffset)
    {
      saturationWheel.pos = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    
    
    void parseAngleAUpdate(OSCMessage& msg, int addressOffset)
    {
      angleaWheel.pos = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    
    void parseThrustAUpdate(OSCMessage& msg, int addressOffset)
    {
      thrustaWheel.pos = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    
    void parseAngleBUpdate(OSCMessage& msg, int addressOffset)
    {
      anglebWheel.pos = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    
    void parseThrustBUpdate(OSCMessage& msg, int addressOffset)
    {
      thrustbWheel.pos = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    void parseAngleCUpdate(OSCMessage& msg, int addressOffset)
    {
      anglecWheel.pos = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    
    void parseThrustCUpdate(OSCMessage& msg, int addressOffset)
    {
      thrustcWheel.pos = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    void parseAngleDUpdate(OSCMessage& msg, int addressOffset)
    {
      angledWheel.pos = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    
    void parseThrustDUpdate(OSCMessage& msg, int addressOffset)
    {
      thrustdWheel.pos = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    
    void parseFrameRotUpdate(OSCMessage& msg, int addressOffset)
    {
      framerotWheel.pos = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    
    void parseIrisUpdate(OSCMessage& msg, int addressOffset)
    {
      irisWheel.pos = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    
    void parseEdgeUpdate(OSCMessage& msg, int addressOffset)
    {
      edgeWheel.pos = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    
    void parseZoomUpdate(OSCMessage& msg, int addressOffset)
    {
      zoomWheel.pos = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    
    void parseDiffusionUpdate(OSCMessage& msg, int addressOffset)
    {
      diffusionWheel.pos = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    
    void parseCTOUpdate(OSCMessage& msg, int addressOffset)
    {
      ctoWheel.pos = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    
    void parseGoboSpdUpdate(OSCMessage& msg, int addressOffset)
    {
      gobospdWheel.pos = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    
    void parseAnimationUpdate(OSCMessage& msg, int addressOffset)
    {
      animationWheel.pos = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    
    void parsePrismUpdate(OSCMessage& msg, int addressOffset)
    {
      prismWheel.pos = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    
    void parseStrobeUpdate(OSCMessage& msg, int addressOffset)
    {
      strobeWheel.pos = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    
    void parseGoboModeUpdate(OSCMessage& msg, int addressOffset)
    {
      gobomode = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    
    void parseAnimationModeUpdate(OSCMessage& msg, int addressOffset)
    {
      animationmode = msg.getOSCData(0)->getFloat();
      connectedToEos = true; // Update this here just in case we missed the handshake
      updateDisplay = true;
    }
    
    
    /*******************************************************************************
       Given an unknown OSC message we check to see if it's a handshake message.
       If it's a handshake we issue a subscribe, otherwise we begin route the OSC
       message to the appropriate function.
    
       Parameters:
        msg - The OSC message of unknown importance
    
       Return Value: void
    
     ******************************************************************************/
    void parseOSCMessage(String& msg)
    {
      // check to see if this is the handshake string
      if (msg.indexOf(HANDSHAKE_QUERY) != -1)
      {
        // handshake string found!
        SLIPSerial.beginPacket();
        SLIPSerial.write((const uint8_t*)HANDSHAKE_REPLY.c_str(), (size_t)HANDSHAKE_REPLY.length());
        SLIPSerial.endPacket();
    
        // Let Eos know we want updates on some things
        issueSubscribes();
    
        // Make our splash screen go away
        connectedToEos = true;
        updateDisplay = true;
      }
      else
      {
        // prepare the message for routing by filling an OSCMessage object with our message string
        OSCMessage oscmsg;
        oscmsg.fill((uint8_t*)msg.c_str(), (int)msg.length());
    
        // route pan/tilt messages to the relevant update function
        oscmsg.route("/eos/out/param/pan", parsePanUpdate);
        oscmsg.route("/eos/out/param/tilt", parseTiltUpdate);
    
        // route color messages to the relevant update function
        oscmsg.route("/eos/out/param/cyan", parseCyanUpdate);
        oscmsg.route("/eos/out/param/magenta", parseMagentaUpdate);
        oscmsg.route("/eos/out/param/yellow", parseYellowUpdate);
        oscmsg.route("/eos/out/param/color select", parseColorUpdate);
        oscmsg.route("/eos/out/param/hue", parseHueUpdate);
        oscmsg.route("/eos/out/param/saturatn", parseSaturationUpdate);
        // route shutter messages to the relevant update function
        oscmsg.route("/eos/out/param/thrust a", parseThrustAUpdate);
        oscmsg.route("/eos/out/param/angle a", parseAngleAUpdate);
        oscmsg.route("/eos/out/param/thrust b", parseThrustBUpdate);
        oscmsg.route("/eos/out/param/angle b", parseAngleBUpdate);
        oscmsg.route("/eos/out/param/thrust c", parseThrustCUpdate);
        oscmsg.route("/eos/out/param/angle c", parseAngleCUpdate);
        oscmsg.route("/eos/out/param/thrust d", parseThrustDUpdate);
        oscmsg.route("/eos/out/param/angle d", parseAngleDUpdate);
        oscmsg.route("/eos/out/param/frame assembly", parseFrameRotUpdate);
        // route iris/edge/zoom/diffusion/cto messages to the relevant update function
        oscmsg.route("/eos/out/param/iris", parseIrisUpdate);
        oscmsg.route("/eos/out/param/edge", parseEdgeUpdate);
        oscmsg.route("/eos/out/param/zoom", parseZoomUpdate);
        oscmsg.route("/eos/out/param/diffusn", parseDiffusionUpdate);
        oscmsg.route("/eos/out/param/cto", parseCTOUpdate);
        // route rot/beam/anmation/strobe messages to the relevant update function
        oscmsg.route("/eos/out/param/gobo ind/spd", parseGoboSpdUpdate);
        oscmsg.route("/eos/out/param/animation select", parseAnimationUpdate);
        oscmsg.route("/eos/out/param/beam fx ind/spd", parsePrismUpdate);
        oscmsg.route("/eos/out/param/shutter strobe", parseStrobeUpdate);
        oscmsg.route("/eos/out/param/gobo_mode", parseGoboModeUpdate);
        oscmsg.route("/eos/out/param/animation_wheel_mode", parseAnimationModeUpdate);
    
      }
    }
    
    /*******************************************************************************
       Updates the display with the latest parameters
    
       Parameters:  none
    
       Return Value: void
    
     ******************************************************************************/
    void displayStatus()
    {
      lcd.clear();
    
      if (!connectedToEos)
      {
        // display a splash message before the Eos connection is open
        lcd.setCursor(0, 0);
        lcd.print(String("Box8 v" + VERSION_STRING).c_str());
        lcd.setCursor(0, 1);
        lcd.print("waiting for element");
        lcd.setCursor(20, 0);
        lcd.print("Theater an der Uni");
        lcd.setCursor(28, 1);
        lcd.print("Regensburg");
        lcd.setCursor(0, 2);
        lcd.print("Programm/Elektronik: Orlando");
        lcd.setCursor(0, 3);
        lcd.print("Gehaeuse: Daniel");
      }
      else
      {
        switch (ENC_MODE)
        {
          case 1:
            par1name = "Pan";
            par1val = panWheel.pos;
            par2name = "Tilt";
            par2val = tiltWheel.pos;
            par3name = "FrameRtn";
            par3val = framerotWheel.pos;
            par4name = "";
            par4val = 9999;
            par5name = "Iris";
            par5val = irisWheel.pos;
            par6name = "Edge";
            par6val = edgeWheel.pos;
            par7name = "Zoom";
            par7val = zoomWheel.pos;
            par8name = "Diffusion";
            par8val = diffusionWheel.pos;
            break;
    
          case 2:
            par1name = "Thrust D";
            par1val = thrustdWheel.pos;
            par2name = "Angle D";
            par2val = angledWheel.pos;
            par3name = "Thrust B";
            par3val = thrustbWheel.pos;
            par4name = "Angle B";
            par4val = anglebWheel.pos;
            par5name = "Thrust A";
            par5val = thrustaWheel.pos;
            par6name = "Angle A";
            par6val = angleaWheel.pos;
            par7name = "Thrust C";
            par7val = thrustcWheel.pos;
            par8name = "Angle C";
            par8val = anglecWheel.pos;
            break;
    
          case 3:
            par1name = "Cyan";
            par1val = cyanWheel.pos;
            par2name = "Magenta";
            par2val = magentaWheel.pos;
            par3name = "Yellow";
            par3val = yellowWheel.pos;
            par4name = "Colorwheel";
            par4val = colorWheel.pos;
            par5name = "Hue";
            par5val = hueWheel.pos;
            par6name = "Sat";
            par6val = saturationWheel.pos;
            par7name = "CTO";
            par7val = ctoWheel.pos;
            par8name = "";
            par8val = 9999;
            break;
    
          case 4:
            par1name = "GobIndSpd";
            par1val = gobospdWheel.pos;
            par2name = "Animation";
            par2val = animationWheel.pos;
            par3name = "BeamFXSpd";
            par3val = prismWheel.pos;
            par4name = "Strobe";
            par4val = strobeWheel.pos;
            par5name = "GoboRot";
            par5val = 8888;
            par6name = "AnimatnRt";
            par6val = 8888;
            par7name = "";
            par7val = 9999;
            par8name = "";
            par8val = 9999;
            break;
        }
    
        // put the cursor at the begining of the first line
        lcd.setCursor(0, 0);
        lcd.print(par1name);
        // put the cursor at the begining of the second line
        lcd.setCursor(0, 1);
        if (par1val != 9999)
          lcd.print(par1val);
    
        // put the cursor at Pos 10 of the first line
        lcd.setCursor(10, 0);
        lcd.print(par2name);
        // put the cursor at Pos 10 of the second line
        lcd.setCursor(10, 1);
        if (par2val != 9999)
          lcd.print(par2val);
    
        // put the cursor at Pos 20 of the first line
        lcd.setCursor(20, 0);
        lcd.print(par3name);
        // put the cursor at Pos 20 of the second line
        lcd.setCursor(20, 1);
        if (par3val != 9999)
          lcd.print(par3val);
    
        // put the cursor at Pos 30 of the first line
        lcd.setCursor(30, 0);
        lcd.print(par4name);
        // put the cursor at Pos 30 of the second line
        lcd.setCursor(30, 1);
        if (par4val != 9999)
          lcd.print(par4val);
    
        // put the cursor at Pos 0 of the third line
        lcd.setCursor(0, 2);
        lcd.print(par5name);
        // put the cursor at Pos 0 of the fourth line
        lcd.setCursor(0, 3);
        if (par5val != 9999)
        {
          if  (par5val == 8888)
          {
            if (gobomode >= 0 && gobomode <= 15) lcd.print("Index");
            else if (gobomode >= 16 && gobomode <= 31) lcd.print("Rot +");
            else if (gobomode >= 32 && gobomode <= 47) lcd.print("Rot -");
            else if (gobomode >= 48 && gobomode <= 63) lcd.print("Animate +");
            else if (gobomode >= 64 && gobomode <= 79) lcd.print("Animate -");
          }
          else lcd.print(par5val);
        };
    
        // put the cursor at Pos 10 of the third line
        lcd.setCursor(10, 2);
        lcd.print(par6name);
        // put the cursor at Pos 10 of the fourth line
        lcd.setCursor(10, 3);
        if (par6val != 9999)
        {
          if   (par6val == 8888)
          {
            if (animationmode >= 0 && animationmode <= 50) lcd.print("Open");
            else if (animationmode >= 51 && animationmode <= 101) lcd.print("Rot +");
            else if (animationmode >= 102 && animationmode <= 152) lcd.print("Rot -");
            else if (animationmode >= 153 && animationmode <= 203) lcd.print("StRo -");
            else if (animationmode >= 204 && animationmode <= 255) lcd.print("StRo +");
          }
          else lcd.print(par6val);
        };
    
        // put the cursor at Pos 20 of the third line
        lcd.setCursor(20, 2);
        lcd.print(par7name);
        // put the cursor at Pos 20 of the fourth line
        lcd.setCursor(20, 3);
        if (par7val != 9999)
          lcd.print(par7val);
    
        // put the cursor at Pos 30 of the third line
        lcd.setCursor(30, 2);
        lcd.print(par8name);
        // put the cursor at Pos 30 of the fourth line
        lcd.setCursor(30, 3);
        if (par8val != 9999)
          lcd.print(par8val);
    
    
        if (JOYSTICK_MODE == 1)
        {
          //put the cursor at Pos 39 of 2nd line
          lcd.setCursor(39, 1);
          lcd.print("J");
        }
    
    
    
    
      }
    
      updateDisplay = false;
    }
    
    /*******************************************************************************
       Initializes a given encoder struct to the requested parameters.
    
       Parameters:
        encoder - Pointer to the encoder we will be initializing
        pinA - Where the A pin is connected to the Arduino
        pinB - Where the B pin is connected to the Arduino
        direction - Determines if clockwise or counterclockwise is "forward"
    
       Return Value: void
    
     ******************************************************************************/
    void initEncoder(struct Encoder* encoder, uint8_t pinA, uint8_t pinB, uint8_t direction)
    {
    
      encoder->pinA = pinA;
      encoder->pinB = pinB;
      encoder->pos = 0;
      encoder->direction = direction;
    
      pinMode(pinA, INPUT_PULLUP);
      pinMode(pinB, INPUT_PULLUP);
    
      encoder->pinAPrevious = digitalRead(pinA);
      encoder->pinBPrevious = digitalRead(pinB);
    }
    
    /*******************************************************************************
       Checks if the encoder has moved by comparing the previous state of the pins
       with the current state. If they are different, we know there is movement.
       In the event of movement we update the current state of our pins.
    
       Parameters:
        encoder - Pointer to the encoder we will be checking for motion
    
       Return Value:
        encoderMotion - Returns the 0 if the encoder has not moved
                                    1 for forward motion
                                   -1 for reverse motion
    
     ******************************************************************************/
    int8_t updateEncoder(struct Encoder* encoder)
    {
      int8_t encoderMotion = 0;
      int pinACurrent = digitalRead(encoder->pinA);
      int pinBCurrent = digitalRead(encoder->pinB);
    
      // has the encoder moved at all?
      if (encoder->pinAPrevious != pinACurrent)
      {
        // Since it has moved, we must determine if the encoder has moved forwards or backwards
        encoderMotion = (encoder->pinAPrevious == encoder->pinBPrevious) ? -1 : 1;
    
        // If we are in reverse mode, flip the direction of the encoder motion
        if (encoder->direction == REVERSE)
          encoderMotion = -encoderMotion;
      }
      encoder->pinAPrevious = pinACurrent;
      encoder->pinBPrevious = pinBCurrent;
    
      return encoderMotion;
    }
    
    /*******************************************************************************
       Checks if the Joystick has moved and how far.
    
       Parameters:
        dir
    
       Return Value:
        joystickMotion
    
     ******************************************************************************/
    
    int32_t updateJoystick(String dir)
    {
      if (dir == "TILT")
        val = (512 - analogRead(XPIN)) / 100;
      else if (dir == "PAN")
        val = (analogRead(YPIN) - 512) / 100;
    
      if (digitalRead(JOYSTICK_BTN) == LOW) val *= 5;
      return val;
    }
    
    
    
    /*******************************************************************************
       Sends a message to Eos informing them of a wheel movement.
    
       Parameters:
        type - the type of wheel that's moving (i.e. pan or tilt)
        ticks - the direction and intensity of the movement
    
       Return Value: void
    
     ******************************************************************************/
    void sendWheelMove(WHEEL_TYPE type, float ticks)
    {
      String wheelMsg("/eos/wheel");
    
      if (type == PAN)
      {
        if (digitalRead(SHIFT_BTN) == LOW || JOYSTICK_MODE == 1)
          wheelMsg.concat("/fine");
        else
          wheelMsg.concat("/coarse");
        wheelMsg.concat("/pan");
      }
      else if (type == TILT)
      {
        if (digitalRead(SHIFT_BTN) == LOW || JOYSTICK_MODE == 1)
          wheelMsg.concat("/fine");
        else
          wheelMsg.concat("/coarse");
        wheelMsg.concat("/tilt");
      }
    
      else if (type == THRUSTA)
      {
        wheelMsg.concat("/thrust a");
      }
      else if (type == ANGLEA)
      {
        wheelMsg.concat("/angle a");
      }
      else if (type == THRUSTB)
      {
        wheelMsg.concat("/thrust b");
      }
      else if (type == ANGLEB)
      {
        wheelMsg.concat("/angle b");
      }
      else if (type == THRUSTC)
      {
        wheelMsg.concat("/thrust c");
      }
      else if (type == ANGLEC)
      {
        wheelMsg.concat("/angle c");
      }
      else if (type == THRUSTD)
      {
        wheelMsg.concat("/thrust d");
      }
      else if (type == ANGLED)
      {
        wheelMsg.concat("/angle d");
      }
    
      else if (type == IRIS)
      {
        wheelMsg.concat("/iris");
      }
      else if (type == EDGE)
      {
        wheelMsg.concat("/edge");
      }
      else if (type == ZOOM)
      {
        wheelMsg.concat("/zoom");
      }
      else if (type == DIFFUSION)
      {
        wheelMsg.concat("/diffusn");
      }
    
      else if (type == CYAN)
      {
        wheelMsg.concat("/cyan");
      }
      else if (type == MAGENTA)
      {
        wheelMsg.concat("/magenta");
      }
      else if (type == YELLOW)
      {
        wheelMsg.concat("/yellow");
      }
      else if (type == COLORWHEEL)
      {
        wheelMsg.concat("/color select");
      }
      else if (type == HUE)
      {
        wheelMsg.concat("/hue");
      }
      else if (type == SATURATION)
      {
        wheelMsg.concat("/saturatn");
      }
      else if (type == CTO)
      {
        wheelMsg.concat("/cto");
      }
      else if (type == PRISM)
      {
        wheelMsg.concat("/beam fx ind\\spd");
      }
      else if (type == ANIMATION)
      {
        wheelMsg.concat("/animation select");
      }
      else if (type == STROBE)
      {
        wheelMsg.concat("/shutter strobe");
      }
      else if (type == GOBO)
      {
        wheelMsg.concat("/gobo ind\\spd");
      }
      else if (type == FRAMEROT)
      {
        wheelMsg.concat("/frame assembly");
      }
      else
        // something has gone very wrong
        return;
    
      OSCMessage wheelUpdate(wheelMsg.c_str());
      wheelUpdate.add(ticks);
      SLIPSerial.beginPacket();
      wheelUpdate.send(SLIPSerial);
      SLIPSerial.endPacket();
    }
    
    /*******************************************************************************
       Sends a message to Eos informing them of a key press.
    
       Parameters:
        down - whether a key has been pushed down (true) or released (false)
        key - the key that has moved
    
       Return Value: void
    
     ******************************************************************************/
    void sendKeyPress(bool down, String key)
    {
      key = "/eos/key/" + key;
      OSCMessage keyMsg(key.c_str());
    
      if (down)
        keyMsg.add(EDGE_DOWN);
      else
        keyMsg.add(EDGE_UP);
    
      SLIPSerial.beginPacket();
      keyMsg.send(SLIPSerial);
      SLIPSerial.endPacket();
      keyMsg.empty();
    }
    /*******************************************************************************
       Sends a message to Eos informing them of a parameter change
    
       Parameters:
        key - the parameter that has changed
    
       Return Value: void
    
     ******************************************************************************/
    void sendParameter(String key, float key2)
    {
      key = "/eos/param/" + key;
      OSCMessage keyMsg(key.c_str());
      if (key2 != 9999 ) keyMsg.add(key2);
      SLIPSerial.beginPacket();
      keyMsg.send(SLIPSerial);
      SLIPSerial.endPacket();
      keyMsg.empty();
    }
    /*******************************************************************************
       Sends a message to Eos informing them of a channel change
    
       Parameters:
        key - the channel that has changed
    
       Return Value: void
    
     ******************************************************************************/
    void sendChannel(float key2)
    {
      OSCMessage keyMsg("/eos/chan");
      keyMsg.add(key2);
      SLIPSerial.beginPacket();
      keyMsg.send(SLIPSerial);
      SLIPSerial.endPacket();
      keyMsg.empty();
    }
    
    /*******************************************************************************
       Checks the status of all the buttons
    
       NOTE: This does not check the shift key. The shift key is used in tandem with
       the encoder to determine coarse/fine mode and thus does not report to Eos
       directly.
    
       Parameters: none
    
       Return Value: ENC_MODE
    
     ******************************************************************************/
    int checkButtons()
    {
      static int lastKeyState = HIGH;
      static int joystickenableKeyState = HIGH;
      static int focusKeyState = HIGH;
      static int shutterKeyState = HIGH;
      static int colorKeyState = HIGH;
      static int imageKeyState = HIGH;
      static int effektoffKeyState = HIGH;
      static int ch91KeyState = HIGH;
      static int ch92KeyState = HIGH;
      static int enc1KeyState = HIGH;
      static int enc2KeyState = HIGH;
      static int enc3KeyState = HIGH;
      static int enc4KeyState = HIGH;
      static int enc5KeyState = HIGH;
      static int enc6KeyState = HIGH;
      static int enc7KeyState = HIGH;
      static int enc8KeyState = HIGH;
    
      // Has the button state changed
      if (bouncer2.read() != lastKeyState)
      {
        // Notify Eos of this key press
        if (lastKeyState == LOW)
        {
          //sendKeyPress(false, "NEXT");
          lastKeyState = HIGH;
          updateDisplay = true;
        }
        else
        {
          //  sendKeyPress(true, "NEXT");
          lastKeyState = LOW;
        }
      }
    
      if (bouncer1.read() != joystickenableKeyState)
      {
        if (joystickenableKeyState == LOW)
        {
          if (JOYSTICK_MODE == 0)
          {
            lcd.clear();
            while (digitalRead(JOYSTICK_BTN) == HIGH)
            {
              lcd.setCursor(0, 1);
              lcd.print("Press Joystick Trigger to activate");
              if (digitalRead(JOY_ENABLE_BTN) == LOW) break;
            }
            JOYSTICK_MODE = 1;
          }
          else JOYSTICK_MODE = 0;
          joystickenableKeyState = HIGH;
          updateDisplay = true;
        }
        else
        {
          joystickenableKeyState = LOW;
        }
      }
    
      if (bouncer3.read() != focusKeyState)
      {
        if (focusKeyState == LOW)
        {
          ENC_MODE = 1;
          focusKeyState = HIGH;
          updateDisplay = true;
        }
        else
        {
          // ENC_MODE = 1;
          focusKeyState = LOW;
        }
      }
    
      if (bouncer4.read() != shutterKeyState)
      {
        if (shutterKeyState == LOW)
        {
          ENC_MODE = 2;
          shutterKeyState = HIGH;
          updateDisplay = true;
        }
        else
        {
          // ENC_MODE = 2;
          shutterKeyState = LOW;
        }
      }
    
      if (bouncer5.read() != colorKeyState)
      {
        if (colorKeyState == LOW)
        {
          ENC_MODE = 3;
          colorKeyState = HIGH;
          updateDisplay = true;
        }
        else
        {
          //  ENC_MODE = 3;
          colorKeyState = LOW;
        }
      }
    
      if (bouncer6.read() != imageKeyState)
      {
        if (imageKeyState == LOW)
        {
          ENC_MODE = 4;
          imageKeyState = HIGH;
          updateDisplay = true;
        }
        else
        {
          // ENC_MODE = 4;
          imageKeyState = LOW;
        }
      }
    
      if (bouncer7.read() != effektoffKeyState)
      {
        if (effektoffKeyState == LOW)
        {
          sendParameter("shutter strobe/home", 9999);
          sendParameter("gobo ind\\spd/home", 9999);
          sendParameter("beam fx select/home", 9999);
          sendParameter("animation select/home", 9999);
          sendParameter("gobo_select/home", 9999);
          sendParameter("gobo_select_2/home", 9999);
          sendParameter("gobo_mode/home", 9999);
          sendParameter("animation_wheel_mode/home", 9999);
    
          updateDisplay = true;
          effektoffKeyState = HIGH;
        }
        else
        {
          effektoffKeyState = LOW;
        }
      }
    
      if (bouncer8.read() != ch91KeyState)
      {
        if (ch91KeyState == LOW)
        {
          sendChannel(91);
          updateDisplay = true;
          ch91KeyState = HIGH;
    
        }
        else
        {
          ch91KeyState = LOW;
        }
      }
    
      if (bouncer9.read() != ch92KeyState)
      {
        if (ch92KeyState == LOW)
        {
          sendChannel(92);
          updateDisplay = true;
          ch92KeyState = HIGH;
    
        }
        else
        {
          //   sendKeyPress(true, "MACRO");
          ch92KeyState = LOW;
        }
      }
    
      if (digitalRead(ENC1_BTN) != enc1KeyState)
      {
        if (enc1KeyState == LOW)
        {
          if (ENC_MODE == 1)
          {
          }
          else if (ENC_MODE == 2)
          {
            sendParameter("thrust d/home", 9999);
          }
          else if (ENC_MODE == 3)
          {
            sendParameter("cyan/home", 9999);
    
          }
          else if (ENC_MODE == 4)
          {
            sendParameter("gobo ind\\spd/home", 9999);
    
          };
          updateDisplay = true;
          enc1KeyState = HIGH;
        }
        else
        {
          enc1KeyState = LOW;
        }
      }
    
      if (digitalRead(ENC2_BTN) != enc2KeyState)
      {
        if (enc2KeyState == LOW)
        {
          if (ENC_MODE == 1)
          {
    
          }
          else if (ENC_MODE == 2)
          {
            sendParameter("angle d/home", 9999);
    
          }
          else if (ENC_MODE == 3)
          {
            sendParameter("magenta/home", 9999);
    
          }
          else if (ENC_MODE == 4)
          {
            sendParameter("animation select/home", 9999);
          };
          updateDisplay = true;
          enc2KeyState = HIGH;
        }
        else
        {
          enc2KeyState = LOW;
        }
      }
      if (digitalRead(ENC3_BTN) != enc3KeyState)
      {
        if (enc3KeyState == LOW)
        {
          if (ENC_MODE == 1)
          {
            sendParameter("frame assembly/home", 9999);
    
          }
          else if (ENC_MODE == 2)
          {
            sendParameter("thrust b/home", 9999);
    
          }
          else if (ENC_MODE == 3)
          {
            sendParameter("yellow/home", 9999);
          }
          else if (ENC_MODE == 4)
          {
            sendParameter("beam fx ind\\spd/home", 9999);
    
          };
          updateDisplay = true;
          enc3KeyState = HIGH;
        }
        else
        {
          enc3KeyState = LOW;
        }
      }
      if (digitalRead(ENC4_BTN) != enc4KeyState)
      {
        if (enc4KeyState == LOW)
        {
          if (ENC_MODE == 1)
          {
            // sendParameter("cto/home", 9999);
    
          }
          else if (ENC_MODE == 2)
          {
            sendParameter("angle b/home", 9999);
    
          }
          else if (ENC_MODE == 3)
          {
            sendParameter("color select/home", 9999);
    
          }
          else if (ENC_MODE == 4)
          {
            sendParameter("shutter strobe/home", 9999);
          };
          updateDisplay = true;
          enc4KeyState = HIGH;
        }
        else
        {
          enc4KeyState = LOW;
        }
      }
      if (digitalRead(ENC5_BTN) != enc5KeyState)
      {
        if (enc5KeyState == LOW)
        {
          if (ENC_MODE == 1)
          {
            sendParameter("iris/home", 9999);
          }
          else if (ENC_MODE == 2)
          {
            sendParameter("thrust a/home", 9999);
          }
          else if (ENC_MODE == 3)
          {
          }
          else if (ENC_MODE == 4)
          {
            if (gobomode >= 0 && gobomode <= 15) sendParameter("gobo_mode", 24);
            else if (gobomode >= 16 && gobomode <= 31) sendParameter("gobo_mode", 40);
            else if (gobomode >= 32 && gobomode <= 47) sendParameter("gobo_mode", 56);
            else if (gobomode >= 48 && gobomode <= 63) sendParameter("gobo_mode", 72);
            else if (gobomode >= 64 && gobomode <= 79) sendParameter("gobo_mode", 7);
          };
          updateDisplay = true;
          enc5KeyState = HIGH;
        }
        else
        {
          enc5KeyState = LOW;
        }
      }
      if (digitalRead(ENC6_BTN) != enc6KeyState)
      {
        if (enc6KeyState == LOW)
        {
          if (ENC_MODE == 1)
          {
            sendParameter("edge/home", 9999);
          }
          else if (ENC_MODE == 2)
          {
            sendParameter("angle a/home", 9999);
    
          }
          else if (ENC_MODE == 3)
          {
          }
          else if (ENC_MODE == 4)
          {
            if (animationmode >= 0 && animationmode <= 50) sendParameter("animation_wheel_mode", 76);
            else  if (animationmode >= 51 && animationmode <= 101) sendParameter("animation_wheel_mode", 127);
            else if (animationmode >= 102 && animationmode <= 152) sendParameter("animation_wheel_mode", 178);
            else if (animationmode >= 153 && animationmode <= 203) sendParameter("animation_wheel_mode", 230);
            else if (animationmode >= 204 && animationmode <= 255) sendParameter("animation_wheel_mode", 25);
          };
          updateDisplay = true;
          enc6KeyState = HIGH;
        }
        else
        {
          enc6KeyState = LOW;
        }
      }
      if (digitalRead(ENC7_BTN) != enc7KeyState)
      {
        if (enc7KeyState == LOW)
        {
          if (ENC_MODE == 1)
          {
            sendParameter("zoom/home", 9999);
    
          }
          else if (ENC_MODE == 2)
          {
            sendParameter("thrust c/home", 9999);
    
          }
          else if (ENC_MODE == 3)
          {
            sendParameter("cto/home", 9999);
    
          }
          else if (ENC_MODE == 4)
          {
          };
          updateDisplay = true;
          enc7KeyState = HIGH;
        }
        else
        {
          enc7KeyState = LOW;
        }
      }
      if (digitalRead(ENC8_BTN) != enc8KeyState)
      {
        if (enc8KeyState == LOW)
        {
          if (ENC_MODE == 1)
          {
            sendParameter("diffusn/home", 9999);
    
          }
          else if (ENC_MODE == 2)
          {
            sendParameter("angle c/home", 9999);
    
          }
          else if (ENC_MODE == 3)
          {
          }
          else if (ENC_MODE == 4)
          {
          };
          updateDisplay = true;
          enc8KeyState = HIGH;
        }
        else
        {
          enc8KeyState = LOW;
        }
      }
      return ENC_MODE;
    }
    
    /*******************************************************************************
       Here we define our encoders per page.
       Depending on the Encoder Page, the different parameters are loaded onto the
       encoders
       Encoder 1 = A0, A1
       Encoder 2 = A2, A3
       Encoder 3 = A4, A5
       Encoder 4 = A6, A7
       Encoder 5 = A8, A9
       Encoder 6 = A20, A19
       Encoder 7 = A14, A15
       Encoder 8 = A16, A17
    
       Parameters: none
    
       Return Value: void
    
     ******************************************************************************/
    void initEncoderPage()
    {
      initEncoder(&panWheel, A0, A1, PAN_DIR);
      initEncoder(&tiltWheel, A2, A3, TILT_DIR);
      initEncoder(&framerotWheel, A4, A5, ROT_DIR);
      initEncoder(&irisWheel, A8, A9, IRIS_DIR);
      initEncoder(&edgeWheel, A20, A19, EDGE_DIR);
      initEncoder(&zoomWheel, A14, A15, ZOOM_DIR);
      initEncoder(&diffusionWheel, A16, A17, DIFF_DIR);
      initEncoder(&thrustdWheel, A0, A1, THRUST_DIR);
      initEncoder(&angledWheel, A2, A3, ANGLE_DIR);
      initEncoder(&thrustbWheel, A4, A5, THRUST_DIR);
      initEncoder(&anglebWheel, A6, A7, ANGLE_DIR);
      initEncoder(&thrustaWheel, A8, A9, THRUST_DIR);
      initEncoder(&angleaWheel, A20, A19, ANGLE_DIR);
      initEncoder(&thrustcWheel, A14, A15, THRUST_DIR);
      initEncoder(&anglecWheel, A16, A17, ANGLE_DIR);
      initEncoder(&cyanWheel, A0, A1, COLOR_DIR);
      initEncoder(&magentaWheel, A2, A3, COLOR_DIR);
      initEncoder(&yellowWheel, A4, A5, COLOR_DIR);
      initEncoder(&colorWheel, A6, A7, COLOR_DIR);
      initEncoder(&hueWheel, A8, A9, COLOR_DIR);
      initEncoder(&saturationWheel, A20, A19, COLOR_DIR);
      initEncoder(&ctoWheel, A14, A15, COLOR_DIR);
      initEncoder(&gobospdWheel, A0, A1, ROT_DIR);
      initEncoder(&animationWheel, A2, A3, ROT_DIR);
      initEncoder(&prismWheel, A4, A5, ROT_DIR);
      initEncoder(&strobeWheel, A6, A7, ROT_DIR);
    }
    
    
    /*******************************************************************************
       Here we setup our encoder, lcd, and various input devices. We also prepare
       to communicate OSC with Eos by setting up SLIPSerial. Once we are done with
       setup() we pass control over to loop() and never call setup() again.
    
       NOTE: This function is the entry function. This is where control over the
       Arduino is passed to us (the end user).
    
       Parameters: none
    
       Return Value: void
    
     ******************************************************************************/
    void setup()
    {
      lcd.begin(LCD_CHARS, LCD_LINES);
      lcd.clear();
      // display a splash message before the Eos connection is open
      lcd.setCursor(0, 0);
      lcd.print(String("Box8 v" + VERSION_STRING).c_str());
      lcd.setCursor(0, 1);
      lcd.print("waiting for element");
      lcd.setCursor(20, 0);
      lcd.print("Theater an der Uni");
      lcd.setCursor(28, 1);
      lcd.print("Regensburg");
      lcd.setCursor(0, 2);
      lcd.print("Programm/Elektronik: Orlando");
      lcd.setCursor(0, 3);
      lcd.print("Gehaeuse: Daniel");
    
    
      SLIPSerial.begin(115200);
      // This is a hack around an Arduino bug. It was taken from the OSC library
      //examples
    #ifdef BOARD_HAS_USB_SERIAL
      while (!SerialUSB);
    #else
      while (!Serial);
    #endif
    
    
    
      // This is necessary for reconnecting a device because it needs some time
      // for the serial port to open, but meanwhile the handshake message was
      // sent from Eos
      SLIPSerial.beginPacket();
      SLIPSerial.write((const uint8_t*)HANDSHAKE_REPLY.c_str(), (size_t)HANDSHAKE_REPLY.length());
      SLIPSerial.endPacket();
      // Let Eos know we want updates on some things
      issueSubscribes();
    
      pinMode(JOY_ENABLE_BTN, INPUT_PULLUP);
      bouncer1.attach( JOY_ENABLE_BTN );
      bouncer1.interval(5);
      pinMode(LAST_BTN, INPUT_PULLUP);
      bouncer2.attach( LAST_BTN );
      bouncer2.interval(5);
      pinMode(SHIFT_BTN, INPUT_PULLUP);
      pinMode(FOCUS_BTN, INPUT_PULLUP);
      bouncer3.attach( FOCUS_BTN );
      bouncer3.interval(5);
      pinMode(SHUTTER_BTN, INPUT_PULLUP);
      bouncer4.attach( SHUTTER_BTN );
      bouncer4.interval(5);
      pinMode(COLOR_BTN, INPUT_PULLUP);
      bouncer5.attach( COLOR_BTN );
      bouncer5.interval(5);
      pinMode(IMAGE_BTN, INPUT_PULLUP);
      bouncer6.attach( IMAGE_BTN );
      bouncer6.interval(5);
      pinMode(EFFEKTOFF_BTN, INPUT_PULLUP);
      bouncer7.attach( EFFEKTOFF_BTN );
      bouncer7.interval(5);
      pinMode(CH91_BTN, INPUT_PULLUP);
      bouncer8.attach( CH91_BTN );
      bouncer8.interval(5);
      pinMode(CH92_BTN, INPUT_PULLUP);
      bouncer9.attach( CH92_BTN );
      bouncer9.interval(5);
      pinMode(ENC1_BTN, INPUT_PULLUP);
      pinMode(ENC2_BTN, INPUT_PULLUP);
      pinMode(ENC3_BTN, INPUT_PULLUP);
      pinMode(ENC4_BTN, INPUT_PULLUP);
      pinMode(ENC5_BTN, INPUT_PULLUP);
      pinMode(ENC6_BTN, INPUT_PULLUP);
      pinMode(ENC7_BTN, INPUT_PULLUP);
      pinMode(ENC8_BTN, INPUT_PULLUP);
      pinMode(JOYSTICK_BTN, INPUT_PULLUP);
    
    
      initEncoderPage();
      displayStatus();
    }
    
    /*******************************************************************************
       Here we service, monitor, and otherwise control all our peripheral devices.
       First, we retrieve the status of our encoders and buttons and update Eos.
       Next, we check if there are any OSC messages for us.
       Finally, we update our display (if an update is necessary)
    
       NOTE: This function is our main loop and thus this function will be called
       repeatedly forever
    
       Parameters: none
    
       Return Value: void
    
     ******************************************************************************/
    void loop()
    {
    
      static String curMsg;
      int size;
      // get the updated state of each encoder
    
      switch (ENC_MODE)
      {
        case 1:
          if (JOYSTICK_MODE == 1) panMotion = updateJoystick("PAN");
          else panMotion = updateEncoder(&panWheel);
          if (JOYSTICK_MODE == 1) tiltMotion = updateJoystick("TILT");
          else tiltMotion = updateEncoder(&tiltWheel);
          framerotMotion = updateEncoder(&framerotWheel);
          irisMotion = updateEncoder(&irisWheel);
          edgeMotion = updateEncoder(&edgeWheel);
          zoomMotion = updateEncoder(&zoomWheel);
          diffusionMotion = updateEncoder(&diffusionWheel);
          // Scale the result by a scaling factor
          if (JOYSTICK_MODE == 0) panMotion *= PAN_SCALE;
          if (JOYSTICK_MODE == 0)  tiltMotion *= TILT_SCALE;
          break;
    
        case 2: //else if (ENC_MODE == 2)
          if (JOYSTICK_MODE == 1) panMotion = updateJoystick("PAN");
          if (JOYSTICK_MODE == 1) tiltMotion = updateJoystick("TILT");
          thrustaMotion = updateEncoder(&thrustaWheel);
          angleaMotion = updateEncoder(&angleaWheel);
          thrustbMotion = updateEncoder(&thrustbWheel);
          anglebMotion = updateEncoder(&anglebWheel);
          thrustcMotion = updateEncoder(&thrustcWheel);
          anglecMotion = updateEncoder(&anglecWheel);
          thrustdMotion = updateEncoder(&thrustdWheel);
          angledMotion = updateEncoder(&angledWheel);
          break;
    
        case 3: //      else if (ENC_MODE == 3)
          if (JOYSTICK_MODE == 1) panMotion = updateJoystick("PAN");
          if (JOYSTICK_MODE == 1) tiltMotion = updateJoystick("TILT");
          cyanMotion = updateEncoder(&cyanWheel);
          magentaMotion = updateEncoder(&magentaWheel);
          yellowMotion = updateEncoder(&yellowWheel);
          colorMotion = updateEncoder(&colorWheel);
          hueMotion = updateEncoder(&hueWheel);
          saturationMotion = updateEncoder(&saturationWheel);
          ctoMotion = updateEncoder(&ctoWheel);
          break;
    
        case 4: //  else if (ENC_MODE == 4)
          if (JOYSTICK_MODE == 1) panMotion = updateJoystick("PAN");
          if (JOYSTICK_MODE == 1) tiltMotion = updateJoystick("TILT");
          gobospdMotion = updateEncoder(&gobospdWheel);
          animationMotion = updateEncoder(&animationWheel);
          prismMotion = updateEncoder(&prismWheel);
          strobeMotion = updateEncoder(&strobeWheel);
          break;
      }
    
    bouncer1.update();
    bouncer2.update();
    bouncer3.update();
    bouncer4.update();
    bouncer5.update();
    bouncer6.update();
    bouncer7.update();
    bouncer8.update();
    bouncer9.update();
    
    
      // check for buttons
      ENC_MODE = checkButtons();
    
      // now update our wheels
    
      if (tiltMotion != 0)
        sendWheelMove(TILT, tiltMotion);
      if (panMotion != 0)
        sendWheelMove(PAN, panMotion);
      if (framerotMotion != 0)
        sendWheelMove(FRAMEROT, framerotMotion);
      if (ctoMotion != 0)
        sendWheelMove(CTO, ctoMotion);
      if (irisMotion != 0)
        sendWheelMove(IRIS, irisMotion);
      if (edgeMotion != 0)
        sendWheelMove(EDGE, edgeMotion);
      if (zoomMotion != 0)
        sendWheelMove(ZOOM, zoomMotion);
      if (diffusionMotion != 0)
        sendWheelMove(DIFFUSION, diffusionMotion);
    
      if (thrustaMotion != 0)
        sendWheelMove(THRUSTA, thrustaMotion);
      if (angleaMotion != 0)
        sendWheelMove(ANGLEA, angleaMotion);
      if (thrustbMotion != 0)
        sendWheelMove(THRUSTB, thrustbMotion);
      if (anglebMotion != 0)
        sendWheelMove(ANGLEB, anglebMotion);
      if (thrustcMotion != 0)
        sendWheelMove(THRUSTC, thrustcMotion);
      if (anglecMotion != 0)
        sendWheelMove(ANGLEC, anglecMotion);
      if (thrustdMotion != 0)
        sendWheelMove(THRUSTD, thrustdMotion);
      if (angledMotion != 0)
        sendWheelMove(ANGLED, angledMotion);
    
      if (cyanMotion != 0)
        sendWheelMove(CYAN, cyanMotion);
      if (magentaMotion != 0)
        sendWheelMove(MAGENTA, magentaMotion);
      if (yellowMotion != 0)
        sendWheelMove(YELLOW, yellowMotion);
      if (colorMotion != 0)
        sendWheelMove(COLORWHEEL, colorMotion);
      if (hueMotion != 0)
        sendWheelMove(HUE, hueMotion);
      if (saturationMotion != 0)
        sendWheelMove(SATURATION, saturationMotion);
      if (ctoMotion != 0)
        sendWheelMove(CTO, ctoMotion);
    
      if (gobospdMotion != 0)
        sendWheelMove(GOBO, gobospdMotion);
      if (animationMotion != 0)
        sendWheelMove(ANIMATION, animationMotion);
      if (prismMotion != 0)
        sendWheelMove(PRISM, prismMotion);
      if (strobeMotion != 0)
        sendWheelMove(STROBE, strobeMotion);
    
    
    
      // Then we check to see if any OSC commands have come from Eos
      // and update the display accordingly.
      size = SLIPSerial.available();
      if (size > 0)
      {
        // Fill the msg with all of the available bytes
        while (size--)
          curMsg += (char)(SLIPSerial.read());
      }
      if (SLIPSerial.endofPacket())
      {
        parseOSCMessage(curMsg);
        lastMessageRxTime = millis();
        // We only care about the ping if we haven't heard recently
        // Clear flag when we get any traffic
        timeoutPingSent = false;
        curMsg = String();
      }
    
      if (lastMessageRxTime > 0)
      {
        unsigned long diff = millis() - lastMessageRxTime;
        //We first check if it's been too long and we need to time out
        if (diff > TIMEOUT_AFTER_IDLE_INTERVAL)
        {
          connectedToEos = false;
          lastMessageRxTime = 0;
          updateDisplay = true;
          timeoutPingSent = false;
        }
    
        //It could be the console is sitting idle. Send a ping once to
        // double check that it's still there, but only once after 2.5s have passed
        if (!timeoutPingSent && diff > PING_AFTER_IDLE_INTERVAL)
        {
          OSCMessage ping("/eos/ping");
          ping.add("box8_hello"); // This way we know who is sending the ping
          SLIPSerial.beginPacket();
          ping.send(SLIPSerial);
          SLIPSerial.endPacket();
          timeoutPingSent = true;
        }
      }
    
      if (updateDisplay)
        displayStatus();
    }

    my last post was deleted (spam - seriously?)
    Here is my code - which still works 100% in 2.7.4 but not in 2.8 (no handshake - driver is ok)

Children
No Data