Code for the Spinner - I2C

I2C is a 2 wire interface to connect devices and uses a data (SDA) and clock line (SCL). You have a main controller which sets up a line and sends / receives messages to node follower controllers on the line which have addresses.

The main controller can't tell what order the nodes are connected on so although the hubs are identical, they can't be connected in any order as I planned initially.

The main controller is a rp2040 and the node controllers are ATTiny 412. In order to code the 412 you want to use the legacy Arduino ide.


To begin with I went through and tested all the inputs on the controller using its led.

Code to test the potentiometers.

  int const potPin = A1;  // analog pin used to connect the potentiometer, A0 or A1
  int const ledPin = 29;
  int potVal;             // variable to read the value from the analog pin
  int bright;              // variable to hold the angle for the servo motor
  
  void setup() {
  pinMode(ledPin, OUTPUT);
  }
  
  void loop() {
  potVal = analogRead(potPin);  // read the value of the potentiometer
  
  
  // scale the numbers from the pot
  bright = map(potVal, 0, 1023, 0, 255);
  
  // set brightness
  analogWrite(ledPin, bright);
  
  
  delay(15);
  }

Code to test the switches

  const int buttonPin = 1;     // the number of the pushbutton pin
  const int buttonPintwo = 2;     // the number of the pushbutton pin
  const int ledPin =  29;      // the number of the LED pin
   
  int buttonState = 0;         // variable for reading the pushbutton status
  int buttonStatetwo = 0;         // variable for reading the pushbutton status
   
  void setup() {
    // initialize the LED pin as an output:
    pinMode(ledPin, OUTPUT);
    // initialize the pushbutton pin as an input:
    pinMode(buttonPin, INPUT_PULLUP);
    // initialize the pushbutton pin as an input:
    pinMode(buttonPintwo, INPUT_PULLUP);
  }
   
  void loop() {
    // read the state of the pushbutton value:
    buttonState = digitalRead(buttonPin);
    buttonStatetwo = digitalRead(buttonPintwo);
   
    // check if the pushbutton is pressed. If it is, the buttonState is HIGH:
    if (buttonState == HIGH) {
      // turn LED off:
      digitalWrite(ledPin, HIGH);
    } else {
      // turn LED on:
      digitalWrite(ledPin, LOW);
    }
  
    if (buttonStatetwo == HIGH) {
      // turn LED off:
      digitalWrite(ledPin, HIGH);
    } else {
      // turn LED on:
      digitalWrite(ledPin, LOW);
    }
  }

I then connected one node board to the main controller so I could test the potentiometer controlling the led on the nodes board.

The value the from the potentiometer 0-1023 was mapped between the 0-255 brightness value of the led. This is then sent along the wire to the node address connected to the clock and data lines.


Code for the main controller - one node, led control

  #include 

    const int nodeAddress = 1;
    const int potenPin = A0; 
    int potVal;
    int bright;
    
    void setup()
    {
    Wire.begin(); 
    Serial.begin( 9600 ); 
    }
    
    void loop() {                                             
        potVal = analogRead(potenPin);
        bright = map(potVal, 0, 1023, 0, 255);
    
        Wire.beginTransmission(nodeAddress);
        Wire.write(bright);
        Wire.endTransmission();
    }

The node controller one passes the value through to the esc, it does not do any calculations or specific actions other than its set address. This means this code does not change much. The 'Wire.begin(1)' sets up which address it responds to.

The node boards are coded using updi.

Node code for led.

#include  
  int ledPin = 1;
  
  void setup()
  {
  pinMode (ledPin, OUTPUT);
  Wire.begin(1);
  Wire.onReceive(receiveEvent);
  }
  
  void loop(){
  }
  
  void receiveEvent( int howMany )
  {
  int bright = Wire.read();
  analogWrite(ledPin, bright);
  
  
  }

Now I added the second node board with the code to dim the second led separately. This will be slowing down the second motor in the final code which is necessary for spinning.

We also have a check to make sure the value doesn't make the breaking value doesn't go negative.

Two nodes and difference control

    #include 

      const int nodeAddress = 1;
      const int node2Address = 2;
      const int potenPin = A0; 
      const int poten2Pin = A1;
      int potVal;
      int pot2Val;
      int bright;
      int differ;
      int breaking;
      
      void setup()
      {
      Wire.begin(); 
      Serial.begin( 9600 ); 
      }
      
      void loop() {                                             
          potVal = analogRead(potenPin);
          bright = map(potVal, 0, 1023, 10, 255);
          pot2Val = analogRead(poten2Pin);
          differ = map(pot2Val, 0, 1023, 1, 127);
          breaking = bright-differ;
          if (breaking < 0) {
            breaking = 0;
          }
      
          Wire.beginTransmission(nodeAddress);
          Wire.write(bright);
          Wire.endTransmission();
      
          Wire.beginTransmission(node2Address);
          Wire.write(breaking);
          Wire.endTransmission();
        
      }
  

Node code

    #include  
      int ledPin = 1;
      int bright = 0;
      
      void setup()
      {
      pinMode (ledPin, OUTPUT);
      Wire.begin(1);
      Wire.onReceive(receiveEvent);
      }
      
      void loop(){
        analogWrite(ledPin, bright);
        delay (10);
      }
      
      void receiveEvent( int howMany )
      {
        bright = Wire.read();
      
      }
  
    #include 
      int ledPin = 1;
      int bright = 0;
      
      void setup()
      {
        pinMode (ledPin, OUTPUT);
        Wire.begin(2);
        Wire.onReceive(receiveEvent);
      }
      
      void loop() {
        analogWrite(ledPin, bright);
        delay (10);
      }
      
      void receiveEvent( int howMany )
      {
        bright = Wire.read();
      
      }
  

