Communicating between Python and Arduino with pyserial

Posted on Dec 28, 2011 in Arduino, C++, Electronics, Programming, Python, UberFridge | 4 comments

In my UberFridge project, my router runs a python script that periodically requests new sensor data from the Arduino. The script logs this data in files for the web server running on the router. All other communication with Arduino (getting the OLED’s content, reading/changing settings) also goes via the python script. The Arduino is programmed as a slave that responds to requests from python. With the pyserial library for python, setting this up is fairly easy.

Python code

My Arduino connects to the router via a USB hub. The router mounts the Arduino as /dev/usb/tts/0. See this article on installing the drivers, python, etc.

Python can open the serial port to communicate to Arduino. I use a 2 second timeout, so Python waits 2 seconds for a reply after requesting data.

import serial
 
# open serial port
ser = serial.Serial('/dev/usb/tts/0',9600,timeout=2)

I can now read lines printed by the Arduino with:

line = ser.readline()

Request new data with:

ser.write("r")

And give commands, in this case setting it a constant beer mode at a certain temperature:

ser.write("b"+str(temperatureSetting))

As you see, I start every line with a letter that indicates the type of message. The Arduino knows how the respond to these commands.

Arduino code

The Arduino constantly polls the serial port for data. When data is available, it looks at the first character of this data and performs the corresponding action. The functions that send sensor data to the serial port will be discussed in the article on data logging for Google annotated time line charts.

void handleSerialCommunication(void){
  int newTemp;
  if (Serial.available() > 0)
  {
    char inByte = Serial.read();
    switch(inByte)
    {
    case 'r': //Data request
      serialPrintTemperatures();
      break;
    case 'b': //Set to constant beer temperature
      beerTemperatureSetting = numberFromSerial();
      mode = BEER_CONSTANT;
      initControl();
      updateSettings();
      saveSettings(); //write new settings to EEPROM
      doNegPeakDetect=0;
      doPosPeakDetect=0;
      lastCoolTime=0;
      lastHeatTime=0;
      previousState=0xFF; //force LCD update
      state=IDLE;
      lcdPrintState();
      serialBeerMessage(BEER_SETTING_FROM_SERIAL);
      break;
    case 'p': //Set profile temperature
      beerTemperatureSetting = numberFromSerial();
      mode = BEER_PROFILE;
      updateSettings();
      if(abs(beerTemperatureSetting - eepromReadInt(EEPROM_BEER_SETTING))>=5){
          initControl();
          updateSettings();
          saveSettings(); //write new settings to EEPROM every half degree difference
          doNegPeakDetect=0;
          doPosPeakDetect=0;
          lastCoolTime=0;
          lastHeatTime=0;
          previousState=0xFF; //force LCD update
          state=IDLE;
          lcdPrintState();
      }
      serialBeerMessage(BEER_SETTING_FROM_PROFILE);
      break;
    case 'f': //Set to constant fridge temperature
      newTemp = numberFromSerial();
      if(abs(newTemp-fridgeTemperatureSetting)>3){
        doNegPeakDetect=0;
        doPosPeakDetect=0;
        lastCoolTime=0;
        lastHeatTime=0;
        previousState=0xFF; //force LCD update
        state=IDLE;
        lcdPrintState();
        initControl();
      }
      mode = FRIDGE_CONSTANT;
      fridgeTemperatureSetting = newTemp;
      updateSettings();
      saveSettings(); //write new settings to EEPROM
      serialFridgeMessage(FRIDGE_SETTING_FROM_SERIAL);
      break;
    case 's': //Settings requested
      switch(mode){
        case BEER_CONSTANT:
          Serial.println("beer");
          Serial.println(int(beerTemperatureSetting+.5));
          break;
        case FRIDGE_CONSTANT:
          Serial.println("fridge");
          Serial.println(int(fridgeTemperatureSetting+.5));
          break;
        case BEER_PROFILE:
          Serial.println("profile");
          Serial.println(int(beerTemperatureSetting+.5));
          break;
      }
      break;
    case'l': //lcd contents requested
      lcdReadContents();
      Serial.print(lcdText[0]);Serial.print("
");
      Serial.print(lcdText[1]);Serial.print("
");
      Serial.print(lcdText[2]);Serial.print("
");
      Serial.println(lcdText[3]);
      break;
    default:
      Serial.println(".Invalid command Received by Arduino");
    }
    Serial.flush();
  }
}
 
int numberFromSerial(void)
{
  char numberString[8];
  unsigned char index=0;
  delay(10);
  while(Serial.available() > 0)
  {
    delay(10);
    numberString[index++]=Serial.read();
    if(index>6)
    {
      break;
    }
  }
  numberString[index]=0;
  return atoi(numberString);
}

4 Comments

  1. Hi Elco,

    Your Uberfridge inspired me to create an interface by which I can controll my aquarium lights over the internet. I am looking for information on how to get my OpenWrt router to mount the Arduino. A link in this article that would have the information I want is dead. Would you please fix the link? Or else you could email me any resources you used

    Best Regards

    Marinus

    • Thanks for the heads up. It’s fixed now.

  2. Hi Elco – 

    Did you ever see differences in the value between the Arduino output via the serial monitor and what was coming in via pySerial? I’m seeing a difference of about 40F. Thanks for any help. Brew on!

    • I have not noticed that. There is a small difference on how both languages round floats, so 22.0 might become 21.9999994 or something. I wrote the whole thing for Celcius. Could that explain something?

Leave a Reply

Your email address will not be published. Required fields are marked *