Picadae hardware and control code
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

picadae_shell.py 7.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. ##| Copyright: (C) 2018-2020 Kevin Larke <contact AT larke DOT org>
  2. ##| License: GNU GPL version 3.0 or above. See the accompanying LICENSE file.
  3. import os,sys,argparse,yaml,types,select,serial,logging,time
  4. from picadae_api import Picadae
  5. from picadae_api import Result
  6. class PicadaeShell:
  7. def __init__( self, cfg ):
  8. self.p = None
  9. self.parseD = {
  10. 'q':{ "func":None, "minN":0, "maxN":0, "help":"quit"},
  11. '?':{ "func":"_help", "minN":0, "maxN":0, "help":"Print usage text."},
  12. 'w':{ "func":"_write", "minN":-1, "maxN":-1,"help":"write <i2c_addr> <reg_addr> <data0> ... <dataN>"},
  13. 'r':{ "func":"_read", "minN":4, "maxN":4, "help":"read <i2c_addr> <src> <reg_addr> <byteN> src: 0=reg_array 1=vel_table 2=eeprom"},
  14. 'v':{ "func":"note_on_vel", "minN":2, "maxN":2, "help":"note-on <pitch> <vel>"},
  15. 'u':{ "func":"note_on_us", "minN":2, "maxN":3, "help":"note-on <pitch> <usec> <prescale> (1=1, 2=8 .5us, 3=64 4us,(4)=256 16us, 5=1024 64us)"},
  16. 'o':{ "func":"note_off", "minN":1, "maxN":1, "help":"note-off <pitch>"},
  17. 'T':{ "func":"set_vel_map", "minN":3, "maxN":3, "help":"table <pitch> <vel> <usec>"},
  18. 't':{ "func":"get_vel_map", "minN":2, "maxN":2, "help":"table <pitch> <vel>"},
  19. 'D':{ "func":"set_pwm_duty", "minN":2, "maxN":4, "help":"duty <pitch> <percent> {<hz> {<div>}} " },
  20. 'd':{ "func":"get_pwm_duty", "minN":1, "maxN":1, "help":"duty <pitch>"},
  21. 'H':{ "func":"set_hold_delay", "minN":2, "maxN":2, "help":"hold delay <pitch> <usec>"},
  22. 'h':{ "func":"get_hold_delay", "minN":1, "maxN":1, "help":"hold delay <pitch>"},
  23. 'F':{ "func":"set_pwm_freq", "minN":2, "maxN":2, "help":"pwm freq <pitch> <hz> 254=~123Hz"},
  24. 'f':{ "func":"get_pwm_freq", "minN":1, "maxN":1, "help":"pwm freq <pitch>"},
  25. 'I':{ "func":"set_pwm_div", "minN":2, "maxN":2, "help":"pwm div <pitch> <div> div:2=2,3=4,4=8,(5)=16 1us,6=32,7=64,8=128,9=256,10=512 32us, 11=1024,12=2048,13=4096,14=8192,15=16384"},
  26. 'i':{ "func":"get_pwm_div", "minN":1, "maxN":1, "help":"pwm div <pitch>"},
  27. 'A':{ "func":"set_flags", "minN":2, "maxN":2, "help":"set flags <pitch> <flags>"},
  28. 'a':{ "func":"get_flags", "minN":1, "maxN":1, "help":"get flags <pitch>"},
  29. 'W':{ "func":"write_table", "minN":1, "maxN":1, "help":"write_table <pitch>"},
  30. 'N':{ "func":"make_note", "minN":3, "maxN":3, "help":"note <pitch> <atkUs> <durMs>"},
  31. 'S':{ "func":"make_seq", "minN":5, "maxN":5, "help":"seq <pitch> <atkUs> <durMs> <deltaUs> <note_count>"},
  32. 'C':{ "func":"make_scale", "minN":4, "maxN":4, "help":"scale <pitch0> <pitch1> <atkUs> <durMs>"},
  33. 'L':{ "func":"set_log_level", "minN":1, "maxN":1, "help":"log <level> (0-1)."}
  34. }
  35. def _help( self, _=None ):
  36. for k,d in self.parseD.items():
  37. s = "{} = {}".format( k, d['help'] )
  38. print(s)
  39. return Result()
  40. def _write( self, argL ):
  41. return self.p.write(argL[0], argL[1], argL[2:])
  42. def _read( self, i2c_addr, src_id, reg_addr, byteN ):
  43. return self.p.block_on_picadae_read(i2c_addr, src_id, reg_addr, byteN)
  44. def _syntaxError( self, msg ):
  45. print("Syntax Error: " + msg )
  46. return Result()
  47. def _exec_cmd( self, tokL ):
  48. result = Result()
  49. if len(tokL) <= 0:
  50. return None
  51. opcode = tokL[0]
  52. if opcode not in self.parseD:
  53. return self._syntaxError("Unknown opcode: '{}'.".format(opcode))
  54. d = self.parseD[ opcode ]
  55. func_name = d['func']
  56. func = None
  57. # find the function associated with this command
  58. if hasattr(self, func_name ):
  59. func = getattr(self, func_name )
  60. elif hasattr(self.p, func_name ):
  61. func = getattr(self.p, func_name )
  62. else:
  63. return self._syntaxError("Exec function not found: '{}'.".format(func_name))
  64. try:
  65. # convert the parameter list into integers
  66. argL = [ int(tokL[i]) for i in range(1,len(tokL)) ]
  67. except:
  68. return self._syntaxError("Unable to create integer arguments.")
  69. # validate the count of command args
  70. if d['minN'] != -1 and (d['minN'] > len(argL) or len(argL) > d['maxN']):
  71. return self._syntaxError("Argument count mismatch. {} is out of range:{} to {}".format(len(argL),d['minN'],d['maxN']))
  72. # call the command function
  73. result = func(*argL)
  74. return result
  75. def run( self ):
  76. # create the API object
  77. self.p = Picadae( cfg.key_mapL, cfg.i2c_base_addr, cfg.serial_dev, cfg.serial_baud, cfg.prescaler_usec )
  78. # wait for the letter 'a' to come back from the serial port
  79. result = self.p.wait_for_serial_sync(timeoutMs=cfg.serial_sync_timeout_ms)
  80. if not result:
  81. print("Serial port sync failed.")
  82. else:
  83. print(result.value)
  84. print("'q'=quit '?'=help")
  85. time_out_secs = 1
  86. while True:
  87. # wait for keyboard activity
  88. i, o, e = select.select( [sys.stdin], [], [], time_out_secs )
  89. if (i):
  90. # read the command
  91. s = sys.stdin.readline().strip()
  92. # tokenize the command
  93. tokL = s.split(' ')
  94. # if this is the 'quit' command
  95. if tokL[0] == 'q':
  96. break
  97. # execute the command
  98. result = self._exec_cmd( tokL )
  99. if result.value:
  100. print(result.value)
  101. self.p.close()
  102. def parse_args():
  103. """Parse the command line arguments."""
  104. descStr = """Picadae auto-calibrate."""
  105. logL = ['debug','info','warning','error','critical']
  106. ap = argparse.ArgumentParser(description=descStr)
  107. ap.add_argument("-s","--setup", default="picadae_cmd.yml", help="YAML configuration file.")
  108. ap.add_argument("-c","--cmd", nargs="*", help="Give a command as multiple tokens")
  109. ap.add_argument("-r","--run", help="Run a named command list from the setup file.")
  110. ap.add_argument("-l","--log_level",choices=logL, default="warning", help="Set logging level: debug,info,warning,error,critical. Default:warning")
  111. return ap.parse_args()
  112. def parse_yaml_cfg( fn ):
  113. """Parse the YAML configuration file."""
  114. cfg = None
  115. with open(fn,"r") as f:
  116. cfgD = yaml.load(f, Loader=yaml.FullLoader)
  117. cfg = types.SimpleNamespace(**cfgD['picadae_cmd'])
  118. return cfg
  119. if __name__ == "__main__":
  120. args = parse_args()
  121. cfg = parse_yaml_cfg( args.setup )
  122. app = PicadaeShell(cfg)
  123. app.run()