191 lines
4.9 KiB
Python
191 lines
4.9 KiB
Python
import os
|
|
import logging
|
|
logging.basicConfig(
|
|
format='[%(asctime)s] %(levelname)s %(module)s/%(funcName)s - %(message)s',
|
|
level=logging.DEBUG if os.environ.get('DEBUG') else logging.INFO)
|
|
|
|
logging.info('')
|
|
logging.info('BOOT UP')
|
|
|
|
import asyncio
|
|
import aiomqtt
|
|
import serial
|
|
import glob
|
|
|
|
import dyn4
|
|
|
|
ENCODER_PPR = 65536
|
|
|
|
ONE_MOTOR = os.environ.get('ONE_MOTOR', False)
|
|
|
|
dmm1 = None
|
|
dmm2 = None
|
|
|
|
|
|
async def send_mqtt(topic, message):
|
|
try:
|
|
async with aiomqtt.Client('localhost') as client:
|
|
await client.publish(topic, payload=message.encode())
|
|
except BaseException as e:
|
|
logging.error('Problem sending MQTT topic %s, message %s: %s - %s', topic, message, e.__class__.__name__, e)
|
|
await asyncio.sleep(1)
|
|
return False
|
|
|
|
|
|
def set_motors(rpm):
|
|
# we want these to happen no matter what so motors stay in sync
|
|
|
|
logging.debug('Setting motor RPMs to %s', rpm)
|
|
|
|
if dmm1:
|
|
try:
|
|
dmm1.set_speed(rpm)
|
|
except BaseException as e:
|
|
logging.error('Problem setting Motor1 rpm %s: %s - %s', rpm, e.__class__.__name__, e)
|
|
|
|
if dmm2:
|
|
try:
|
|
dmm2.set_speed(rpm)
|
|
except BaseException as e:
|
|
logging.error('Problem setting Motor2 rpm %s: %s - %s', rpm, e.__class__.__name__, e)
|
|
|
|
|
|
async def process_mqtt(message):
|
|
try:
|
|
text = message.payload.decode()
|
|
except UnicodeDecodeError:
|
|
return
|
|
|
|
topic = message.topic.value
|
|
logging.debug('MQTT topic: %s, message: %s', topic, text)
|
|
|
|
if topic == 'motion/set_rpm':
|
|
rpm = int(float(text))
|
|
set_motors(rpm)
|
|
|
|
|
|
async def monitor_mqtt():
|
|
await asyncio.sleep(1)
|
|
|
|
# from https://sbtinstruments.github.io/aiomqtt/reconnection.html
|
|
# modified to make new client since their code didn't work
|
|
# https://github.com/sbtinstruments/aiomqtt/issues/269
|
|
while True:
|
|
try:
|
|
async with aiomqtt.Client('localhost') as client:
|
|
await client.subscribe('motion/#')
|
|
async for message in client.messages:
|
|
await process_mqtt(message)
|
|
except aiomqtt.MqttError:
|
|
logging.info('MQTT connection lost, reconnecting in 1 second...')
|
|
await asyncio.sleep(1)
|
|
|
|
|
|
async def init_motor(path, name):
|
|
try:
|
|
dmm = dyn4.DMMDrive(path, 0)
|
|
except BaseException as e:
|
|
logging.error('Problem opening %s port %s: %s - %s', name, path, e.__class__.__name__, e)
|
|
await send_mqtt('server/motor_status', name + ' disconnected')
|
|
await asyncio.sleep(1)
|
|
return False
|
|
|
|
logging.info('Port %s connected.', path)
|
|
await send_mqtt('server/motor_status', name + ' connected')
|
|
|
|
dmm.set_speed(0)
|
|
|
|
return dmm
|
|
|
|
|
|
async def read_motor(dmm, name):
|
|
try:
|
|
return dmm.read_AbsPos32()
|
|
except BaseException as e:
|
|
# any problems, kill both motors
|
|
# TODO: add tripping e-stop
|
|
set_motors(0)
|
|
|
|
logging.error('Problem reading %s: %s - %s', name, e.__class__.__name__, e)
|
|
await send_mqtt('server/motor_status', name + ' disconnected')
|
|
dmm = False
|
|
return False
|
|
|
|
|
|
async def check_sync(rev1, rev2):
|
|
global dmm1, dmm2
|
|
|
|
difference = rev1 - rev2
|
|
|
|
if abs(difference) > 1.0:
|
|
# out of sync, kill both motors
|
|
# TODO: add tripping e-stop
|
|
set_motors(0)
|
|
|
|
dmm1 = False
|
|
dmm2 = False
|
|
|
|
logging.error('MOTOR READINGS OUT OF SYNC! Motor1: %s, Motor2: %s, difference: %s', rev1, rev2, difference)
|
|
await send_mqtt('server/motor_status', 'Motor desync')
|
|
|
|
|
|
async def monitor_dyn4():
|
|
global dmm1, dmm2
|
|
|
|
while True:
|
|
await asyncio.sleep(0.1)
|
|
|
|
if not dmm1:
|
|
dmm1 = await init_motor('/dev/ttyUSB0', 'Motor1')
|
|
continue
|
|
|
|
if not dmm2 and not ONE_MOTOR:
|
|
dmm2 = await init_motor('/dev/ttyUSB1', 'Motor2')
|
|
continue
|
|
|
|
pos1 = await read_motor(dmm1, 'Motor1')
|
|
if pos1 is False:
|
|
dmm1 = False
|
|
continue
|
|
rev1 = pos1 / ENCODER_PPR
|
|
|
|
if ONE_MOTOR:
|
|
revolutions = rev1
|
|
else:
|
|
pos2 = await read_motor(dmm2, 'Motor2')
|
|
if pos2 is False:
|
|
dmm2 = False
|
|
continue
|
|
rev2 = pos2 / ENCODER_PPR
|
|
|
|
revolutions = (rev1 + rev2) / 2.0
|
|
|
|
await check_sync(rev1, rev2)
|
|
|
|
topic = 'server/position'
|
|
message = str(round(revolutions, 5))
|
|
await send_mqtt(topic, message)
|
|
|
|
|
|
|
|
def task_died(future):
|
|
if os.environ.get('SHELL'):
|
|
logging.error('Motion control task died!')
|
|
else:
|
|
logging.error('Motion control task died! Waiting 60s and exiting...')
|
|
time.sleep(60)
|
|
exit()
|
|
|
|
|
|
def main():
|
|
loop = asyncio.get_event_loop()
|
|
|
|
a = loop.create_task(monitor_dyn4()).add_done_callback(task_died)
|
|
b = loop.create_task(monitor_mqtt()).add_done_callback(task_died)
|
|
|
|
loop.run_forever()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|