1 __author__ = 'Copyright (c) 2013 Alan Yorinks All rights reserved.'
2
3 """
4 Copyright (c) 2013-14 Alan Yorinks All rights reserved.
5
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public
8 License as published by the Free Software Foundation; either
9 version 3 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 """
20
21 from collections import deque
22 import threading
23 import sys
24 import time
25 import os.path
26
27 import ino_uploader
28 from pymata_serial import PyMataSerial
29 from pymata_command_handler import PyMataCommandHandler
30
31 SCRIPT_PATH = os.path.dirname( __file__ )
32 SKETCHES_DIR = SCRIPT_PATH + "/ArduinoSketch"
33 if not os.path.exists( SKETCHES_DIR ):
34 SKETCHES_DIR = SCRIPT_PATH + "/../ArduinoSketch"
35
36 STANDARD_FIRMATA_DIR = SKETCHES_DIR + "/Firmata/examples/StandardFirmata"
37 STANDARD_FIRMATA_LIBS = [
38 SKETCHES_DIR + "/Firmata"
39 ]
40
41 NOT_SO_STANDARD_FIRMATA_DIR = SKETCHES_DIR + "/NotSoStandardFirmata/examples/NotSoStandardFirmata"
42 NOT_SO_STANDARD_FIRMATA_LIBS = [
43 SKETCHES_DIR + "/AdaEncoder",
44 SKETCHES_DIR + "/ByteBuffer",
45 SKETCHES_DIR + "/cbiface",
46 SKETCHES_DIR + "/cppfix",
47 SKETCHES_DIR + "/MemoryFree",
48 SKETCHES_DIR + "/NotSoStandardFirmata",
49 SKETCHES_DIR + "/ooPinChangeInt"
50 ]
51
52
53
55 """
56 This class contains the complete set of API methods that permit control of an Arduino
57 Micro-Controller utilizing Firmata or its derivatives.
58
59 For information about the Firmata protocol, refer to: http://firmata.org/wiki/Protocol
60 """
61
62 HIGH = 1
63 LOW = 0
64
65 REPORTING_ENABLE = 1
66 REPORTING_DISABLE = 0
67
68
69
70
71
72 _command_deque = deque()
73
74
75 _command_handler = None
76
77
78 _arduino = None
79
80
81
82 _data_lock = threading.Lock()
83
84
85 I2C_WRITE = 0B00000000
86 I2C_READ = 0B00001000
87 I2C_READ_CONTINUOUSLY = 0B00010000
88 I2C_STOP_READING = 0B00011000
89 I2C_READ_WRITE_MODE_MASK = 0B00011000
90
91
92 TONE_TONE = 0
93 TONE_NO_TONE = 1
94
95
96 INPUT = None
97 OUTPUT = None
98 ANALOG = None
99 PWM = None
100 SERVO = None
101 I2C = None
102 TONE = None
103 SONAR = None
104 IGNORE = None
105 ENCODER = None
106 DIGITAL = None
107
108
109 digital_output_port_pins = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
110 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
111
112 STANDARD_FIRMATA = ( STANDARD_FIRMATA_DIR, STANDARD_FIRMATA_LIBS )
113 NOT_SO_STANDARD_FIRMATA = ( NOT_SO_STANDARD_FIRMATA_DIR, NOT_SO_STANDARD_FIRMATA_LIBS )
114
115
118 """
119 The constructor tries to connect to an Arduino or Arduino compatible running
120 Firmata. If Firmata is not found on the Arduino, then we can optionally try to
121 program the Arduino with the Firmata sketch
122 @param port_id: Communications port specifier (COM3, /dev/ttyACM0, etc)
123 @param max_wait_time: Maximum time to wait when trying to connect to Firmata
124 @param program_if_needed: Attempt to upload the Firmata sketch if it's not found
125 on the Arduino.
126 @param board_model: The type of Arduino being used. This is only needed if the
127 board is to be programmed. Run 'ino list-models' at the command line for a
128 list of available boards.
129 @param firmata_type: A directory containing the firmata sketch, and a list of supporting
130 libs that are used when uploading firmata
131 """
132
133 if not self.connect_to_firmata( port_id, max_wait_time ):
134
135 able_to_connect = False
136
137 if program_if_needed:
138 if not self.upload_firmata_sketch( port_id, board_model, firmata_type ):
139
140 raise Exception( "Unable to upload Firmata sketch" )
141
142 else:
143
144 able_to_connect = self.connect_to_firmata( port_id, max_wait_time )
145
146 if not able_to_connect:
147
148 raise Exception( "Unable to connect to Firmata" )
149
244
246 """
247 Uses the Ino library to upload Firmata to the Arduino attached to the given serial port
248 @param port_id: The serial port to use
249 """
250
251 firmata_dir, firmata_libs = firmata_type
252 return ino_uploader.upload( firmata_dir, port_id, board_model, firmata_libs )
253
260
271
285
287 """
288 Send a Firmata capability query message via sysex. Client retrieves the results with a
289 call to get_capability_query_results()
290 The Arduino can be rather slow in responding to this command. For
291 the Mega 2560 R3 it has taken up to 25 seconds for a response.
292 """
293 self._command_handler.send_sysex(self._command_handler.CAPABILITY_QUERY, None)
294
296 """
297 This method will close the transport (serial port) and exit
298 @return: No return value, but sys.exit(0) is called.
299 """
300 self.reset( reset_sleep_time=0.1 )
301
302 self._command_handler.stop()
303 self._arduino.stop()
304 self._command_handler.join()
305 self._arduino.join()
306
307 print "PyMata close(): Calling sys.exit(0): Hope to see you soon!"
308 sys.exit(0)
309
321
349
350
351
360
371
380
382 """
383 Enables digital reporting. By turning reporting on for all 8 bits in the "port" -
384 this is part of Firmata's protocol specification.
385 @param pin: Pin and all pins for this port
386 @return: No return value
387 """
388 port = pin / 8
389 command = [self._command_handler.REPORT_DIGITAL + port, self.REPORTING_ENABLE]
390 self._command_handler.send_command(command)
391
392
410
412 """
413 This method will send an extended data analog output command to the selected pin
414 @param pin: 0 - 127
415 @param data: 0 - 0xfffff
416 """
417 analog_data = [pin, data & 0x7f, (data >> 7) & 0x7f, data >> 14]
418 self._command_handler.send_sysex(self._command_handler.EXTENDED_ANALOG, analog_data)
419
421 """
422 A list is returned containing the latch state for the pin, the latched value, and the time stamp
423 [pin_num, latch_state, latched_value, time_stamp]
424 If the the latch state is LATCH_LATCHED, the table is reset (data and timestamp set to zero)
425 @param pin: Pin number.
426 @return: [pin, latch_state, latch_data_value, time_stamp]
427 """
428 return self._command_handler.get_analog_latch_data(pin)
429
431 """
432 Call this method after calling analog_mapping_query() to retrieve its results
433 @return: raw data returned by firmata
434 """
435 return self._command_handler.analog_mapping_query_results
436
438 """
439 This method returns a list of lists representing the current pin mode and
440 associated data values for all analog pins.
441 All configured pin types, both input and output will be listed. Output pin data will contain zero.
442 @return: The last update of the digital response table
443 """
444 return self._command_handler.get_analog_response_table()
445
447 """
448 Retrieve the data returned by a previous call to capability_query()
449 @return: Raw capability data returned by firmata
450 """
451 return self._command_handler.capability_query_results
452
454 """
455 A list is returned containing the latch state for the pin, the latched value, and the time stamp
456 [pin_num, latch_state, latched_value, time_stamp]
457 If the the latch state is LATCH_LATCHED, the table is reset (data and timestamp set to zero)
458 @param pin: Pin number.
459 @return: [pin, latch_state, latch_data_value, time_stamp]
460 """
461 return self._command_handler.get_digital_latch_data(pin)
462
464 """
465 This method returns a list of lists representing the current pin mode
466 and associated data for all digital pins.
467 All pin types, both input and output will be listed. Output pin data will contain zero.
468 @return: The last update of the digital response table
469 """
470 return self._command_handler.get_digital_response_table()
471
473 """
474 Retrieve the firmata version information returned by a previous call to refresh_report_version()
475 @return: Firmata_version list [major, minor] or None
476 """
477 return self._command_handler.firmata_version
478
479
481 """
482 Retrieve the firmware id information returned by a previous call to refresh_report_firmware()
483 @return: Firmata_firmware list [major, minor, file_name] or None
484 """
485 return self._command_handler.firmata_firmware
486
488 """
489 This method returns the results of a previous call to pin_state_query() and then resets
490 the pin state query data to None
491
492 @return: Raw pin state query data
493 """
494 r_data = self._command_handler.last_pin_query_results
495 self._command_handler.last_pin_query_results = []
496 return r_data
497
498
500 """
501 Returns the PyMata version number in a list: [Major Number, Minor Number]
502
503 @return:
504 """
505 return [1, 5]
506
507
508
510 """
511 Retrieve Ping (HC-SR04 type) data. The data is presented as a dictionary.
512 The 'key' is the trigger pin specified in sonar_config() and the 'data' is the
513 current measured distance (in centimeters)
514 for that pin. If there is no data, the value is set to IGNORE (127).
515
516 @return: active_sonar_map
517 """
518 return self._command_handler.active_sonar_map
519
520
521 - def i2c_config(self, read_delay_time=0, pin_type=None, clk_pin=0, data_pin=0):
551
552 - def i2c_read(self, address, register, number_of_bytes, read_type):
553 """
554 This method requests the read of an i2c device. Results are retrieved by a call to
555 i2c_get_read_data()
556 @param address: i2c device address
557 @param register: register number (can be set to zero)
558 @param number_of_bytes: number of bytes expected to be returned
559 @param read_type: I2C_READ or I2C_READ_CONTINUOUSLY
560 """
561 data = [address, read_type, register & 0x7f, register >> 7,
562 number_of_bytes & 0x7f, number_of_bytes >> 7]
563 self._command_handler.send_sysex(self._command_handler.I2C_REQUEST, data)
564
566 """
567 Write data to an i2c device.
568 @param address: i2c device address
569 @param args: A variable number of bytes to be sent to the device
570 """
571 data = [address, self.I2C_WRITE]
572 for item in args:
573 data.append(item)
574 self._command_handler.send_sysex(self._command_handler.I2C_REQUEST, data)
575
583
585 """
586 This method retrieves the i2c read data as the result of an i2c_read() command.
587 @param address: i2c device address
588 @return: raw data read from device
589 """
590 if self._command_handler.i2c_map.has_key(address):
591 return self._command_handler.i2c_map[address]
592
600
601 - def play_tone(self, pin, tone_command, frequency, duration):
602 """
603 This method will call the Tone library for the selected pin.
604 If the tone command is set to TONE_TONE, then the specified tone will be played.
605 Else, if the tone command is TONE_NO_TONE, then any currently playing tone will be disabled.
606 It is intended for a future release of Arduino Firmata
607 @param pin: Pin number
608 @param tone_command: Either TONE_TONE, or TONE_NO_TONE
609 @param frequency: Frequency of tone
610 @param duration: Duration of tone in milliseconds
611 @return: No return value
612 """
613
614
615 if tone_command == self.TONE_TONE:
616
617 if duration:
618 data = [tone_command, pin, frequency & 0x7f, frequency >> 7, duration & 0x7f, frequency >> 7]
619
620 else:
621 data = [tone_command, pin, frequency & 0x7f, frequency >> 7]
622
623 self._command_handler.digital_response_table[pin][self._command_handler.RESPONSE_TABLE_MODE] = \
624 self.TONE
625
626 else:
627 data = [tone_command, pin]
628 self._command_handler.send_sysex(self._command_handler.TONE_PLAY, data)
629
636
643
644 - def reset(self,reset_sleep_time=2):
660
662 """
663 This method "arms" an analog pin for its data to be latched and saved in the latching table
664 @param pin: Analog pin number (value following an 'A' designator, i.e. A5 = 5
665 @param threshold_type: ANALOG_LATCH_GT | ANALOG_LATCH_LT | ANALOG_LATCH_GTE | ANALOG_LATCH_LTE
666 @param threshold_value: numerical value - between 0 and 1023
667 @return: True if successful, False if parameter data is invalid
668 """
669 if self.ANALOG_LATCH_GT <= threshold_type <= self.ANALOG_LATCH_LTE:
670 if 0 <= threshold_value <= 1023:
671 self._command_handler.set_analog_latch(pin, threshold_type, threshold_value)
672 return True
673 else:
674 return False
675
677 """
678 This method "arms" a digital pin for its data to be latched and saved in the latching table
679 @param pin: Digital pin number
680 @param threshold_type: DIGITAL_LATCH_HIGH | DIGITAL_LATCH_LOW
681 @return: True if successful, False if parameter data is invalid
682 """
683 if 0 <= threshold_type <= 1:
684 self._command_handler.set_digital_latch(pin, threshold_type)
685 return True
686 else:
687 return False
688
721
723 """
724 This method sends the desired sampling interval to Firmata.
725 Note: Standard Firmata will ignore any interval less than 10 milliseconds
726 @param interval: Integer value for desired sampling interval in milliseconds
727 @return: No return value.
728 """
729 data = [interval & 0x7f, interval >> 7]
730 self._command_handler.send_sysex(self._command_handler.SAMPLING_INTERVAL, data)
731
732
733 - def servo_config(self, pin, min_pulse=544, max_pulse=2400):
734 """
735 Configure a pin as a servo pin. Set pulse min, max in ms.
736 @param pin: Servo Pin.
737 @param min_pulse: Min pulse width in ms.
738 @param max_pulse: Max pulse width in ms.
739 @return: No return value
740 """
741 self.set_pin_mode(pin, self.SERVO, self.OUTPUT)
742 command = [self._command_handler.SERVO_CONFIG, pin, min_pulse & 0x7f, min_pulse >> 7, max_pulse & 0x7f,
743 max_pulse >> 7]
744
745 self._command_handler.send_command(command)
746
747 - def sonar_config(self, trigger_pin, echo_pin, ping_interval=50):
748 """
749 Configure the pins, and ping interval for an HC-SR04 type device.
750 Single pin configuration may be used. To do so, set both the trigger and echo pins to the same value.
751 Up to a maximum of 6 SONAR devices is supported
752 If the maximum is exceeded a message is sent to the console and the request is ignored.
753 NOTE: data is measured in centimeters
754 :param trigger_pin: The pin number of for the trigger (transmitter).
755 :param echo_pin: The pin number for the received echo.
756 :param ping_interval: Minimum interval between pings. Lowest number to use is 33 ms.Max is 127
757 """
758 data = [trigger_pin, echo_pin, ping_interval]
759 self.set_pin_mode(trigger_pin, self.SONAR, self.INPUT)
760 self.set_pin_mode(echo_pin, self.SONAR, self.INPUT)
761
762 if len(self._command_handler.active_sonar_map) > 6:
763 print "sonar_config: maximum number of devices assigned - ignoring request"
764 return
765 else:
766 self._data_lock.acquire(True)
767 self._command_handler.active_sonar_map[trigger_pin] = self.IGNORE
768 self._data_lock.release()
769
770 self._command_handler.send_sysex(self._command_handler.SONAR_CONFIG, data)
771