Reading serial data from Arduino to trigger commands on computer

I had a problem. A big one. By mistake I ordered Arduino Nano clones instead of Micros. Why this matters is that Micros use ATmega32U4, which has USB capability built-in. My attempts at using USB HID library went less than successful, since the library would just throw an compile error, which is understandable.

But then I thought as occured to me – why not send data over serial connection and just read them with some kind of intermediary in form of script or program. That way I’ll be able to do whatever I want with the data sent from Arduino to a computer!

Arduino

This turned out to be easier than expected but first I had to program my Arduino. I wrote a simple sketch that will be able to handle a button press.

bool state = false;
int pin = 3;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  pinMode(pin, INPUT_PULLUP);

}

void loop() {
  // put your main code here, to run repeatedly:
  state = digitalRead(pin);
  if (!state){
      Serial.write(0x1);
    }
   else {
      Serial.write(0x0);
    }
  delay(100);
}

I checked if the code was working simply by replacing Serial.write command with Serial.print. Rest was done in Python.

Python

I installed 2 mandatory packages for this operation

pip install pyserial pynput

Pyserial is the library that facilitates the serial communication in Python, with it’s help you can read and write to serial port. Pynput is my library of choice for simulating keyboard and mouse – I’ll be triggering a button press through it but you can do whatever you want, even transmit data from sensors to your PC!

import serial
import pynput.keyboard

#ser is cheese in Polish
cheese = serial.Serial(port="COM4", baudrate=115200)
keyboard = pynput.keyboard.Controller()
print(f"Connected to {cheese.name}")

while True:
    val = int.from_bytes(cheese.read(), "big")
    if val == 0x1:
        keyboard.press("q")
    else:
        keyboard.release("q")

Let’s go through the code line by line.

import serial
import pynput.keyboard

First of all, let’s import our libraries. We’re using only a submodule of pynput, instad of importing everything – makes handling it less unwieldly.

cheese = serial.Serial(port="COM4", baudrate=115200)

Define your serial connection. In my case the port is equal to COM4 – you can check this in Device Manager, if you’re on Windows. Baudrate has to match what we’ve set before in our sketch.

keyboard = pynput.keyboard.Controller()

Define the controller for the keyboard.

while True:
    val = int.from_bytes(cheese.read(), "big")

I start the infinite loop here, since I want this to run as long as possible – side effect of this is that the script will crash once the Arduino is disconnected. In the next line we read from our serial connection, by default we read 1 byte of information. This is customizable and pyserial even makes it possible to read strings from your Arduino using readlines(). Then we use a bit of built-in magic, int type makes it possible for us to convert from bytes to int by using from_bytes. It’s important to do it this way, otherwise you’ll have problems with simple casting (int(my_bytes) for example). More about from_bytes here.

    if val == 0x1:
        keyboard.press("q")
    else:
        keyboard.release("q")

What’s left is to trigger the key press, when the Arduino sends over value of 0x1 and release it when the value is something other that 0x1. I deliberately done it this way, the button will be pressed, unless released.

Hope you’ve learned something today – don’t be stupid and buy Micros.

Leave a Reply

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