motorized fader controller teensy 3.6 issue

Status
Not open for further replies.

rotabox

Well-known member
hi
im working on a motorized midi controler with 8 motor faders.
i start develop my project with 1 teensy trying to use it for all functions.
first after few weeks since i connect the first teensy the teensy just die one day (i did not make any changes or touch any thing just power it up as usual.
so i thout that bay be my connection or wires soldering so i order another teensy and i solder everything from start to be sure its not my mistake. after few weeks of development i decided thats i should add 1 more teensy as a slave coz 1 teensy starts act weird when i used all the functions i need in the code. so i buy another teensy and connected it as a master in i2c with pullup resistors.
it work for few days of code development untill today. i must say that again i didnt touch any thing or make any changes. the only change i made was in the master code trying to send movement data to gerrit slave code to move the faders and i did it they move to the place i send them. so i know the system work and also the codes work until the teensy dies.

first i must say im new in the code field and im not programer.
my goal is to send usb midi from pc to the master process the data and send it to faders.
i have already did this process in serial rx tx connection test and its work fine also sending data from the fader to the pc was ok
but only when i tryd to read and write from both sides its got stack. so i move to gerrit slave code that i know is build for this goal.
but after 2 or 3 times i change the slave code and send the data to master the master stop responed and just die.
i tryd all the recovery methods from press reset button and plug the cable also tryd to look in verbos info and even i lookd under- about this mac / usb/
and nothing.... the chip is dead.

ther is any chance the master can kill the slave chip with overflow data? coz if so that the only think i can think of.

im waitng now for the pcb i develop and orderd. and im waiting for new teensy to come to build this project again in the best way but im realy afraid that this is gonna heppend again from no where.
so if u gays can halp me to understend what im doing wrong ill be very grateful.
also ill be glad to get any help with the code.

this is the PCB schematic i made of the project its the same as gerrit project.(i have one mistake over ther coz the pcb ready for Serial RX TX 33,34 and im gonna use I2C. so ill solder 2 new lines in the back of the pcb with pull ups resistors from 33,34 in the slave to new free pin that il choos in the master)

https://drive.google.com/file/d/1RzpDqx0uk7i45H-KwqzlbZ8_fo2bvtM1/view?usp=sharing


gerrit controller project:
https://forum.pjrc.com/threads/42477-Teensy-3-6-controlled-motorfader-panel.


i used the same parts in my project:

220 ac to 12 dc 4A psu
2X dc to dc step down (for 5v and 10v)
2X teensy 3.6
4X l9110s H briges
8X ALPS motorized faders 100mm
AD70 cricut for AREF

gerrit code for the slave i used (i tryd put this code to run in the loop for test that the code that moves my faders in the last time)
Code:
void sendFaderCommand(byte mode){
  size_t idx;
  faderTargetData[16]=0;
  for (int i=0;i<8;i++){
    // split Target integers over two bytes in array
    faderTargetData[(i*2)]=(faderTarget[i] >> 8) & 0xFF;
    faderTargetData[(i*2)+1]=faderTarget[i] & 0xFF;
    // fill last byte in array with faderMove boolean array
    faderTargetData[16] |= faderMove[i] << i;
  }
  // Transmit to Slave
  Wire.beginTransmission(PANEL_ADDR);   // Slave address
  // Send mode
  Wire.write(mode);
  // Send target data
  for(idx = 0; idx <= MESSAGE_LEN; idx++) // Write data to I2C Tx buffer
      Wire.write(faderTargetData[idx]);
  Wire.endTransmission();           // Transmit to Slave
}

the way i tryd to run it
Code:
// -------------------------------------------------------------------------------------------
// Basic Master
// -------------------------------------------------------------------------------------------
//
// This creates a simple I2C Master device which when triggered will send/receive a text 
// string to/from a Slave device.  It is intended to pair with a Slave device running the 
// basic_slave sketch.
//
// Pull pin12 input low to send.
// Pull pin11 input low to receive.
//
// This example code is in the public domain.
//
// -------------------------------------------------------------------------------------------

#include <i2c_t3.h>

// Memory
#define MESSAGE_LEN 17
uint8_t PANEL_ADDR = 0x66; 
char databuf[MESSAGE_LEN];

int count;
int     faderTargetData[8]={500,500,500,500,500,500,500,500}; 
boolean faderMove[8];
int     faderTarget[8];


void setup()
{
    pinMode(LED_BUILTIN,OUTPUT);    // LED
    digitalWrite(LED_BUILTIN,LOW);  // LED off
    pinMode(12,INPUT_PULLUP);       // Control for Send
    pinMode(11,INPUT_PULLUP);       // Control for Receive

    // Setup for Master mode, pins 18/19, external pullups, 400kHz, 200ms default timeout
    Wire.begin(I2C_MASTER, 0x00, I2C_PINS_18_19, I2C_PULLUP_EXT, 400000);
    Wire.setDefaultTimeout(200000); // 200ms

    // Data init
    memset(databuf, 0, sizeof(databuf));
    count = 0;

    Serial.begin(115200);
}

void loop()
{
sendFaderCommand(MESSAGE_LEN);
//    if(digitalRead(11) == LOW)
//    {
//        digitalWrite(LED_BUILTIN,HIGH);   // LED on
//
//        // Print message
//        Serial.print("Reading from Slave: ");
//        
//        // Read from Slave
//        Wire.requestFrom(PANEL_ADDR, (size_t)MESSAGE_LEN); // Read from Slave (string len unknown, request full buffer)
//
//        // Check if error occured
//        if(Wire.getError())
//            Serial.print("FAIL\n");
//        else
//        {
//            // If no error then read Rx data into buffer and print
//            Wire.read(databuf, Wire.available());
//            Serial.printf("'%s' OK\n",databuf);
//        }
//
//        digitalWrite(LED_BUILTIN,LOW);    // LED off
//        delay(100);                       // Delay to space out tests
//    }
}
void sendFaderCommand(byte mode){
  size_t idx;
  faderTargetData[16]=0;
  for (int i=0;i<8;i++){
    // split Target integers over two bytes in array
    faderTargetData[(i*2)]=(faderTarget[i] >> 8) & 0xFF;
    faderTargetData[(i*2)+1]=faderTarget[i] & 0xFF;
    // fill last byte in array with faderMove boolean array
    faderTargetData[16] |= faderMove[i] << i;
  }
  // Transmit to Slave
  Wire.beginTransmission(PANEL_ADDR);   // Slave address
  // Send mode
  Wire.write(mode);
  // Send target data
  for(idx = 0; idx <= MESSAGE_LEN; idx++) // Write data to I2C Tx buffer
      Wire.write(faderTargetData[idx]);
  Wire.endTransmission();           // Transmit to Slave
}

gerrit code for the slave i used

Code:
/* 
  Zeus Commander Controller Series
  I2C controlled motorfader octet

  Full PID motor control supporting two operating modes:
  TARGET  for fast preset recall
  FOLLOW  for slow to fast following of values 

  This code expands on the examples provided with the i2c_t3 library for the I2Ccommunication
  
  This code is in the public domain.
*/
#include <i2c_t3.h>
#include <ResponsiveAnalogRead.h>

// PID arrays
float dState[8];
float iState[8];
float iMin[8];
float iMax[8];
float pGain[8];
float iGain[8];
float dGain[8];
float plantDrive[8];
float maxD=0.00;       // maximum derivative value for debugging purposes

void UpdatePID(int faderIndex, float faderError, float faderPosition)
{
  float pTerm, dTerm, iTerm, plantDriveInput;
  // calculate the proportional term
  pTerm = pGain[faderIndex] * faderError;   
  // calculate the integral state with appropriate limiting
  iState[faderIndex] += faderError;
  if (iState[faderIndex] > iMax[faderIndex]){
    iState[faderIndex] = iMax[faderIndex];
    }
  else if (iState[faderIndex] < iMin[faderIndex]){
    iState[faderIndex] = iMin[faderIndex];
  }
  iTerm = iGain[faderIndex] * iState[faderIndex];  // calculate the integral term
  dTerm = dGain[faderIndex] * (faderPosition - dState[faderIndex]);
  if (abs(faderPosition - dState[faderIndex]) > maxD){
    maxD=abs(faderPosition - dState[faderIndex]);
  }
  dState[faderIndex] = faderPosition;

  plantDriveInput = pTerm + iTerm - dTerm;
  if (plantDriveInput > 4096) {
    plantDriveInput=4096;
  }
    if (plantDriveInput < -4096) {
    plantDriveInput=-4096;
  }
  plantDrive[faderIndex]=plantDriveInput;
}

elapsedMicros sinceFaderRead;                         // timer for fader check
unsigned int  faderReadInterval=1000;                 // interval in microseconds for checking fader position(update)
elapsedMicros sinceTouchRead;                         // timer for touch read
unsigned int  touchReadInterval=5000;                 // interval in microseconds for reading fader touch
elapsedMillis sinceLastCommand;                       // timer for time-out of PIF after last command
unsigned int  commandTimeOutInterval=500;             // interval for PID time-out in milliseconds
int     faderWiperPin[8]={32,31,39,20,21,15,65,64};   // analog in pin for fader wipers
int     faderTouchPin[8]={1,0,16,17,18,19,22,23};     // touchRead pins for fader touch
int     faderMotorUpPin[8]={29,9,7,5,3,14,36,38};     // motor up PWM pin
int     faderMotorDownPin[8]={30,10,8,6,4,2,35,37};   // motor down PWM pin
int     faderValue[8];                                // fader value ranging 0 to 1023
int     faderTarget[8];                               // fader target value ranging 0 to 1023
int     faderTouch[8];                                // fader touch reading
boolean faderPidOn[8];                                // status of PID
int     faderTouchThreshold=3000;                     // threshold value for touchRead
boolean faderTouched[8];                              // boolean indicating if fader is touched

ResponsiveAnalogRead analog[8]{ResponsiveAnalogRead(32, true),ResponsiveAnalogRead(31, true),
                               ResponsiveAnalogRead(39, true),ResponsiveAnalogRead(20, true),
                               ResponsiveAnalogRead(21, true),ResponsiveAnalogRead(15, true),
                               ResponsiveAnalogRead(65, true),ResponsiveAnalogRead(64, true)};

// I2C communication
// Command definitions
#define TARGET    0x10              // All faders to target value as fast as possible. PID state is cleared with each command.
#define FOLLOW    0x20              // Faders follow value, last byte in message is used to determine which motors should move.
                                    // PIDs will time-out after last command.

// Function prototypes
void receiveEvent(size_t count);
void requestEvent(void);
#define PANEL_ADDR 0x66              // I2C address, should be selectable by jumper using leftover pins.
#define MESSAGE_LEN 17
byte faderValueData[MESSAGE_LEN];    // 16 bytes for values, 1 byte for touch
byte faderTargetData[MESSAGE_LEN];   // 16 for target values, 1 byte for indicating which faders should be moved.
volatile uint8_t received;
volatile uint8_t cmd;
byte operatingMode = TARGET;


void setup()
{  
  
  //initialize faders and analog reference 
  analogReference(EXTERNAL);
  analogWriteResolution(12);
  for (int i=0; i<8; i++){
    pinMode(faderMotorUpPin[i], OUTPUT);
    pinMode(faderMotorDownPin[i], OUTPUT);
    analogWriteFrequency(faderMotorUpPin[i],14648.437);  // 14648.437 ideal frequency for 180MHz 12bit PWM
    analogWriteFrequency(faderMotorDownPin[i],14648.437);  //14648.437 ideal frequency for 180MHz 12bit PWM
    faderTouched[i]=false;
    faderValueData[i]=0;
    faderTarget[i]=500;
    faderPidOn[i]=false;
  }    
       
  readFaders();
  // set PID tuning parameters
  for (int i=0;i<8;i++){
    pGain[i]=10;
    iGain[i]=1;
    dGain[i]=50.00;
    iMin[i]=-1500;
    iMax[i]=1500;
  }
  
  // Setup for Slave mode pins 33/34, external pullups, 400kHz
  Wire.begin(I2C_SLAVE, PANEL_ADDR, I2C_PINS_33_34, I2C_PULLUP_EXT, 400000);
  // register events
  Wire.onReceive(receiveEvent);
  Wire.onRequest(requestEvent);
  cmd = 0;    // incoming command operating mode
}

void loop()                     
{
  // check elapsedMicros and millis timers
  // faders
  if (sinceFaderRead >=faderReadInterval) {
    sinceFaderRead = sinceFaderRead-faderReadInterval;
    readFaders();
    
    // always update PID for timing consistency
    for (int i=0;i<8;i++){
        UpdatePID(i,faderTarget[i]-faderValue[i],faderValue[i]);
      }
      
    // loop through faders and move where necessary
    for (int i=0;i<8;i++){
      if (!faderTouched[i] && faderPidOn[i]){
        if(plantDrive[i]>0 && faderValue[i] <=1022){
          analogWrite(faderMotorDownPin[i],0);
          analogWrite(faderMotorUpPin[i],plantDrive[i]);
        } else if (plantDrive[i] <=0 && faderValue[i] >=1){
          analogWrite(faderMotorUpPin[i],0);
          analogWrite(faderMotorDownPin[i],-plantDrive[i]);
        }
      } 
      else if(!faderPidOn[i]){
          analogWrite(faderMotorDownPin[i],0);
          analogWrite(faderMotorUpPin[i],0);
        }
    }
  }
  
  //  separate touchRead timer 
  if (sinceTouchRead >= touchReadInterval){
    sinceTouchRead = sinceTouchRead - touchReadInterval;
    readTouch();
  }
  
  // command time-out  
  if (sinceLastCommand >= commandTimeOutInterval){
    for (int i=0;i<8;i++){
      faderPidOn[i]=false;
    }
    sinceLastCommand = 0;
  }
  
}

// read fader analog values and update faderValue array
void readFaders() {
  for (int i=0; i<8; i++) {
    analog[i].update();
    faderValue[i] = analog[i].getValue();
  } 
}

// read fader touch
void readTouch() {
  for (int i=0; i<8; i++) {
    faderTouch[i]= touchRead(faderTouchPin[i]);
    if (faderTouch[i]>faderTouchThreshold){
      faderTouched[i]=true;
    } else if (faderTouch[i]<=faderTouchThreshold){
      faderTouched[i]=false;
    }
  } 
}

// handle Rx Event (incoming I2C data)
void receiveEvent(size_t count) {
  size_t idx;
  int incomingTargetValue[8];
  boolean incomingFaderMove[8];
  if(count)
  {
    // grab command
    cmd = Wire.readByte();
    switch(cmd)
    {
    case TARGET:
      if (operatingMode==FOLLOW){
        // set PID tuning parameters
//        for (int i=0;i<8;i++){
//          pGain[i]=20.00;
//          iGain[i]=0.5;
//          dGain[i]=40.00;
//        }
        operatingMode=TARGET;
      }
      break;
    case FOLLOW:
      if (operatingMode==TARGET){
        // set PID tuning parameters
//        for (int i=0;i<8;i++){
//          pGain[i]=20.00;
//          iGain[i]=0.5;
//          dGain[i]=40.00;
//        }
        operatingMode=FOLLOW;
      }
      break;
    }
    idx = 0;
    while(Wire.available()) faderTargetData[idx++] = Wire.readByte();
          
    // Process data
    for (int i=0;i<8;i++){
      // combine bytes back to integers and put in temporary array
      incomingTargetValue[i]= faderTargetData[(i*2)];
      incomingTargetValue[i]= incomingTargetValue[i] << 8 | faderTargetData[(i*2)+1];
      // fill faderPidOn boolean array with last byte
      incomingFaderMove[i] = 1 & faderTargetData[16] >> i;
      // clear PID states if PID is switched on or if mode is TARGET
      if ((incomingFaderMove[i] && faderPidOn[i]!=incomingFaderMove[i]) || operatingMode==TARGET){
        dState[i]=faderValue[i]; 
        iState[i]=0; 
      }
      faderPidOn[i]=  incomingFaderMove[i];
      // update fader target
      faderTarget[i]=incomingTargetValue[i];
    }
    // reset timer
    sinceLastCommand=0;
  }
}

// handle Tx Event (outgoing I2C data)
void requestEvent(void)
{
  faderValueData[16]=0;
  for (int i=0;i<8;i++){
    // split value integers over two bytes in array
    faderValueData[(i*2)]=(faderValue[i] >> 8) & 0xFF;
    faderValueData[(i*2)+1]=faderValue[i] & 0xFF;
    // fill last byte in array with touch boolean array
    faderValueData[16] |= faderTouched[i] << i;
  }
  Wire.write(faderValueData, MESSAGE_LEN); // fill Tx buffer (send full mem)
}

my final goal is send PROTOOLS faders data to motor faders thru usbmidi and receive data from motor faders to PROTOOLS with faders touch sens press / release command in the highest speed response i can get and best stability.
soon ill upload the assembled pcb and ill up load the codes im gonna use to get ur opinion before i run it again,

thank u all
Rota
 
Last edited:
An actual schematic would be required to comment on this, not just the PCB layout. My first guess would be that you connected the I2C pull up resistors to 5V in stead of 3.3V because there's no way sending data can destroy a Teensy.
 
An actual schematic would be required to comment on this, not just the PCB layout. My first guess would be that you connected the I2C pull up resistors to 5V in stead of 3.3V because there's no way sending data can destroy a Teensy.

thank you gerrit for your fast replay
i connected the pullup resistor to the 3V inthe master teensy.
just for mention master teensy got the 5v power from the usb cable and the slave got the power from psu after i cut the usb power trace as wirte on the teensy manual and all works ok.

here is my schematic:
https://drive.google.com/file/d/1sjNHtPpB1PZyUcDGTmzxbnawlNORVtsJ/view?usp=sharing
 
Last edited:
in time im waiting for parts
im trying to think how the master code should be handle usb midi i guess its need to be somthing like this:

Code:
// -------------------------------------------------------------------------------------------
// Basic Master
// -------------------------------------------------------------------------------------------
//
// This creates a simple I2C Master device which when triggered will send/receive a text
// string to/from a Slave device.  It is intended to pair with a Slave device running the
// basic_slave sketch.
//
// Pull pin12 input low to send.
// Pull pin11 input low to receive.
//
// This example code is in the public domain.
//
// -------------------------------------------------------------------------------------------
#include <MIDIUSB.h>
#include <i2c_t3.h>

// Memory
#define MESSAGE_LEN 17
uint8_t PANEL_ADDR = 0x66;
char databuf[MESSAGE_LEN];

int count;
int     faderTargetData[8];
boolean faderMove[8];
int     faderTarget[8];
byte lo[8];
byte hi[8];
byte Gain[8];

void setup()
{

  // Setup for Master mode, pins 18/19, external pullups, 400kHz, 200ms default timeout
  Wire.begin(I2C_MASTER, 0x00, I2C_PINS_18_19, I2C_PULLUP_EXT, 400000);
  Wire.setDefaultTimeout(200000); // 200ms

  // Data init
  memset(databuf, 0, sizeof(databuf));
  count = 0;

  Serial.begin(115200);
}

void loop()
{
  sendFaderCommand(MESSAGE_LEN);
  //    if(digitalRead(11) == LOW)
  //    {
  //        digitalWrite(LED_BUILTIN,HIGH);   // LED on
  //
  //        // Print message
  //        Serial.print("Reading from Slave: ");
  //
  //        // Read from Slave
  //        Wire.requestFrom(PANEL_ADDR, (size_t)MESSAGE_LEN); // Read from Slave (string len unknown, request full buffer)
  //
  //        // Check if error occured
  //        if(Wire.getError())
  //            Serial.print("FAIL\n");
  //        else
  //        {
  //            // If no error then read Rx data into buffer and print
  //            Wire.read(databuf, Wire.available());
  //            Serial.printf("'%s' OK\n",databuf);
  //        }
  //
  //        digitalWrite(LED_BUILTIN,LOW);    // LED off
  //        delay(100);                       // Delay to space out tests
  //    }
}
void sendFaderCommand(byte mode) {
  size_t idx;
  faderTargetData[16] = 0;
  for (int i = 0; i < 8; i++) {
    // split Target integers over two bytes in array
    faderTargetData[(i * 2)] = (faderTarget[i] >> 8) & 0xFF;
    faderTargetData[(i * 2) + 1] = faderTarget[i] & 0xFF;
    // fill last byte in array with faderMove boolean array
    faderTargetData[16] |= faderMove[i] << i;
  }
  // Transmit to Slave
  Wire.beginTransmission(PANEL_ADDR);   // Slave address
  // Send mode
  Wire.write(mode);
  // Send target data
  for (idx = 0; idx <= MESSAGE_LEN; idx++) // Write data to I2C Tx buffer
    Wire.write(faderTargetData[idx]);
  Wire.endTransmission();           // Transmit to Slave
}
void myControlChange(byte channel, byte control, byte value) {               // blink the LED a number of times
  value = map(value, 0, 127, 127, 0);
  //INT FADERS GAINS
  if (control == 32) {
    hi[0] = value;
  }
  if (control == 0) {
    lo[0] = value;
    Gain[0] = (hi[0] >> 7) + lo[0];
    faderTargetData[0] = Gain[0];
  }

  if (control == 33) {
    hi[1] = value;
  }
  if (control == 1) {
    lo[1] = value;
    Gain[1] = (hi[1] >> 7) + lo[1];
    faderTargetData[1] = Gain[1];
  }
  if (control == 34) {
    hi[2] = value;
  }
  if (control == 2) {
    lo[2] = value;
    Gain[2] = (hi[2] >> 7) + lo[2];
    faderTargetData[2] = Gain[2];
  }

  if (control == 35) {
    hi[3] = value;
  }
  if (control == 3) {
    lo[3] = value;
    Gain[3] = (hi[3] >> 7) + lo[3];
    faderTargetData[3] = Gain[3];
  }

  if (control == 36) {
    hi[4] = value;
  }
  if (control == 4) {
    lo[4] = value;
    Gain[4] = (hi[4] >> 7) + lo[4];
    faderTargetData[4] = Gain[4];
  }

  if (control == 37) {
    hi[5] = value;
  }
  if (control == 5) {
    lo[5] = value;
    Gain[5] = (hi[5] >> 7) + lo[5];
    faderTargetData[5] = Gain[5];
  }

  if (control == 38) {
    hi[6] = value;
  }
  if (control == 6) {
    lo[6] = value;
    Gain[6] = (hi[6] >> 7) + lo[6];
    faderTargetData[6] = Gain[6];
  }

  if (control == 39) {
    hi[7] = value;
  }
  if (control == 7) {
    lo[7] = value;
    Gain[7] = (hi[7] >> 7) + lo[7];
    faderTargetData[7] = Gain[7];
  }
}
 
thank you gerrit for your fast replay
i connected the pullup resistor to the 3V inthe master teensy.
just for mention master teensy got the 5v power from the usb cable and the slave got the power from psu after i cut the usb power trace as wirte on the teensy manual and all works ok.

here is my schematic:
https://drive.google.com/file/d/1sjNHtPpB1PZyUcDGTmzxbnawlNORVtsJ/view?usp=sharing

You really should use labels in your schematic in stead of hooking everything up with lines because it's not very readable like this.
It's not clear to me what the status is, does it all work now? No more issues with dead chips?

With regards to Protools, I don't know how (and how fast) Protools sends the fader data so I can't comment on that yet.
Please keep in mind that the panel was developed for fast preset recall for use with a MIDI step sequencer and not for slowly following fades, the PID control would probably need to be adapted for that purpose (tuning parameters).
 
You really should use labels in your schematic in stead of hooking everything up with lines because it's not very readable like this.
It's not clear to me what the status is, does it all work now? No more issues with dead chips?

With regards to Protools, I don't know how (and how fast) Protools sends the fader data so I can't comment on that yet.
Please keep in mind that the panel was developed for fast preset recall for use with a MIDI step sequencer and not for slowly following fades, the PID control would probably need to be adapted for that purpose (tuning parameters).

you right about the labels . sorrry its the first time i try to do schem on EASY EDA.
now after the last chip is dead i take all apart and im witing now for the pcb.
and im waitng for new teensy to arrive from ebay and im gonna build everything again.
so nothing work now.
about protools movment ill take care for that tuning part at the end after communication will work good both side.
thanks to you its gonna be less hard to do it coz the pid for 8 motors its allredy in your code.
 
about protools hui midi protocol.
I know the protocol works and how he send the data like in my example under control change void
I test this void in all of my test and it works and send data to faders as aspected.
I also test send wiper value to protocols and it works and move the fader in the DAW.
Like I said before when I was trying to combine the send receive void's in my test Its always stack's the slave or the master teensy I did so many test in midi USB to faders
and midi USB to serial to slave to fader.
But I didn't figure out how to get smooth send receive for for read and wirte faders.
Should I use the regular master sketch example for this use or advanced master example or maybe loopback?
Should I send receive 17 bytes all the time non stop? If yes how can I do it in the right way?
 
This is the code I currently have in the master:

Code:
// get fader values and touch status
void getFaderData() {
  size_t idx;
  int incomingFaderValue[16];    // 
  int touchCount=0;
  ZeusPattern *currentPattern=&sequencerChannel[selectedChannel].pattern[sequencerChannel[selectedChannel].selectedPattern];
  // Read from Slave
  for (int j=0;j<2;j++){
    Wire.requestFrom(panelAddress[j], (size_t)MESSAGE_LEN); // Read from Slave
    // Check if error occured
    if(Wire.getError())
        Serial.print("FAIL\n");
    else
    {
      // If no error then read Rx data into buffer
      idx = 0;
      while(Wire.available()) faderValueData[idx++] = Wire.readByte();
      // Process data in buffer
      for (int i=0;i<8;i++){
      // combine bytes back to integers and put in temporary array
      incomingFaderValue[i]= faderValueData[(i*2)];
      incomingFaderValue[i]= incomingFaderValue[i] << 8 | faderValueData[(i*2)+1];
      // fill touch boolean array with last byte
      faderTouched[i+j*8] = 1 & faderValueData[16] >> i;
      if (faderTouched[i+j*8]) touchCount++;
      // check if faderValue needs to be updated
      if (incomingFaderValue[i]!=faderValue[i+j*8] and faderTouched[i+j*8]){
        faderValue[i+j*8]=incomingFaderValue[i];
        currentPattern->step[i+j*8].value[selectedParameter]=map(faderValue[i+j*8],0,1023,parameterLimit[selectedParameter][0],parameterLimit[selectedParameter][1]);
        updateDisplayData=true;
//        Serial.print("Fader ");
//        Serial.print(i+j*8);
//        Serial.print("\t");
//        Serial.println(faderValue[i+j*8]);
      }
    }
  }
  faderTouchCount=touchCount;
  }
}

// Send command with faderTarget array values to fader, TARGET or FOLLOW mode
void sendFaderCommand(byte mode){
  size_t idx;
  faderTargetData[16]=0;
  for (int j=0;j<2;j++){
    for (int i=0;i<8;i++){
      faderTargetData[(i*2)]=(faderTarget[i+j*8] >> 8) & 0xFF;                // split Target integers over two bytes in array
      faderTargetData[(i*2)+1]=faderTarget[i+j*8] & 0xFF;
      faderTargetData[16] |= faderMove[i+j*8] << i;                           // fill last byte in array with faderMove boolean array
    }
    Wire.beginTransmission(panelAddress[j]);                                  // Transmit to Slave
    Wire.write(mode);                                                         // Send mode (TARGET or FOLLOW)
    for(idx = 0; idx < MESSAGE_LEN; idx++) Wire.write(faderTargetData[idx]);  // Write data to I2C Tx buffer
    Wire.endTransmission();                                                   // Transmit to Slave
  }
}

There're two panels connected to the master getFaderData() is called from within the main loop:
Code:
  //  substract interval to maintain schedule
  // faders
  if (sinceFaderRead >=faderReadInterval) {
    sinceFaderRead = sinceFaderRead - faderReadInterval;
    getFaderData();
    if (linkMode!=LINK_OFF) handleLinkMode();
  }
The fader read interval is set at 5ms. The I2C communication is setup for 1Mhz:
Code:
  //**** I2C *****
  // Setup for Master mode, pins 18/19, external pullups, 1MHz, 200ms default timeout
  Wire.begin(I2C_MASTER, 0x00, I2C_PINS_18_19, I2C_PULLUP_EXT, 1000000);
  Wire.setDefaultTimeout(200000); // 200ms

The getFaderData() function takes just under 0.5ms for sending requests to two panels and storing the responses. You should be able to just call sendFaderCommand() from your MIDI event handler function.

Sometimes I get I2C communication errors on startup, I haven't figured out yet what the cause is but it only happens on startup. Once started everything works just fine.
 
This is the code I currently have in the master:

Code:
// get fader values and touch status
void getFaderData() {
  size_t idx;
  int incomingFaderValue[16];    // 
  int touchCount=0;
  ZeusPattern *currentPattern=&sequencerChannel[selectedChannel].pattern[sequencerChannel[selectedChannel].selectedPattern];
  // Read from Slave
  for (int j=0;j<2;j++){
    Wire.requestFrom(panelAddress[j], (size_t)MESSAGE_LEN); // Read from Slave
    // Check if error occured
    if(Wire.getError())
        Serial.print("FAIL\n");
    else
    {
      // If no error then read Rx data into buffer
      idx = 0;
      while(Wire.available()) faderValueData[idx++] = Wire.readByte();
      // Process data in buffer
      for (int i=0;i<8;i++){
      // combine bytes back to integers and put in temporary array
      incomingFaderValue[i]= faderValueData[(i*2)];
      incomingFaderValue[i]= incomingFaderValue[i] << 8 | faderValueData[(i*2)+1];
      // fill touch boolean array with last byte
      faderTouched[i+j*8] = 1 & faderValueData[16] >> i;
      if (faderTouched[i+j*8]) touchCount++;
      // check if faderValue needs to be updated
      if (incomingFaderValue[i]!=faderValue[i+j*8] and faderTouched[i+j*8]){
        faderValue[i+j*8]=incomingFaderValue[i];
        currentPattern->step[i+j*8].value[selectedParameter]=map(faderValue[i+j*8],0,1023,parameterLimit[selectedParameter][0],parameterLimit[selectedParameter][1]);
        updateDisplayData=true;
//        Serial.print("Fader ");
//        Serial.print(i+j*8);
//        Serial.print("\t");
//        Serial.println(faderValue[i+j*8]);
      }
    }
  }
  faderTouchCount=touchCount;
  }
}

// Send command with faderTarget array values to fader, TARGET or FOLLOW mode
void sendFaderCommand(byte mode){
  size_t idx;
  faderTargetData[16]=0;
  for (int j=0;j<2;j++){
    for (int i=0;i<8;i++){
      faderTargetData[(i*2)]=(faderTarget[i+j*8] >> 8) & 0xFF;                // split Target integers over two bytes in array
      faderTargetData[(i*2)+1]=faderTarget[i+j*8] & 0xFF;
      faderTargetData[16] |= faderMove[i+j*8] << i;                           // fill last byte in array with faderMove boolean array
    }
    Wire.beginTransmission(panelAddress[j]);                                  // Transmit to Slave
    Wire.write(mode);                                                         // Send mode (TARGET or FOLLOW)
    for(idx = 0; idx < MESSAGE_LEN; idx++) Wire.write(faderTargetData[idx]);  // Write data to I2C Tx buffer
    Wire.endTransmission();                                                   // Transmit to Slave
  }
}

There're two panels connected to the master getFaderData() is called from within the main loop:
Code:
  //  substract interval to maintain schedule
  // faders
  if (sinceFaderRead >=faderReadInterval) {
    sinceFaderRead = sinceFaderRead - faderReadInterval;
    getFaderData();
    if (linkMode!=LINK_OFF) handleLinkMode();
  }
The fader read interval is set at 5ms. The I2C communication is setup for 1Mhz:
Code:
  //**** I2C *****
  // Setup for Master mode, pins 18/19, external pullups, 1MHz, 200ms default timeout
  Wire.begin(I2C_MASTER, 0x00, I2C_PINS_18_19, I2C_PULLUP_EXT, 1000000);
  Wire.setDefaultTimeout(200000); // 200ms

The getFaderData() function takes just under 0.5ms for sending requests to two panels and storing the responses. You should be able to just call sendFaderCommand() from your MIDI event handler function.

Sometimes I get I2C communication errors on startup, I haven't figured out yet what the cause is but it only happens on startup. Once started everything works just fine.

amazing. thank you very much gerrit
thats for sure gonna help me. ill dig it and ill try to understand how should i write my code .
soon when all the new parts will arrive ill post some new photos with the new pcb and the system and ill post the sketch im gonna test first.
 
so...
i got the pcb and i started to solder the mount pins and the pullups 4.6K resistors on the back side from pins 18 19 to 3V pin.
like i said here before i i have mistake on this bord so i solder 2 lines from pins 18 19 in the master to pins 33 34 on the slave to use i2c connection.
here is the Back side Pic of PCB:
https://drive.google.com/file/d/1l4OyU1S1jIT-n27TQPRuOU9bkytJOJtc/view?usp=sharing

4k6 seems a bit high to me, master and slave are on the same board but still why not use a something like 2k2 or 1k8? Do you have (access to) an oscilloscope so you can check the I2C signal to verify that the rise time is sufficiently low (120 ns for 1MHz)?
 
4k6 seems a bit high to me, master and slave are on the same board but still why not use a something like 2k2 or 1k8? Do you have (access to) an oscilloscope so you can check the I2C signal to verify that the rise time is sufficiently low (120 ns for 1MHz)?

i used info from here:
https://www.pjrc.com/teensy/td_libs_Wire.html

Teensy LC & 3.0-3.6 requires pullup resistors to +3.3V. The on-chip pullups are not used. 4.7K resistors are recommended for most applications.

that could make any damage to use 4.7K? what resistor size u use for pullup resistors?
 
4k6 seems a bit high to me, master and slave are on the same board but still why not use a something like 2k2 or 1k8? Do you have (access to) an oscilloscope so you can check the I2C signal to verify that the rise time is sufficiently low (120 ns for 1MHz)?

i search on web and i also find paul stoffregen recommendation to use 1K to 2k2 resistors for SDA SCL .
but why the pjrc site recommends for 4k7?
i must say that in my last test it works with the 4k7. im a bit confused.
i find some 2k2 value in my stock and also 10K value.
now i wonder what is the best way... to connect the 2k2 to 3.3v from the teensy. or 10K to 5V from the PSU?
 
Last edited:
i search on web and i also find paul stoffregen recommendation to use 1K to 2k2 resistors for SDA SCL .
but why the pjrc site recommends for 4k7?
The ideal resistance a factor of bus speed and voltage. Possibly the PJRC pages not linked was in reference to 5V T_2's? The 3.3V power of the T_LC and 3.x's are different and a lower value can allow faster bus speed transitions.
 
The ideal resistance a factor of bus speed and voltage. Possibly the PJRC pages not linked was in reference to 5V T_2's? The 3.3V power of the T_LC and 3.x's are different and a lower value can allow faster bus speed transitions.

i change my resistors to 2K2 connected to 3.3v pin of the master teensy. i hope that will be ok.

the PJRC page talk about reference to 3.3V
 
i search on web and i also find paul stoffregen recommendation to use 1K to 2k2 resistors for SDA SCL .
but why the pjrc site recommends for 4k7?
i must say that in my last test it works with the 4k7. im a bit confused.
i find some 2k2 value in my stock and also 10K value.
now i wonder what is the best way... to connect the 2k2 to 3.3v from the teensy. or 10K to 5V from the PSU?

First, always connect the pull up resistors to 3.3V when using a Teensy 3.6! Second, a too high value of the pull up resistors cannot damage a Teensy (a much too low value could).

The best way is to use an oscilloscope to check the shape of the pulses, that's why I asked if you have (access to) one. The value of the pull up resistors depends on the operating voltage and the I2C speed. My guess would be that the 4k7 recommendation refers to 5V operation and 100kHz clock (normal I2C speed). If it works in your case and you have reliable communication there's no need to lower the value. The thing is that without using an oscilloscope to check the pulse shape you do not know if you're close to the edge of what is required which might lead to intermittent errors, hence my suggestion to use 2k2 or 1k8 just to be on the safe side.

My case is very different from yours because I have two slaves connected via a cable to the master and I wanted to use the highest speed possible, this requires very low values for the pull ups because of the high(er) capacitance of the cables.
Here's a comparison of three different values for the pull up resistors:

I2C_PullUp_Compare.jpg

The I2C speed was 2.5MHz (notice that the pulse frequency is actually just over 1MHz). The 1k8 resistors cannot pull up the voltage fast enough and even the minimum I2C spec value of 980Ω (approximate, for 3mA sink current) is not sufficient so I ended up increasing the sink current to about 5mA by using 640Ω as there are no other I2C devices on the bus, just three Teensies. Even with 640Ω the communication was not reliable at 2.5MHz so I reduced the speed to 1MHz and now everything is working fine.
Note that this is for my specific case with two slaves and long cables, for your case 2k2 should be fine, it's just to show how the pull up resistor values affect the pulse shape.
 
First, always connect the pull up resistors to 3.3V when using a Teensy 3.6! Second, a too high value of the pull up resistors cannot damage a Teensy (a much too low value could).

The best way is to use an oscilloscope to check the shape of the pulses, that's why I asked if you have (access to) one. The value of the pull up resistors depends on the operating voltage and the I2C speed. My guess would be that the 4k7 recommendation refers to 5V operation and 100kHz clock (normal I2C speed). If it works in your case and you have reliable communication there's no need to lower the value. The thing is that without using an oscilloscope to check the pulse shape you do not know if you're close to the edge of what is required which might lead to intermittent errors, hence my suggestion to use 2k2 or 1k8 just to be on the safe side.

My case is very different from yours because I have two slaves connected via a cable to the master and I wanted to use the highest speed possible, this requires very low values for the pull ups because of the high(er) capacitance of the cables.
Here's a comparison of three different values for the pull up resistors:

View attachment 15922

The I2C speed was 2.5MHz (notice that the pulse frequency is actually just over 1MHz). The 1k8 resistors cannot pull up the voltage fast enough and even the minimum I2C spec value of 980Ω (approximate, for 3mA sink current) is not sufficient so I ended up increasing the sink current to about 5mA by using 640Ω as there are no other I2C devices on the bus, just three Teensies. Even with 640Ω the communication was not reliable at 2.5MHz so I reduced the speed to 1MHz and now everything is working fine.
Note that this is for my specific case with two slaves and long cables, for your case 2k2 should be fine, it's just to show how the pull up resistor values affect the pulse shape.

thank for your deep replay gerrit ill dig it as allways
i have an old oscilloscope i m not realy sure how to mesure it but ill try to do it when all the board will be soldered and working.
for now im going with the 2K2 ilke you said. soon ill upload some pics of the panel with the screens and the internal view.
many thanks
 
If you're buying I2C displays, they may also have pullup resistors.

Usually you would want to combined (parallel) resistance to be between 1K to 5K.
 
This is the code I currently have in the master:

Code:
// get fader values and touch status
void getFaderData() {
  size_t idx;
  int incomingFaderValue[16];    // 
  int touchCount=0;
  ZeusPattern *currentPattern=&sequencerChannel[selectedChannel].pattern[sequencerChannel[selectedChannel].selectedPattern];
  // Read from Slave
  for (int j=0;j<2;j++){
    Wire.requestFrom(panelAddress[j], (size_t)MESSAGE_LEN); // Read from Slave
    // Check if error occured
    if(Wire.getError())
        Serial.print("FAIL\n");
    else
    {
      // If no error then read Rx data into buffer
      idx = 0;
      while(Wire.available()) faderValueData[idx++] = Wire.readByte();
      // Process data in buffer
      for (int i=0;i<8;i++){
      // combine bytes back to integers and put in temporary array
      incomingFaderValue[i]= faderValueData[(i*2)];
      incomingFaderValue[i]= incomingFaderValue[i] << 8 | faderValueData[(i*2)+1];
      // fill touch boolean array with last byte
      faderTouched[i+j*8] = 1 & faderValueData[16] >> i;
      if (faderTouched[i+j*8]) touchCount++;
      // check if faderValue needs to be updated
      if (incomingFaderValue[i]!=faderValue[i+j*8] and faderTouched[i+j*8]){
        faderValue[i+j*8]=incomingFaderValue[i];
        currentPattern->step[i+j*8].value[selectedParameter]=map(faderValue[i+j*8],0,1023,parameterLimit[selectedParameter][0],parameterLimit[selectedParameter][1]);
        updateDisplayData=true;
//        Serial.print("Fader ");
//        Serial.print(i+j*8);
//        Serial.print("\t");
//        Serial.println(faderValue[i+j*8]);
      }
    }
  }
  faderTouchCount=touchCount;
  }
}

// Send command with faderTarget array values to fader, TARGET or FOLLOW mode
void sendFaderCommand(byte mode){
  size_t idx;
  faderTargetData[16]=0;
  for (int j=0;j<2;j++){
    for (int i=0;i<8;i++){
      faderTargetData[(i*2)]=(faderTarget[i+j*8] >> 8) & 0xFF;                // split Target integers over two bytes in array
      faderTargetData[(i*2)+1]=faderTarget[i+j*8] & 0xFF;
      faderTargetData[16] |= faderMove[i+j*8] << i;                           // fill last byte in array with faderMove boolean array
    }
    Wire.beginTransmission(panelAddress[j]);                                  // Transmit to Slave
    Wire.write(mode);                                                         // Send mode (TARGET or FOLLOW)
    for(idx = 0; idx < MESSAGE_LEN; idx++) Wire.write(faderTargetData[idx]);  // Write data to I2C Tx buffer
    Wire.endTransmission();                                                   // Transmit to Slave
  }
}

There're two panels connected to the master getFaderData() is called from within the main loop:
Code:
  //  substract interval to maintain schedule
  // faders
  if (sinceFaderRead >=faderReadInterval) {
    sinceFaderRead = sinceFaderRead - faderReadInterval;
    getFaderData();
    if (linkMode!=LINK_OFF) handleLinkMode();
  }
The fader read interval is set at 5ms. The I2C communication is setup for 1Mhz:
Code:
  //**** I2C *****
  // Setup for Master mode, pins 18/19, external pullups, 1MHz, 200ms default timeout
  Wire.begin(I2C_MASTER, 0x00, I2C_PINS_18_19, I2C_PULLUP_EXT, 1000000);
  Wire.setDefaultTimeout(200000); // 200ms

The getFaderData() function takes just under 0.5ms for sending requests to two panels and storing the responses. You should be able to just call sendFaderCommand() from your MIDI event handler function.

Sometimes I get I2C communication errors on startup, I haven't figured out yet what the cause is but it only happens on startup. Once started everything works just fine.

hi gerrit

i see in your master code that u use:

Wire.begin(I2C_MASTER, 0x00, I2C_PINS_18_19, I2C_PULLUP_EXT, 1000000);
Wire.setDefaultTimeout(200000); // 200ms

and the slave is:

Wire.begin(I2C_SLAVE, PANEL_ADDR, I2C_PINS_33_34, I2C_PULLUP_EXT, 400000);

that is ok to use it like that? or should i change the speed in the slave to 1000000?
i looked in the master slave exemples and its the same speed in master and slave codes.

tnx
rota
 
You should change it to 1000000, that's how it's configured now, the 400000 is from the earlier version with the test panel.
 
i tryed to write the master code but im not sure about few things
first under "void sendFaderCommand" its use "faderTarget" and the "faderMove"

thats my master code:

Code:
#include <i2c_t3.h>
#include <MIDIUSB.h>

#define TARGET    0x10              // All faders to target value as fast as possible. PID state is cleared with each command.
#define FOLLOW    0x20
#define MESSAGE_LEN 17

elapsedMicros sinceFaderRead;                         // timer for fader check
unsigned int  faderReadInterval = 1000;
elapsedMicros faderTouchCount;

uint8_t panelAddress = 0x66;

byte faderValueData[MESSAGE_LEN];    // 16 bytes for values, 1 byte for touch
byte faderTargetData[MESSAGE_LEN];   // 16 for target values, 1 byte for indicating which faders should be moved.

boolean faderTouched[8];                              // boolean indicating if fader is touched
int     faderValue[8];                                // fader value ranging 0 to 1023
int     faderTarget[8];                               // fader target value ranging 0 to 1023
int     faderMove[8];                               // fader target value ranging 0 to 1023


int hi[8];
int lo[8];
int Gain[8];


void setup() {
//**** I2C *****
  // Setup for Master mode, pins 18/19, external pullups, 1MHz, 200ms default timeout
  Wire.begin(I2C_MASTER, 0x00, I2C_PINS_18_19, I2C_PULLUP_EXT, 1000000);
  Wire.setDefaultTimeout(200000); // 200ms
  usbMIDI.setHandleControlChange(myControlChange);

}

void loop() {
  usbMIDI.read();
  //  substract interval to maintain schedule
  // faders
  if (sinceFaderRead >=faderReadInterval) {
    sinceFaderRead = sinceFaderRead - faderReadInterval;
    getFaderData();
    //if (linkMode!=LINK_OFF) handleLinkMode();
   
  }
}
// get fader values and touch status
void getFaderData() {
  size_t idx;
  int incomingFaderValue[16];    // 
  int touchCount=0;
  //ZeusPattern *currentPattern=&sequencerChannel[selectedChannel].pattern[sequencerChannel[selectedChannel].selectedPattern];
  // Read from Slave
  
    Wire.requestFrom(panelAddress, (size_t)MESSAGE_LEN); // Read from Slave
    // Check if error occured
    if(Wire.getError())
        Serial.print("FAIL\n");
    else
    {
      // If no error then read Rx data into buffer
      idx = 0;
      while(Wire.available()) faderValueData[idx++] = Wire.readByte();
      // Process data in buffer
      for (int i=0;i<8;i++){
      // combine bytes back to integers and put in temporary array
      incomingFaderValue[i]= faderValueData[(i*2)];
      incomingFaderValue[i]= incomingFaderValue[i] << 8 | faderValueData[(i*2)+1];
      // fill touch boolean array with last byte
      faderTouched[i*8] = 1 & faderValueData[16] >> i;
      if (faderTouched[i*8]) touchCount++;
      // check if faderValue needs to be updated
      if (incomingFaderValue[i]!=faderValue[i*8] and faderTouched[i*8]){
        faderValue[i*8]=incomingFaderValue[i];
//        currentPattern->step[i+j*8].value[selectedParameter]=map(faderValue[i+j*8],0,1023,parameterLimit[selectedParameter][0],parameterLimit[selectedParameter][1]);
//        updateDisplayData=true;
//        Serial.print("Fader ");
//        Serial.print(i+j*8);
//        Serial.print("\t");
//        Serial.println(faderValue[i+j*8]);     
    }
  }
  faderTouchCount=touchCount;
  }
}

// Send command with faderTarget array values to fader, TARGET or FOLLOW mode
void sendFaderCommand(byte mode){
  size_t idx;
  faderTargetData[16]=0;
    for (int i=0;i<8;i++){
      faderTargetData[(i*2)]=(faderTarget[i*8] >> 8) & 0xFF;                // split Target integers over two bytes in array
      faderTargetData[(i*2)+1]=faderTarget[i*8] & 0xFF;
      faderTargetData[16] |= faderMove[i*8] << i;                           // fill last byte in array with faderMove boolean array
    }
    Wire.beginTransmission(panelAddress);                                  // Transmit to Slave
    Wire.write(mode);                                                         // Send mode (TARGET or FOLLOW)
    for(idx = 0; idx < MESSAGE_LEN; idx++) Wire.write(faderTargetData[idx]);  // Write data to I2C Tx buffer
    Wire.endTransmission();                                                   // Transmit to Slave
}
void myControlChange(byte channel, byte control, byte value) {               // blink the LED a number of times

  value = map(value, 0, 127, 127, 0);
  //INT FADERS GAINS
  if (control == 32) {
    hi[0] = value;
  }
  if (control == 0) {
    lo[0] = value;
    Gain[0] = (hi[0] >> 7) + lo[0];
  }

  if (control == 33) {
    hi[1] = value;
  }
  if (control == 1) {
    lo[1] = value;
    Gain[1] = (hi[1] >> 7) + lo[1];
  }
  if (control == 34) {
    hi[2] = value;
  }
  if (control == 2) {
    lo[2] = value;
    Gain[2] = (hi[2] >> 7) + lo[2];
  }

  if (control == 35) {
    hi[3] = value;
  }
  if (control == 3) {
    lo[3] = value;
    Gain[3] = (hi[3] >> 7) + lo[3];
  }

  if (control == 36) {
    hi[4] = value;
  }
  if (control == 4) {
    lo[4] = value;
    Gain[4] = (hi[4] >> 7) + lo[4];
  }

  if (control == 37) {
    hi[5] = value;
  }
  if (control == 5) {
    lo[5] = value;
    Gain[5] = (hi[5] >> 7) + lo[5];
  }

  if (control == 38) {
    hi[6] = value;
  }
  if (control == 6) {
    lo[6] = value;
    Gain[6] = (hi[6] >> 7) + lo[6];
  }

  if (control == 39) {
    hi[7] = value;
  }
  if (control == 7) {
    lo[7] = value;
    Gain[7] = (hi[7] >> 7) + lo[7];
  }
  sendFaderCommand(FOLLOW);
}

my midi fader value is the "int Gain" so should it be like this ? :
Code:
void sendFaderCommand(byte mode){
  size_t idx;
  faderTargetData[16]=0;
    for (int i=0;i<8;i++){
      faderTargetData[(i*2)]=(Gain[i*8] >> 8) & 0xFF;                // split Target integers over two bytes in array
      faderTargetData[(i*2)+1]=Gain[i*8] & 0xFF;
      faderTargetData[16] |= Gain[i*8] << i;                           // fill last byte in array with faderMove boolean array
    }
    Wire.beginTransmission(panelAddress);                                  // Transmit to Slave
    Wire.write(mode);                                                         // Send mode (TARGET or FOLLOW)
    for(idx = 0; idx < MESSAGE_LEN; idx++) Wire.write(faderTargetData[idx]);  // Write data to I2C Tx buffer
    Wire.endTransmission();                                                   // Transmit to Slave
}

and about the "sendFaderCommand(FOLLOW);" i puted under the "void myControlChange"
did i use it right? it should be FOLLOW in there?
can u guys please look at it and tell me if im doing ok?



here is the slave code the only change i have made is in (I2C_PULLUP_EXT, 1000000) :
Code:
/*
  Zeus Commander Controller Series
  I2C controlled motorfader octet

  Full PID motor control supporting two operating modes:
  TARGET  for fast preset recall
  FOLLOW  for slow to fast following of values

  This code expands on the examples provided with the i2c_t3 library for the I2Ccommunication

  This code is in the public domain.
*/
#include <i2c_t3.h>
#include <ResponsiveAnalogRead.h>

// PID arrays
float dState[8];
float iState[8];
float iMin[8];
float iMax[8];
float pGain[8];
float iGain[8];
float dGain[8];
float plantDrive[8];
float maxD = 0.00;     // maximum derivative value for debugging purposes

void UpdatePID(int faderIndex, float faderError, float faderPosition)
{
  float pTerm, dTerm, iTerm, plantDriveInput;
  // calculate the proportional term
  pTerm = pGain[faderIndex] * faderError;
  // calculate the integral state with appropriate limiting
  iState[faderIndex] += faderError;
  if (iState[faderIndex] > iMax[faderIndex]) {
    iState[faderIndex] = iMax[faderIndex];
  }
  else if (iState[faderIndex] < iMin[faderIndex]) {
    iState[faderIndex] = iMin[faderIndex];
  }
  iTerm = iGain[faderIndex] * iState[faderIndex];  // calculate the integral term
  dTerm = dGain[faderIndex] * (faderPosition - dState[faderIndex]);
  if (abs(faderPosition - dState[faderIndex]) > maxD) {
    maxD = abs(faderPosition - dState[faderIndex]);
  }
  dState[faderIndex] = faderPosition;

  plantDriveInput = pTerm + iTerm - dTerm;
  if (plantDriveInput > 4096) {
    plantDriveInput = 4096;
  }
  if (plantDriveInput < -4096) {
    plantDriveInput = -4096;
  }
  plantDrive[faderIndex] = plantDriveInput;
}

elapsedMicros sinceFaderRead;                         // timer for fader check
unsigned int  faderReadInterval = 1000;               // interval in microseconds for checking fader position(update)
elapsedMicros sinceTouchRead;                         // timer for touch read
unsigned int  touchReadInterval = 5000;               // interval in microseconds for reading fader touch
elapsedMillis sinceLastCommand;                       // timer for time-out of PIF after last command
unsigned int  commandTimeOutInterval = 500;           // interval for PID time-out in milliseconds
int     faderWiperPin[8] = {32, 31, 39, 20, 21, 15, A11, A10}; // analog in pin for fader wipers
int     faderTouchPin[8] = {1, 0, 16, 17, 18, 19, 22, 23};   // touchRead pins for fader touch
int     faderMotorUpPin[8] = {9, 29, 5, 7, 4, 2, 35, 37};    // motor up PWM pin
int     faderMotorDownPin[8] = {10, 30, 6, 8, 3, 14, 36, 38}; // motor down PWM pin
int     faderValue[8];                                // fader value ranging 0 to 1023
int     faderTarget[8];                               // fader target value ranging 0 to 1023
int     faderTouch[8];                                // fader touch reading
boolean faderPidOn[8];                                // status of PID
int     faderTouchThreshold = 4800;                   // threshold value for touchRead
boolean faderTouched[8];                              // boolean indicating if fader is touched

ResponsiveAnalogRead analog[8] {ResponsiveAnalogRead(32, true), ResponsiveAnalogRead(31, true),
                       ResponsiveAnalogRead(39, true), ResponsiveAnalogRead(20, true),
                       ResponsiveAnalogRead(21, true), ResponsiveAnalogRead(15, true),
                       ResponsiveAnalogRead(A11, true), ResponsiveAnalogRead(A10, true)
};

// I2C communication
// Command definitions
#define TARGET    0x10              // All faders to target value as fast as possible. PID state is cleared with each command.
#define FOLLOW    0x20              // Faders follow value, last byte in message is used to determine which motors should move.
// PIDs will time-out after last command.

// Function prototypes
void receiveEvent(size_t count);
void requestEvent(void);
#define PANEL_ADDR 0x66              // I2C address, should be selectable by jumper using leftover pins.
#define MESSAGE_LEN 17
byte faderValueData[MESSAGE_LEN];    // 16 bytes for values, 1 byte for touch
byte faderTargetData[MESSAGE_LEN];   // 16 for target values, 1 byte for indicating which faders should be moved.
volatile uint8_t received;
volatile uint8_t cmd;
byte operatingMode = TARGET;

// DEBUG
//elapsedMillis sinceSerialDebug; // timer for serial debug output

void setup()
{
  //Serial.begin(250000);

  //initialize faders and analog reference
  analogReference(EXTERNAL);
  analogWriteResolution(12);
  for (int i = 0; i < 8; i++) {
    pinMode(faderMotorUpPin[i], OUTPUT);
    pinMode(faderMotorDownPin[i], OUTPUT);
    analogWriteFrequency(faderMotorUpPin[i], 14648.437); // 14648.437 ideal frequency for 180MHz 12bit PWM
    analogWriteFrequency(faderMotorDownPin[i], 14648.437); //14648.437 ideal frequency for 180MHz 12bit PWM
    faderTouched[i] = false;
    faderValueData[i] = 0;
    faderTarget[i] = 500;
    faderPidOn[i] = false;
  }

  readFaders();
  // set PID tuning parameters
  for (int i = 0; i < 8; i++) {
    pGain[i] = 10;
    iGain[i] = 1;
    dGain[i] = 50.00;
    iMin[i] = -1500;
    iMax[i] = 1500;
  }

  // Setup for Slave mode pins 33/34, external pullups, 1MHz
  Wire.begin(I2C_SLAVE, PANEL_ADDR, I2C_PINS_33_34, I2C_PULLUP_EXT, 1000000);
  // register events
  Wire.onReceive(receiveEvent);
  Wire.onRequest(requestEvent);
  cmd = 0;    // incoming command operating mode
}

void loop()
{
  // check elapsedMicros and millis timers
  // faders
  if (sinceFaderRead >= faderReadInterval) {
    sinceFaderRead = sinceFaderRead - faderReadInterval;
    readFaders();

    // always update PID for timing consistency
    for (int i = 0; i < 8; i++) {
      UpdatePID(i, faderTarget[i] - faderValue[i], faderValue[i]);
    }

    // loop through faders and move where necessary
    for (int i = 0; i < 8; i++) {
      if (!faderTouched[i] && faderPidOn[i]) {
        if (plantDrive[i] > 0 && faderValue[i] <= 1022) {
          analogWrite(faderMotorDownPin[i], 0);
          analogWrite(faderMotorUpPin[i], plantDrive[i]);
        } else if (plantDrive[i] <= 0 && faderValue[i] >= 1) {
          analogWrite(faderMotorUpPin[i], 0);
          analogWrite(faderMotorDownPin[i], -plantDrive[i]);
        }
      }
      else if (!faderPidOn[i]) {
        analogWrite(faderMotorDownPin[i], 0);
        analogWrite(faderMotorUpPin[i], 0);
      }
    }
  }

  //  separate touchRead timer
  if (sinceTouchRead >= touchReadInterval) {
    sinceTouchRead = sinceTouchRead - touchReadInterval;
    readTouch();
  }

  // command time-out
  if (sinceLastCommand >= commandTimeOutInterval) {
    for (int i = 0; i < 8; i++) {
      faderPidOn[i] = false;
    }
    sinceLastCommand = 0;
  }

  // DEBUG serial
  //  if (sinceSerialDebug >= 100){
  //    sinceSerialDebug = sinceSerialDebug - 100;
  //    for (int i=0;i<8;i++){
  //      Serial.print(faderTarget[i]);
  //      Serial.print("\t");
  //    }
  //    Serial.print("\t");
  //    Serial.println();
  //  }
}

// read fader analog values and update faderValue array
void readFaders() {
  for (int i = 0; i < 8; i++) {
    analog[i].update();
    faderValue[i] = analog[i].getValue();
  }
}

// read fader touch
void readTouch() {
  for (int i = 0; i < 8; i++) {
    faderTouch[i] = touchRead(faderTouchPin[i]);
    if (faderTouch[i] > faderTouchThreshold) {
      faderTouched[i] = true;
    } else if (faderTouch[i] <= faderTouchThreshold) {
      faderTouched[i] = false;
    }
  }
}

// handle Rx Event (incoming I2C data)
void receiveEvent(size_t count) {
  size_t idx;
  int incomingTargetValue[8];
  boolean incomingFaderMove[8];
  Serial.println(incomingTargetValue[1]);
  if (count)
  {
    // grab command
    cmd = Wire.readByte();
    switch (cmd)
    {
      case TARGET:
        if (operatingMode == FOLLOW) {
          // set PID tuning parameters
          //        for (int i=0;i<8;i++){
          //          pGain[i]=20.00;
          //          iGain[i]=0.5;
          //          dGain[i]=40.00;
          //        }
          operatingMode = TARGET;
        }
        break;
      case FOLLOW:
        if (operatingMode == TARGET) {
          // set PID tuning parameters
          //        for (int i=0;i<8;i++){
          //          pGain[i]=20.00;
          //          iGain[i]=0.5;
          //          dGain[i]=40.00;
          //        }
          operatingMode = FOLLOW;
        }
        break;
    }
    idx = 0;
    while (Wire.available()) faderTargetData[idx++] = Wire.readByte();

    // Process data
    for (int i = 0; i < 8; i++) {
      // combine bytes back to integers and put in temporary array
      incomingTargetValue[i] = faderTargetData[(i * 2)];
      incomingTargetValue[i] = incomingTargetValue[i] << 8 | faderTargetData[(i * 2) + 1];
      // fill faderPidOn boolean array with last byte
      incomingFaderMove[i] = 1 & faderTargetData[16] >> i;
      // clear PID states if PID is switched on or if mode is TARGET
      if ((incomingFaderMove[i] && faderPidOn[i] != incomingFaderMove[i]) || operatingMode == TARGET) {
        dState[i] = faderValue[i];
        iState[i] = 0;
      }
      faderPidOn[i] =  incomingFaderMove[i];
      // update fader target
      faderTarget[i] = incomingTargetValue[i];
    }
    // reset timer
    sinceLastCommand = 0;
  }
}

// handle Tx Event (outgoing I2C data)
void requestEvent(void)
{
  faderValueData[16] = 0;
  for (int i = 0; i < 8; i++) {
    // split value integers over two bytes in array
    faderValueData[(i * 2)] = (faderValue[i] >> 8) & 0xFF;
    faderValueData[(i * 2) + 1] = faderValue[i] & 0xFF;
    // fill last byte in array with touch boolean array
    faderValueData[16] |= faderTouched[i] << i;
  }
  Wire.write(faderValueData, MESSAGE_LEN); // fill Tx buffer (send full mem)
}
 
Status
Not open for further replies.
Back
Top