Module 2: Preparing the Arduino

Preparing the Arduino for accepting instructions from Python code

Loading Sketch to your Arduino

The “sketch” is a set of instructions loaded onto the memory of the Arduino that allows it to understand what your Python code is asking for. In our face-to-face workshop, we do this for you. Thanks to the pandemic, you have to learn how to do this yourself. It is not hard.

  1. Go to https://www.arduino.cc/en/Main/Software and download the Arduino Integrated Development Environment (IDE) onto your computer.
  2. Attach your Arduino to a USB port. Open the IDE. Check the IDE recognizes your brand of Arduino and the right port number.

If you are using a Maker UNO, the IDE will recognize an “Arduino”. In the figures below, we used an Arduino Nano, which has some bootloader complications. A Maker Uno should be simpler. In this case our Arduino Nano is in USB port COM6; yours may be in COM3, for example:

Note: you may have to click on “COM6” to convince the bootloader that your Arduino is in COM6.

Our Arduino Nano required the ATMega328P “Old Bootloader”. Yours may not.


Finding USB Port Name for Arduino

Every time you connect the Arduino to your computer, it will be assigned a port name so that you and software can reference it. This name may change every time you connect the device.

Windows

  • - Open _Device Manager_
  • - Expand the _Ports (COM & LPT)_ list
  • A typical port name for Windows is `COM3`

Mac

  • - Open the _terminal_
  • - Type the command `ls /dev/*`
  • A typical port name for macOS is `/dev/tty.usbserial-1410`

