ev3dev – using IRLink with python

I got myself a HiTechnic IRLink sensor.

As of today (August 2016) ev3dev already recognizes the IRLink as a nxt-i2c sensor but there’s no language support for it. David Lechner suggested me using the “direct” attribute to communicate directly with the IRLink at I2C level.

Last time I wrote something mildly related to I2C was about 20 years ago for a Microchip PIC project but well… why not?

So after lots of trial and error, reading several times the LEGO Power Functions RC Protocol and shamelessly copying code from Mike Hatton (“Parax”), Xander Soldaat and Lawrie Griffiths I found on GitHub, RobotC forum and LeJOS forum I fanally managed to control a PF motor in ComboPWM mode.

In the following video, I’m increasing the motor speed (all 7 steps) then decreasing it again until it stops:

This is the python script running in the EV3:

#!/usr/bin/python

#
# based mainly on RobotC code from Mike Hatton ("Parax") and Xander Soldaat
# but also on LeJOS code from Lawrie Griffiths
#

# assumes IRLink at Input 1 as sensor0

import sys
from time import sleep

# channel: 0..3
# motorA, motorB: 0..7

channel = 0
for motorA in (1,1,2,2,3,3,4,4,5,5,6,6,7,7,6,6,5,5,4,4,3,3,2,2,1,1,0,0):

  motorB = motorA

  iBufferSize=2
  iBuffer = bytearray(iBufferSize)

  iBuffer[0] = ((0x04 | channel) << 4) | motorB
  iBuffer[1] = motorA << 4
  check = 0xF ^ (0x04 | channel) ^ motorB ^ motorA
  iBuffer[1] = iBuffer[1] | check

  oBufferSize=14
  oBuffer = bytearray(oBufferSize)

  # clear all positions
  for i in range (0,oBufferSize):
    oBuffer[i]=0

  oBuffer[0]=0x80    # Start Bit

  oBufferIdx = 0

  for iBufferByte in range (0,2):
    for iBufferIdx in range (0,8):
      oBuffer[1 + (oBufferIdx / 8)] |= (0x80 >> (oBufferIdx % 8) )
      if ( ( ( iBuffer[iBufferByte] ) & (0x80 >> (iBufferIdx % 8) ) ) != 0 ) :
        oBufferIdx = oBufferIdx + 6
      else:
        oBufferIdx = oBufferIdx + 3

# Stop bit
  oBuffer[1+ (oBufferIdx / 8)] |= (0x80 >> (oBufferIdx % 8) )

  tailIdx = 1 + (oBufferIdx / 8) + 1

  # Tail


  if (tailIdx == 10):
    oBuffer[tailIdx]= 0x10 # IRLink message payload length
    register = 0x43
  else:
    oBuffer[tailIdx]= 0x11
    register = 0x42

  oBuffer[tailIdx+1]= 0x02 # IRLink in Power Functions Mode
  oBuffer[tailIdx+2]= 0x01 # IRLInk Start transmission 


# clear IRLink (not sure if correct but seems to improve)

  fd = open("/sys/class/lego-sensor/sensor0/direct", 'wb',0)
  fd.seek(0x41)
  fd.write(chr(0x46))
  fd.write(chr(0x44))
  fd.write(chr(0x4C))
  fd.write(chr(0x50))
  fd.close()
  sleep(0.1)

  for i in range(0,5):
    fd = open("/sys/class/lego-sensor/sensor0/direct", 'wb',0)
    fd.seek(register)
    for oBufferIdx in range (0,oBufferSize):
      fd.write(chr(oBuffer[oBufferIdx]))
    fd.close()

    # Power Functions timings (for a 5-command burst)
    if (i==1):
      sleep(0.064)
    elif (i==5):
      sleep(0.096)
    else:
      sleep(0.080)

 

Deixe um comentário

O seu endereço de email não será publicado. Campos obrigatórios marcados com *