Picadae hardware and control code
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

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()