If you cannot find a port with this type of format, it might be that you have to install the driver for the board manually. The [ch340g-ch34g-ch34x-mac-os-x-driver](https://github.com/adrianmihalko/ch340g-ch34g-ch34x-mac-os-x-driver) can be downloaded and installed using [Homebrew](https://brew.sh) (a package manager for Mac). Follow the installation instructions, or ask for help.

The Sketch

#include <SoftwareSerial.h>
#include <Wire.h>
#include <Servo.h>
#include <EEPROM.h>

SoftwareSerial *sserial = NULL;
Servo servos[8];
int servo_pins[] = {0, 0, 0, 0, 0, 0, 0, 0};
boolean connected = false;

int Str2int (String Str_value)
{
  char buffer[10]; //max length is three units
  Str_value.toCharArray(buffer, 10);
  int int_value = atoi(buffer);
  return int_value;
}

void split(String results[], int len, String input, char spChar) {
  String temp = input;
  for (int i=0; i<len; i++) {
    int idx = temp.indexOf(spChar);
    results[i] = temp.substring(0,idx);
    temp = temp.substring(idx+1);
  }
}

void Version(){
  Serial.println("version");
}

uint8_t readCapacitivePin(String data) {
  int pinToMeasure = Str2int(data);
  // readCapacitivePin
  //  Input: Arduino pin number
  //  Output: A number, from 0 to 17 expressing
  //  how much capacitance is on the pin
  //  When you touch the pin, or whatever you have
  //  attached to it, the number will get higher
  //  http://playground.arduino.cc/Code/CapacitiveSensor
  //
  // Variables used to translate from Arduino to AVR pin naming
  volatile uint8_t* port;
  volatile uint8_t* ddr;
  volatile uint8_t* pin;
  // Here we translate the input pin number from
  //  Arduino pin number to the AVR PORT, PIN, DDR,
  //  and which bit of those registers we care about.
  byte bitmask;
  port = portOutputRegister(digitalPinToPort(pinToMeasure));
  ddr = portModeRegister(digitalPinToPort(pinToMeasure));
  bitmask = digitalPinToBitMask(pinToMeasure);
  pin = portInputRegister(digitalPinToPort(pinToMeasure));
  // Discharge the pin first by setting it low and output
  *port &= ~(bitmask);
  *ddr  |= bitmask;
  delay(1);
  // Make the pin an input with the internal pull-up on
  *ddr &= ~(bitmask);
  *port |= bitmask;

  // Now see how long the pin to get pulled up. This manual unrolling of the loop
  // decreases the number of hardware cycles between each read of the pin,
  // thus increasing sensitivity.
  uint8_t cycles = 17;
       if (*pin & bitmask) { cycles =  0;}
  else if (*pin & bitmask) { cycles =  1;}
  else if (*pin & bitmask) { cycles =  2;}
  else if (*pin & bitmask) { cycles =  3;}
  else if (*pin & bitmask) { cycles =  4;}
  else if (*pin & bitmask) { cycles =  5;}
  else if (*pin & bitmask) { cycles =  6;}
  else if (*pin & bitmask) { cycles =  7;}
  else if (*pin & bitmask) { cycles =  8;}
  else if (*pin & bitmask) { cycles =  9;}
  else if (*pin & bitmask) { cycles = 10;}
  else if (*pin & bitmask) { cycles = 11;}
  else if (*pin & bitmask) { cycles = 12;}
  else if (*pin & bitmask) { cycles = 13;}
  else if (*pin & bitmask) { cycles = 14;}
  else if (*pin & bitmask) { cycles = 15;}
  else if (*pin & bitmask) { cycles = 16;}

  // Discharge the pin again by setting it low and output
  //  It's important to leave the pins low if you want to 
  //  be able to touch more than 1 sensor at a time - if
  //  the sensor is left pulled high, when you touch
  //  two sensors, your body will transfer the charge between
  //  sensors.
  *port &= ~(bitmask);
  *ddr  |= bitmask;

  //return cycles;
  Serial.println(cycles);
}

void Tone(String data){
  int idx = data.indexOf('%');
  int len = Str2int(data.substring(0,idx));
  String data2 = data.substring(idx+1);
  int idx2 = data2.indexOf('%');
  int pin = Str2int(data2.substring(0,idx2));
  String data3 = data2.substring(idx2+1);
  String melody[len*2];
  split(melody,len*2,data3,'%');

  for (int thisNote = 0; thisNote < len; thisNote++) {
    int noteDuration = 1000/Str2int(melody[thisNote+len]);
    int note = Str2int(melody[thisNote]);
    tone(pin, note, noteDuration);
    int pause = noteDuration * 1.30;
    delay(pause);
    noTone(pin);
  }
} 

void ToneNo(String data){
  int pin = Str2int(data);
  noTone(pin);
} 

void DigitalHandler(int mode, String data){
      int pin = Str2int(data);
    if(mode<=0){ //read
        Serial.println(digitalRead(pin));
    }else{
        if(pin <0){
            digitalWrite(-pin,LOW);
        }else{
            digitalWrite(pin,HIGH);
        }
        //Serial.println('0');
    }
}

void AnalogHandler(int mode, String data){
     if(mode<=0){ //read
        int pin = Str2int(data);
        Serial.println(analogRead(pin));
    }else{
        String sdata[2];
        split(sdata,2,data,'%');
        int pin = Str2int(sdata[0]);
        int pv = Str2int(sdata[1]);
        analogWrite(pin,pv);
    }
}

void ConfigurePinHandler(String data){
    int pin = Str2int(data);
    if(pin <=0){
        pinMode(-pin,INPUT);
    }else{
        pinMode(pin,OUTPUT);
    }
}

void shiftOutHandler(String data) {    
    String sdata[4];
    split(sdata, 4, data, '%');
    int dataPin = sdata[0].toInt();
    int clockPin = sdata[1].toInt();
    String bitOrderName = sdata[2];
    byte value = (byte)(sdata[3].toInt());
    if (bitOrderName == "MSBFIRST") {
       shiftOut(dataPin, clockPin, MSBFIRST, value);
    } else {
       shiftOut(dataPin, clockPin, LSBFIRST, value);
    }
}

void shiftInHandler(String data) {
    String sdata[3];
    split(sdata, 3, data, '%');
    int dataPin = sdata[0].toInt();
    int clockPin = sdata[1].toInt();
    String bitOrderName = sdata[2];
    int incoming;
    if (bitOrderName == "MSBFIRST") {
       incoming = (int)shiftIn(dataPin, clockPin, MSBFIRST);
    } else {
       incoming = (int)shiftIn(dataPin, clockPin, LSBFIRST);
    }
    Serial.println(incoming);
}

void SS_set(String data){
  delete sserial;
  String sdata[3];
  split(sdata,3,data,'%');
  int rx_ = Str2int(sdata[0]);
  int tx_ = Str2int(sdata[1]);
  int baud_ = Str2int(sdata[2]);
  sserial = new SoftwareSerial(rx_, tx_);
  sserial->begin(baud_);
  Serial.println("ss OK");
}

void SS_write(String data) {
 int len = data.length()+1;
 char buffer[len];
 data.toCharArray(buffer,len);
 Serial.println("ss OK");
 sserial->write(buffer); 
}
void SS_read(String data) {
 char c = sserial->read(); 
 Serial.println(c);
}

void pulseInHandler(String data){
    int pin = Str2int(data);
    long duration;
    if(pin <=0){
          pinMode(-pin, INPUT);
          duration = pulseIn(-pin, LOW);      
    }else{
          pinMode(pin, INPUT);
          duration = pulseIn(pin, HIGH);      
    }
    Serial.println(duration);
}

void pulseInSHandler(String data){
    int pin = Str2int(data);
    long duration;
    if(pin <=0){
          pinMode(-pin, OUTPUT);
          digitalWrite(-pin, HIGH);
          delayMicroseconds(2);
          digitalWrite(-pin, LOW);
          delayMicroseconds(5);
          digitalWrite(-pin, HIGH);
          pinMode(-pin, INPUT);
          duration = pulseIn(-pin, LOW);      
    }else{
          pinMode(pin, OUTPUT);
          digitalWrite(pin, LOW);
          delayMicroseconds(2);
          digitalWrite(pin, HIGH);
          delayMicroseconds(5);
          digitalWrite(pin, LOW);
          pinMode(pin, INPUT);
          duration = pulseIn(pin, HIGH);      
    }
    Serial.println(duration);
}

void SV_add(String data) {
    String sdata[3];
    split(sdata,3,data,'%');
    int pin = Str2int(sdata[0]);
    int min = Str2int(sdata[1]);
    int max = Str2int(sdata[2]);
    int pos = -1;
    for (int i = 0; i<8;i++) {
        if (servo_pins[i] == pin) { //reset in place
            servos[pos].detach();
            servos[pos].attach(pin, min, max);
            servo_pins[pos] = pin;
            Serial.println(pos);
            return;
            }
        }
    for (int i = 0; i<8;i++) {
        if (servo_pins[i] == 0) {pos = i;break;} // find spot in servo array
        }
    if (pos == -1) {;} //no array position available!
    else {
        servos[pos].attach(pin, min, max);
        servo_pins[pos] = pin;
        Serial.println(pos);
        }
}

void SV_remove(String data) {
    int pos = Str2int(data);
    servos[pos].detach();
    servo_pins[pos] = 0;
}

void SV_read(String data) {
    int pos = Str2int(data);
    int angle;
    angle = servos[pos].read();
    Serial.println(angle);
}

void SV_write(String data) {
    String sdata[2];
    split(sdata,2,data,'%');
    int pos = Str2int(sdata[0]);
    int angle = Str2int(sdata[1]);
    servos[pos].write(angle);
}

void SV_write_ms(String data) {
    String sdata[2];
    split(sdata,2,data,'%');
    int pos = Str2int(sdata[0]);
    int uS = Str2int(sdata[1]);
    servos[pos].writeMicroseconds(uS);
}

void sizeEEPROM() {
    Serial.println(E2END + 1);
}

void EEPROMHandler(int mode, String data) {
    String sdata[2];
    split(sdata, 2, data, '%');
    if (mode == 0) {  
        EEPROM.write(Str2int(sdata[0]), Str2int(sdata[1]));  
    } else {
        Serial.println(EEPROM.read(Str2int(sdata[0])));
    }
}

void SerialParser(void) {
  char readChar[64];
  Serial.readBytesUntil(33,readChar,64);
  String read_ = String(readChar);
  //Serial.println(readChar);
  int idx1 = read_.indexOf('%');
  int idx2 = read_.indexOf('$');
  // separate command from associated data
  String cmd = read_.substring(1,idx1);
  String data = read_.substring(idx1+1,idx2);
  
  // determine command sent
  if (cmd == "dw") {
      DigitalHandler(1, data);   
  }
  else if (cmd == "dr") {
      DigitalHandler(0, data);   
  }  
  else if (cmd == "aw") {
      AnalogHandler(1, data);   
  }    
  else if (cmd == "ar") {
      AnalogHandler(0, data);   
  }      
  else if (cmd == "pm") {
      ConfigurePinHandler(data);   
  }    
  else if (cmd == "ps") {
      pulseInSHandler(data);   
  }    
  else if (cmd == "pi") {
      pulseInHandler(data);   
  }        
  else if (cmd == "ss") {
      SS_set(data);   
  }
  else if (cmd == "sw") {
      SS_write(data);   
  }
  else if (cmd == "sr") {
      SS_read(data);   
  }    
  else if (cmd == "sva") {
      SV_add(data);   
  }      
  else if (cmd == "svr") {
      SV_read(data);   
  }   
 else if (cmd == "svw") {
      SV_write(data);   
  }    
 else if (cmd == "svwm") {
      SV_write_ms(data);   
  }      
  else if (cmd == "svd") {
      SV_remove(data);   
  } 
  else if (cmd == "version") {
      Version();   
  }
  else if (cmd == "to") {
      Tone(data);   
  } 
  else if (cmd == "nto") {
      ToneNo(data);   
  }  
  else if (cmd == "cap") {
      readCapacitivePin(data);   
  }
  else if (cmd == "so") {
      shiftOutHandler(data);
  }
  else if (cmd == "si") {
      shiftInHandler(data);
  }
  else if (cmd == "eewr") {
      EEPROMHandler(0, data);   
  } 
  else if (cmd == "eer") {
      EEPROMHandler(1, data);   
  }  
  else if (cmd == "sz") {  
      sizeEEPROM();
  }  
}

void setup()  {
  Serial.begin(9600); 
    while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }
}

