Permalink
Browse files

Stub RoboClaw for running tests without a RoboClaw attached.

  • Loading branch information...
Roger-random committed Feb 2, 2018
1 parent 1286bfd commit 50b2285e0ee5d70f02a3322245ebfc8e6a6bcd34
Showing with 162 additions and 2 deletions.
  1. +153 −0 PiBotBrain/roboclaw_stub.py
  2. +1 −0 PiBotBrain/templates/connect_menu.html
  3. +8 −2 PiBotBrain/testconfig.py
@@ -0,0 +1,153 @@
# This class implements a subset of the full RoboClaw API as released by
# Ion Motion Control for testing purposes. It allows running our control
# app in the absence of an actual RoboClaw attached to the computer.

# The functional fidelity of this class only needs to be enough to
# exercise features of the control application.

import random
import serial
import struct
import time

class Roboclaw_stub:
'Stub of Roboclaw Interface Class'

def __init__(self):
# Values that would otherwise be stored in RoboClaw
self.config = 0
self.encoderM1 = 0
self.encoderM2 = 0
self.encoderModeM1 = 0
self.encoderModeM2 = 0
self.minVoltage = 115
self.maxVoltage = 360
self.maxCurrentM1 = 500
self.maxCurrentM2 = 500
self.pwmMode = 0
self.pinS3 = self.pinS4 = self.pinS5 = 0
self.vpm1 = self.vim1 = self.vdm1 = self.vqppsm1 = 0
self.vpm2 = self.vim2 = self.vdm2 = self.vqppsm2 = 0
self.ppm1 = self.pim1 = self.pdm1 = self.pimaxm1 = self.pdeadm1 = self.pminm1 = self.pmaxm1 = 0
self.ppm2 = self.pim2 = self.pdm2 = self.pimaxm2 = self.pdeadm2 = self.pminm2 = self.pmaxm2 = 0

def ForwardM1(self,address,val):
return True

def ForwardM2(self,address,val):
return True

def ReadEncM1(self,address):
return (1, self.encoderM1)

def ReadEncM2(self,address):
return (1, self.encoderM2)

def ReadVersion(self,address):
return (1, "RoboClaw API Test Stub")

def SetEncM1(self,address,cnt):
self.encoderM1 = cnt
return True

def SetEncM2(self,address,cnt):
self.encoderM2 = cnt
return True

def SetM1VelocityPID(self,address,p,i,d,qpps):
self.vpm1 = p, self.vim1 = i, self.vdm1 = d, self.vqppsm1 = qpps
return True

def SetM2VelocityPID(self,address,p,i,d,qpps):
self.vpm2 = p, self.vim2 = i, self.vdm2 = d, self.vqppsm2 = qpps
return True

def SpeedM1M2(self,address,m1,m2):
return True

def ReadM1VelocityPID(self,address):
return (1, self.vpm1, self.vim1, self.vdm1, self.vqppsm1)

def ReadM2VelocityPID(self,address):
return (1, self.vpm2, self.vim2, self.vdm2, self.vqppsm2)

def SetMainVoltages(self,address,min, max):
self.minVoltage = min
self.maxVoltage = max
return True

def ReadMinMaxMainVoltages(self,address):
return (1, self.minVoltage, self.maxVoltage)

def SetM1PositionPID(self,address,kp,ki,kd,kimax,deadzone,min,max):
self.ppm1, self.pim1, self.pdm1, self.pimaxm1, self.pdeadm1, self.pminm1, self.pmaxm1 = kp,ki,kd,kimax,deadzone,min,max
return True

def SetM2PositionPID(self,address,kp,ki,kd,kimax,deadzone,min,max):
self.ppm2, self.pim2, self.pdm2, self.pimaxm2, self.pdeadm2, self.pminm2, self.pmaxm2 = kp,ki,kd,kimax,deadzone,min,max
return True

def ReadM1PositionPID(self,address):
return (1, self.ppm1, self.pim1, self.pdm1, self.pimaxm1, self.pdeadm1, self.pminm1, self.pmaxm1)

def ReadM2PositionPID(self,address):
return (1, self.ppm2, self.pim2, self.pdm2, self.pimaxm2, self.pdeadm2, self.pminm2, self.pmaxm2)

def SpeedAccelDeccelPositionM1M2(self,address,accel1,speed1,deccel1,position1,accel2,speed2,deccel2,position2,buffer):
return True

def SetPinFunctions(self,address,S3mode,S4mode,S5mode):
self.pinS3, self.pinS4, self.pinS5 = S3mode, S4mode, S5mode
return True

def ReadPinFunctions(self,address):
return (1,self.pinS3, self.pinS4, self.pinS5)

def ReadError(self,address):
return (1, 0)

def ReadEncoderModes(self,address):
return (1, self.encoderModeM1, self.encoderModeM2)

def SetM1EncoderMode(self,address,mode):
self.encoderModeM1 = mode
return True

def SetM2EncoderMode(self,address,mode):
self.encoderModeM2 = mode
return True

def WriteNVM(self,address):
return True

def SetConfig(self,address,config):
self.config = config
return True

def GetConfig(self,address):
return (1, self.config)

def SetM1MaxCurrent(self,address,max):
self.maxCurrentM1 = max
return True;

def SetM2MaxCurrent(self,address,max):
self.maxCurrentM2 = max
return True;

def ReadM1MaxCurrent(self,address):
return (1,self.maxCurrentM1)

def ReadM2MaxCurrent(self,address):
return (1,self.maxCurrentM2)

def SetPWMMode(self,address,mode):
self.pwmMode = mode
return True;

def ReadPWMMode(self,address):
return (1,self.pwmMode)

def Open(self):
return 1

@@ -10,6 +10,7 @@ <h1>Connect menu</h1>
<li><button onclick="document.getElementById('serialPortInput').value='/dev/tty.usbmodem'">MacOS USB Serial</button></li>
<li><button onclick="document.getElementById('serialPortInput').value='/dev/ttyS3'">WSL passthrough USB Serial COM3</button></li>
<li><button onclick="document.getElementById('serialPortInput').value='COM3'">Windows USB Serial COM3</button></li>
<li><button onclick="document.getElementById('serialPortInput').value='Test_Stub'">No RoboClaw - use test stub</button></li>
</ul>
</p>
<br/>
@@ -3,6 +3,7 @@
from flask import Flask, flash, g, redirect, render_template, request, session, url_for
import os
from roboclaw import Roboclaw
from roboclaw_stub import Roboclaw_stub

defaultAccelDecel = 2400
defaultSpeed = 240
@@ -136,8 +137,13 @@ def connect_menu():
interCharTimeout = float(request.form['interCharTimeout'])
retries = int(request.form['retries'])

# Create the Roboclaw object against the specified serial port
newrc = Roboclaw(portName,baudrate,interCharTimeout,retries)
if portName == 'Test_Stub':
# No RoboClaw - use the test stub.
newrc = Roboclaw_stub()
else:
# Create the Roboclaw object against the specified serial port
newrc = Roboclaw(portName,baudrate,interCharTimeout,retries)

if newrc.Open():
rc = newrc
flash(successPrefix + "Roboclaw API connected to " + portName)

0 comments on commit 50b2285

Please sign in to comment.