Here is a video of automation test to read and write faders:
https://m.facebook.com/groups/246767148755072?view=permalink&id=2112554192176349
https://m.facebook.com/groups/246767148755072?view=permalink&id=2112554192176349
Sorry, I don't know anything about Protools or Studio One as I use Logic and Reaper.
Why do you send the same data to two controllers? Isn't that supposed to a NRPN controller where the value is split over two MIDI bytes? But like I said, I don't know how Protools is controlled. Logic uses the Mackie MCU protocol and this uses pitch bend to send the fader values.
I don't understand what you're saying. How can the faders jump to a position without getting a move command?hi gerrit
all faders moving well but i see that: when they jump to position, not all of them jump to the right place, but once they get the move commend all of them jump to the right place and start moving.
do u have any idea what parameters should i tune?
tnx
rota
U right first I'll go read the article.I don't understand what you're saying. How can the faders jump to a position without getting a move command?
BTW I don't have a facebook account so I can't watch the video.
With regards to tuning the fader control, I'm afraid you'll really have to read the PID without a Phd article referenced in the thread to get an understanding what the parameters do and what PID control is about. It's also difficult to give any advice without knowing exactly what the problem is.
All so sometimes in aoutomation reading the when faders got very hard commend of hard jump
Sometimes one of the faders got crazy for a sec and then keeps go back to position and keep the right track.
Here is a video of the problem
U can see in the video that faders 1-4 found the same thing in the program that means that that send the same values but the fader controller act difrent when I'm jump to point. I test high P gain and its makes the jumps a bit better coz its higherIf it's always the same fader that's behaving badly then I would check the wiring.
I can't determine what the actual problem is from this video, there's too much going on. Limit it to a single fader or have all faders do exactly the same thing.
You have to take a step back and systematically check the fader control and tune the PID parameters if needed.
If it's always the same fader that's behaving badly then I would check the wiring.
I can't determine what the actual problem is from this video, there's too much going on. Limit it to a single fader or have all faders do exactly the same thing.
You have to take a step back and systematically check the fader control and tune the PID parameters if needed.
i guess the problem is not in pid coz I try to send parameters in FOLLOW mode and its still not going to the exactly correct point when jump to start point But I'll dig itWhich fader control mode do you use? TARGET or FOLLOW? Have you tried the other mode to see what the difference is?
I'm sorry but I can't help you with the tuning other than giving some general advice. It took me some time to get it right for my setup and it still needs some finetuning. You need to get an understanding of how the parameters interact with each other by experimenting. I would start with concentrating on the control of the faders without any DAW control by writing some test functions like I did with the one that cycles through some values which is shown in one of the videos. The point is to check for overshoot (combination of p and d term) when the fader has to move a large distance on the one hand and check that the fader is able to move a little (p and i term) on the other.
Which fader control mode do you use? TARGET or FOLLOW? Have you tried the other mode to see what the difference is?
I'm sorry but I can't help you with the tuning other than giving some general advice. It took me some time to get it right for my setup and it still needs some finetuning. You need to get an understanding of how the parameters interact with each other by experimenting. I would start with concentrating on the control of the faders without any DAW control by writing some test functions like I did with the one that cycles through some values which is shown in one of the videos. The point is to check for overshoot (combination of p and d term) when the fader has to move a large distance on the one hand and check that the fader is able to move a little (p and i term) on the other.
/*
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 sinceFaderDebug; // timer for fader check
unsigned int faderDebugInterval= 50000;
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]={0,1,16,17,18,19,22,23}; // touchRead pins for fader touch
int faderMotorUpPin[8]= {10,29,5,7,3,14,35,37}; // motor up PWM pin
int faderMotorDownPin[8]={9,30,6,8,4,2,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(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]=0.5;
iGain[i]=20;
dGain[i]=2.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()
{
if (sinceFaderDebug >=faderDebugInterval) {
sinceFaderDebug = sinceFaderDebug-faderDebugInterval;
Debugfader();
}
// 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 (!faderTouched[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();
//Serial.println(faderValue[1]);
}
}
// 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];
//Serial.println(faderTarget[1]);
}
// 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)
}
void Debugfader(){
for (int i=0;i<8;i++){
if(faderTarget[i] != faderValue[i]){
faderPidOn[i] = true;
}
}
}