Motor working.

Final Code

We went through a few iterations and changes but here is the final code.

When you first set up the esc you calibrate the thresholds between neutral, forward max, and backwards max. You turn the potentiometer to certain points to mark these. As this involves some leeway the thresholds for the two motors are slightly different so we have two variables for the different motor set ups.

The about of breaking is a fraction on the current speed which means it scales with the speed instead of a fixed range.

  #include 

    const int nodeAddress = 1;
    const int node2Address = 2;
    const int potenPin = A0;
    const int poten2Pin = A1;
    const int pausePin = 2;
    const int dirPin = 1;
    const int ledPin = 29;
    const int threshold = 67;
    const int threshold2 = 65;
    const int breakingratio = 10; //percentage of speed
    
    int potVal;
    int pot2Val;
    int speed1; // speed of motor 1 range 0-1000
    int speed2; // speed of motor 1 - difference range 0-1000
    int speedpot;
    int differpot;
    int motor1;
    int motor2;
    
    int pauseState = 0;
    int dirState = 0;
    
    
    
    void setup() {
      analogWrite(ledPin, 255);
      Wire.begin();
      pinMode(pausePin, INPUT_PULLUP);
      pinMode(dirPin, INPUT_PULLUP);
      Serial.begin(9600);
    }
    
    void loop() {
      // potentiometer set up
      potVal = analogRead(potenPin);
      speedpot = map(potVal, 0, 1023, 0, 1000);
      //Serial.println(bright);
      pot2Val = analogRead(poten2Pin);
      differpot = map(pot2Val, 0, 1023, 0, speedpot/breakingratio);
    
    
      // pause check
      pauseState = digitalRead(pausePin);
      dirState = digitalRead(dirPin);
      if (pauseState == LOW) {
        Wire.beginTransmission(nodeAddress);
        Wire.write(threshold);  // whatever the mid point angle is
        Wire.endTransmission();
        //Serial.println(threshold);
    
        Wire.beginTransmission(node2Address);
        Wire.write(threshold2);  // whatever the mid point angle is
        Wire.endTransmission();
        //Serial.println(threshold, threshold2);
      }
    
      else if (dirState == HIGH) {
        // forward
        
        speed1 = speedpot;
        speed2 = speedpot - differpot;
        if (speed2 < 0) {
          speed2 = 0;
        }
        Serial.print("speed1 ");
        Serial.print(speed1);
        Serial.print(" speed2 ");
        Serial.print(speed2);
        
        motor1 = map(speed1, 0, 1000, threshold, threshold+20);  
    
        motor2 = map(speed2, 0, 1000, threshold2, threshold2+20);
        
    
        Wire.beginTransmission(nodeAddress);
        Wire.write(motor1);
        Wire.endTransmission();
        Serial.print(" motor1 ");
        Serial.print(motor1);
    
        Wire.beginTransmission(node2Address);
        Wire.write(motor2);
        Wire.endTransmission();
        Serial.print(" motor2 ");
        Serial.println(motor2);
      }
    
    
      else if (dirState == LOW) {
        //backwards
        speed1 = speedpot;
        speed2 = speedpot + differpot;
        if (speed2 < 0) {
          speed2 = 0;
        }
        Serial.print("speed1 ");
        Serial.print(speed1);
        Serial.print(" speed2 ");
        Serial.print(speed2);
        
        motor1 = map(speed1, 0, 1000, threshold, threshold-20);  //whatever forward angle range is
    
        motor2 = map(speed2, 0, 1000, threshold2, threshold2-20);
        
    
        Wire.beginTransmission(nodeAddress);
        Wire.write(motor1);
        Wire.endTransmission();
        Serial.print(" motor1 ");
        Serial.print(motor1);
    
        Wire.beginTransmission(node2Address);
        Wire.write(motor2);
        Wire.endTransmission();
        Serial.print(" motor2 ");
        Serial.println(motor2);
      }
    }

Node codes.

  #include 
    #include 
    
    Servo myServo;
    
    int ledPin = 1;
    int angle;
    
    void setup()
    {
      pinMode (ledPin, OUTPUT);
      myServo.attach(0);
      Wire.begin(1);
      Wire.onReceive(receiveEvent);
    }
    
    void loop() {
      analogWrite(ledPin, 255);
      myServo.write(angle);
      delay (10);
    }
    
    void receiveEvent( int howMany )
    {
      angle = Wire.read();
    
    }
  #include 
    #include 
    
    Servo myServo;
    
    int ledPin = 1;
    int angle;
    
    void setup()
    {
      pinMode (ledPin, OUTPUT);
      myServo.attach(0);
      Wire.begin(2);
      Wire.onReceive(receiveEvent);
    }
    
    void loop() {
      analogWrite(ledPin, 255);
      myServo.write(angle);
      delay (10);
    }
    
    void receiveEvent( int howMany )
    {
      angle = Wire.read();
    
    }

In the future I want to investigate different ways of setting up the servo library as the backwards range did not have enough detail in the number of angles it was allocated so did not have a slow enough range. However, it is possible to increase the range.