void loop() {
   SerialParser();
}

In the IDE go to File->Open and open prototype.ino

Upload the sketch.

Check the orange messages in the bottom box of this window when the process is complete. These will tell you everything is OK, or why things went wrong.

If all is well, you are ready to control your Arduino with Python code.

Modules

  1. Buying Parts
  2. Preparing the Arduino
  3. Setting up Python on your PC
  4. Learning Python
  5. Arduino for Data Collection
  6. Ultrasonic Range Sensing
  7. Pendulum Experiment

 

Updated 2010-05-19  CEW

4 responses to “Module 2: Preparing the Arduino”

  1. Mike

    thank you ! It’s working for me now.

  2. Mike

    Hi.

    I got an error when I opened prototype.ino in IDE.

    Arduino: 1.8.13 (Windows 10), Board: “Arduino Uno”

    prototype:15:10: fatal error: DHT.h: No such file or directory

    #include

    ^~~~~~~

    compilation terminated.

    exit status 1

    DHT.h: No such file or directory

    Invalid library found in C:\Users\78470\Documents\Arduino\libraries\readme.txt: no headers files (.h) found in C:\Users\78470\Documents\Arduino\libraries\readme.txt

    Why is this happening and how can I solve it?

    Thanks

    1. waltham

      I have updated prototype.ino with a version that does not use DHT.h. This works with my Uno system.
      CEW

    2. Felix

      Thanks! This was really helpful.

Leave a Reply