Monday, December 5, 2022

Twitchboard

 Previously I worked on a motor controller board that would deliver random pulses to a prop. It worked well, but I was not quite happy with the randomization algorithm, and I wanted more titratable control over the pulses. Additionally I wanted something that could be triggered by either a PIR sensor or a button.  

I’ve now produced a beta version of this new controller and I’m looking for feedback. Here’s a demo video—




This is the schematic—





And here is the code—

//  Jekyll-Labs Twitchy Attiny85
//  Built for Twitchboard v1.5
//
int fetpin = 0;
int trigpin = 1;
int potMpin = A1;
int potRpin = A2;
int potLpin = A3;
int lambda = 5; // number of event
int timeunit = 60; // in this time interval (sec)
unsigned long last = 0;
unsigned long timeinterval = 0;
float exprob=0;

void setup() {
  randomSeed(A1);
  pinMode(fetpin, OUTPUT);
  digitalWrite(fetpin, LOW);
  pinMode(trigpin, INPUT);
  pinMode(potLpin, INPUT);
  pinMode(potMpin, INPUT);
  pinMode(potRpin, INPUT);
}

void loop() {
  // duration of signal will last from 0.5 - 10 sec
  int durationpot = analogRead(potRpin);
  long duration = map(durationpot,100,1023,500,10000); 
  if (duration <500) duration = 150;

  // read middle pot for PWM of fet signal
  int PWMpot = analogRead(potMpin);
  int PWM = map(PWMpot,0,1000,0,255);
  if (PWM>255) PWM = 255;

  // mean frequency of random signals is 5 events per time unit
  int freqpot = analogRead(potLpin);
  int timeunit = map(freqpot,512,1023,60,300); // 2nd half dial range 1-5 min
  if (freqpot>1000) timeunit = 3600; // far right dial 1hr
  if (freqpot<512) timeunit = map(freqpot,0,512,0,60); // 1st half dial range 10-60 sec
  if (freqpot<100) timeunit = 0; // bottom of dial continous on  

  if (timeunit<=0){ // continuous on if left dial all counter clockwise
    analogWrite(fetpin,PWM);
  }
  
  else {
    // Check for trigger (X3 for debounce) and adjust
    // timeunit if trigger is positive.
    if (timeunit>0) {
      int trigger = digitalRead(trigpin);  
      if (trigger == HIGH) {
        delay(100);
        int trigger = digitalRead(trigpin);
        if (trigger == HIGH) {
          delay (100);
          int trigger = digitalRead(trigpin);
          if (trigger == HIGH) {
            timeunit = (duration * 10)/1000;
          }
        }
      }
    }
    
    // Calcuate time interval between events. timeunit depends on:
    // Potentiometer value if trig = 0 
    // Duration multiplier (shorter) if trig = 1
    unsigned long timeinterval = exprob * timeunit * 1000;    
    
    // Check if time interval has passed
    if ((millis() - last) > timeinterval) {
      last = millis(); // updates timer to last event
      analogWrite(fetpin,PWM);  
      delay(duration);
      digitalWrite(fetpin,LOW);
      // the following equation runs once per event and 
      // generates a random float (exprob) that falls in an 
      // exponential distribution. It will be used to
      // determine the interval time for the next event.
      // Events that occur at exponentially distributed  
      // intervals form poisson processes.
      exprob =  (-1)*(log((100-random(100))/100.00)/lambda);
    }
    else digitalWrite(fetpin,LOW);
  }
  delay(20);
}

No comments:

Post a Comment