Advertisement
grist

Tensegrity Bike Controller

Aug 3rd, 2015
344
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 9.47 KB | None | 0 0
  1. /*
  2.  
  3. A sketch to take RPM data from a magnet and Hall Effect sensor and use that to
  4. control a variable speed motor.
  5.  
  6. Also create a visualisation of the rpm using an LED strip.
  7.  
  8. 16 Aug Commenting out tachometer code as we've decided it might distract from the main
  9.     display. Ditto serial code as it's not required. Working version loaded to controller
  10.     for the start of the LUX festival.
  11.  
  12.  
  13.  
  14. */
  15.  
  16. #include <Adafruit_NeoPixel.h>
  17. //#include <Wire.h>
  18.  
  19. // Settings for the tachometer
  20. #define SENSOR_PIN    2  // INT0
  21. #define LOW_RPM_THRESHOLD  3000 // milliseconds. Assumes the wheel is stopped if no ticks detected for this long.
  22. #define WEIGHTING  10  // how much to weight the previous readings by. not used yet
  23. #define EASTER_EGG_THRESHOLD  100  // how many cycles to over-rev to trigger the easter egg. not used yet
  24.  
  25. // Settings for the display
  26. //#define SLAVE_ADDRESS    0x01 // the display's I2C address
  27. //#define FORMAT_DECIMAL   0b01000000 // for sending to the display
  28.  
  29. // settings for the LED strip and motor control
  30. #define MAX_RPM    511  // The maximum RPM from the bike that will define 'full power'
  31. #define MIN_UPDATE  30  // minimum milliseconds between updating the LED strip. Affects how fast the strip 'moves'
  32. #define MAX_UPDATE 1000 // maximum milliseconds between updates. ie, the slowest it will move
  33. #define DASH_LENGTH  10 // the total number of dots that make a dash, includes blank ones
  34. #define LIT_DASHES    7 // how many dots to light out of the total dash length
  35. #define COLOUR_MAX  255 // the brightest to set the pixels
  36.  
  37. #define NUM_PIXELS  150 // how many pixels in the LED strip
  38. #define LED_PIN     6   // which pin the LED strip data line ia connected to
  39.  
  40. // settings for the stepper motor
  41. #define DIR_PIN    3
  42. #define STEP_PIN   4
  43. // Empirically derived from manually rotating the pot with a 10v supply.
  44. #define MAX_VOLTAGE              10
  45. #define MIN_VOLTAGE               0
  46. #define MAX_VOLTAGE_READING    1020  // What the ADC reads at the max voltage required
  47. #define MIN_VOLTAGE_READING       0  // Off
  48. #define STEPPER_SPEED          0.05
  49. #define VOLTAGE_PIN               0  // Analog read
  50. #define JITTER_THRESHOLD          8  // Voltage readings jitter by a small amount. Ignore changes less than this
  51. // Thresholds for the red -> yellow -> white fade
  52. #define RED_THRESHOLD           150  // starts to go yellow after this many rpm
  53. #define YELLOW_THRESHOLD        350  // starts to go white after this.
  54.  
  55. // Parameter 1 = number of pixels in strip
  56. // Parameter 2 = pin number (most are valid)
  57. // Parameter 3 = pixel type flags, add together as needed:
  58. // NEO_RGB Pixels are wired for RGB bitstream
  59. // NEO_GRB Pixels are wired for GRB bitstream
  60. // NEO_KHZ400 400 KHz bitstream (e.g. FLORA pixels)
  61. // NEO_KHZ800 800 KHz bitstream (e.g. High Density LED strip)
  62. Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_PIXELS, LED_PIN, NEO_GRB + NEO_KHZ800);
  63.  
  64. // volatile globals used by the sensor interrupt to calculate RPM
  65. volatile unsigned long tick_time;
  66. volatile unsigned long last_tick_time;
  67. volatile int rpm;
  68.  
  69. void setup() {
  70.   //Serial.begin(115200);
  71.   // Stepper motor controller pins
  72.   pinMode(DIR_PIN, OUTPUT);
  73.   pinMode(STEP_PIN, OUTPUT);
  74.   setVoltage(0);  // make sure it's off when it comes up
  75.   //Wire.begin();
  76.   strip.begin();
  77.   strip.show(); // Initialize all pixels to 'off'
  78.   attachInterrupt(INT0, ticker, FALLING); // pin goes low when sensor detects a magnetic field    
  79. }
  80.  
  81. void loop() {  
  82.  
  83.    
  84.     int voltage_reading;  // not actual voltage, but 0-1023 from the 10 bit ADC
  85.     //float desired_voltage;  // is an actual voltage between MIN and MAX from defines above
  86.     byte counter = 0;  // to keep track of drawing the dashes
  87.     unsigned long next_push = 0;  // next time a pixel will be sent to the strip
  88.     //byte over_rev = 0; // not used
  89.     int avg_rpm = 0; // to smooth out the actual rpms a bit. Weighted average.
  90.     int constrained_rpm;  // it's good to have boundaries
  91.  
  92.     while(1) {
  93.       //avg_rpm = rpm;
  94.       avg_rpm = ((avg_rpm * WEIGHTING) + rpm)/(WEIGHTING + 1);  // weighted average
  95.       constrained_rpm = constrain(avg_rpm, 0, MAX_RPM);
  96.       setMotorSpeed(constrained_rpm);
  97.       //sendCount(avg_rpm);
  98.       //sendCount(analogRead(VOLTAGE_PIN));
  99.       //Serial.println(millis());
  100.       //Serial.println(next_push);
  101.       if (millis() > next_push) {
  102.         if (counter <= LIT_DASHES) {
  103.             pushPixel(makeStripColour(constrained_rpm));
  104.         } else {
  105.             pushPixel(0,0,0);
  106.         }
  107.         next_push = millis() + MIN_UPDATE + (MAX_UPDATE - ((float)constrained_rpm/MAX_RPM * MAX_UPDATE));
  108.         counter = (counter++) % DASH_LENGTH;
  109.         /*
  110.         Serial.print("AvgRPM: ");
  111.         Serial.println(avg_rpm);
  112.         Serial.print("ConstRPM: ");
  113.         Serial.println(constrained_rpm);
  114.         Serial.println(interval);
  115.         Serial.println("----");
  116.         */
  117.       }
  118.       if (millis() - last_tick_time > LOW_RPM_THRESHOLD) {
  119.         // Setting RPM is done by triggering the sensor. If this
  120.         // hasn't happened for a while then the wheel has stopped.
  121.         rpm = 0;
  122.       }
  123.       delay(20); // so we don't spam the controller and display    
  124.    }
  125. }
  126.  
  127. void ticker() {
  128.     // Triggered by a falling edge interrupt on pin 2 (INT0)
  129.     // Use elapsed time in milliseconds since the last pulse to work out the RPM
  130.     // This is accurate enough for the few hundred RPMs expected.
  131.     tick_time = millis();
  132.     rpm = (float)1000/(tick_time - last_tick_time) * 60;
  133.     last_tick_time = tick_time;
  134. }
  135.  
  136. void pushPixel(int r, int g, int b) {
  137.     // Push a pixel of the given colour onto the first LED and shift all the rest up one.
  138.     pushPixel(strip.Color(r,g,b));
  139. }
  140.  
  141. void pushPixel(uint32_t colour) {
  142.     // Push a pixel of the given colour onto the first LED and shift all the rest up one.
  143.    
  144.     for (int i = NUM_PIXELS-1; i>0; i--) {
  145.         //Serial.println(i);
  146.         strip.setPixelColor(i, strip.getPixelColor(i-1));
  147.     }
  148.     strip.setPixelColor(0, colour);
  149.     //strip.setPixelColor(59, colour);
  150.     strip.show();
  151. }
  152.    
  153. void setMotorSpeed(int rpm) {
  154.     // set the motor speed based on the supplied RPM
  155.     // convert the rpm to voltage
  156.     setVoltage(((float)rpm/MAX_RPM) * MAX_VOLTAGE);
  157. }
  158.  
  159. /* Tachometer. Not used.
  160. void sendCount(int counter) {
  161.     // send counter to display
  162.     uint8_t bytes[5] = {FORMAT_DECIMAL, 0, 0, 0, 0}; // the display expects 5 bytes
  163.     bytes[3] = counter/256;
  164.     bytes[4] = counter % 256;
  165.     Wire.beginTransmission(SLAVE_ADDRESS);
  166.     for (int i=0;i<5;i++) {
  167.         Wire.write((byte)bytes[i]);
  168.         //Serial.print(bytes[i]);
  169.         //Serial.print(" | ");
  170.     }
  171.     Wire.endTransmission();
  172.     //Serial.print("\n");
  173. }
  174. */
  175.  
  176. uint32_t makeStripColour(int revs) {
  177.     // Take the RPM value and convert it to a compressed 32 bit colour for sending to the strip.
  178.     // Fading up from red, through yellow to white using the following scheme
  179.     // 0 - RED_THRESHOLD:  fade up red
  180.     // RED to YELLOW:      fade in green proportionately to make yellow
  181.     // YELLOW to MAX_RPM:  red & green go up together while bringing in blue
  182.  
  183.     int r,g,b;
  184.    
  185.     if (revs < RED_THRESHOLD) {
  186.         r = (float)revs/MAX_RPM * COLOUR_MAX;
  187.         g = 0;
  188.         b = 0;
  189.     } else if (revs <= YELLOW_THRESHOLD) {
  190.         r = (float)revs/MAX_RPM * COLOUR_MAX;
  191.         g = (float)(revs - RED_THRESHOLD) / (YELLOW_THRESHOLD - RED_THRESHOLD) * ((float)YELLOW_THRESHOLD/MAX_RPM * COLOUR_MAX);
  192.         b = 0;
  193.     } else if (revs > YELLOW_THRESHOLD) {
  194.         r = (float)revs/MAX_RPM * COLOUR_MAX;
  195.         g = (float)revs/MAX_RPM * COLOUR_MAX;
  196.         b = (float)(revs - YELLOW_THRESHOLD) / (MAX_RPM - YELLOW_THRESHOLD) * COLOUR_MAX;
  197.     }
  198.     /*
  199.     Serial.println(revs);
  200.     Serial.println(r);
  201.     Serial.println(g);
  202.     Serial.println(b);
  203.     Serial.println("----");
  204.     */
  205.     return strip.Color(r,g,b);
  206. }
  207.  
  208. // Stepper controller functions    
  209. void setVoltage(float voltage) {
  210.   // Seek the desired voltage
  211.   // Convert from the actual voltage requested to the appropriate ADC reading
  212.   //Serial.print("G: ");
  213.   //Serial.println(voltage);
  214.   voltage = voltage * ((float)MAX_VOLTAGE_READING/MAX_VOLTAGE);
  215.   voltage = constrain(voltage,MIN_VOLTAGE_READING, MAX_VOLTAGE_READING);
  216.   //Serial.print("Setting: ");
  217.   //Serial.println(voltage);
  218.   // only change if the current voltage reading is outside the expected jitter
  219.   // or we're aiming for 0
  220.   if (voltage != 0 && abs(analogRead(VOLTAGE_PIN) - voltage) > JITTER_THRESHOLD) {
  221.     if (analogRead(VOLTAGE_PIN) < voltage) {
  222.       while (analogRead(VOLTAGE_PIN) < voltage) {
  223.         //Serial.print("UP: ");
  224.         //Serial.println(analogRead(VOLTAGE_PIN));
  225.         rotate(1, STEPPER_SPEED);
  226.       }
  227.     } else if (analogRead(VOLTAGE_PIN) > voltage) {
  228.       while (analogRead(VOLTAGE_PIN) > voltage) {
  229.         //Serial.print("DOWN: ");
  230.         //Serial.println(analogRead(VOLTAGE_PIN));
  231.         rotate(-1, STEPPER_SPEED);
  232.       }
  233.     }
  234.   }
  235. }
  236.  
  237. void rotate(int steps, float speed){
  238.   //rotate a specific number of microsteps (8 microsteps per step) - (negative for reverse movement)
  239.   //speed is any number from .01 -> 1 with 1 being fastest - Slower is stronger
  240.   int dir = (steps > 0)? HIGH:LOW;
  241.   steps = abs(steps);
  242.  
  243.   digitalWrite(DIR_PIN,dir);
  244.  
  245.   float usDelay = (1/speed) * 70;
  246.  
  247.   for(int i=0; i < steps; i++){
  248.     digitalWrite(STEP_PIN, HIGH);
  249.     delayMicroseconds(usDelay);
  250.  
  251.     digitalWrite(STEP_PIN, LOW);
  252.     delayMicroseconds(usDelay);
  253.   }
  254. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement