This commit is contained in:
Jonas Zeunert
2024-08-16 21:57:55 +02:00
parent adeb5c5ec7
commit 4309a2d185
1696 changed files with 279655 additions and 0 deletions

View File

@@ -0,0 +1,17 @@
"""
Copyright (c) 2018-2019 Kaiyu Liu All rights reserved.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
Version 3 as published by the Free Software Foundation; either
or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""

View File

@@ -0,0 +1,914 @@
"""
Copyright (c) 2018-2019 Kaiyu Liu,, All rights reserved.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
Version 3 as published by the Free Software Foundation; either
or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
import os
import asyncio
from pymata_aio.constants import Constants
try:
from pyboard_core import PyBoardCore
except ImportError:
from .pyboard_core import PyBoardCore
class PyBoard(Constants):
"""
This class exposes and implements a proxy API for the pymata_core asyncio
API, If your application does not use asyncio directly, this is
the API that you should use.
"""
def __init__(self, com_port=None, arduino_wait=2, sleep_tune=0.0001, log_output=False,
ip_address=None, ip_port=2000, ip_handshake='*HELLO*'):
"""
Constructor for the PyBoard API
If log_output is set to True, a log file called 'pymata_log'
will be created in the current directory and all pymata_aio output
will be redirected to the log with no output appearing on the console.
:param arduino_wait: Amount of time to allow Arduino to finish its
reset (2 seconds for Uno, Leonardo can be 0)
:param sleep_tune: This parameter sets the amount of time PyMata core
uses to set asyncio.sleep
:param log_output: If True, pymata_aio.log is created and all
console output is redirected to this file.
:param com_port: If specified, auto port detection will not be
performed and this com port will be used.
:param ip_address: If using a WiFly module, set its address here
:param ip_port: Port to used with ip_address
:param ip_handshake: Connectivity handshake string sent by IP device
:returns: None
"""
self.log_out = log_output
self.loop = asyncio.get_event_loop()
self.sleep_tune = sleep_tune
self.core = PyBoardCore(arduino_wait, self.sleep_tune, log_output,
com_port, ip_address, ip_port, ip_handshake)
os.system('cls||clear')
self.core.start()
self.sleep(1)
def analogRead(self, pin):
"""
Retrieve the last data update for the specified analog pin.
It is intended for a polling application.
:param pin: Analog pin number (ex. A2 is specified as 2)
:returns: Last value reported for the analog pin
"""
task = asyncio.ensure_future(self.core.analog_read(pin))
value = self.loop.run_until_complete(task)
return value
def analogWrite(self, pin, value):
"""
Set the selected PWM pin to the specified value.
:param pin: PWM pin number
:param value: Set the selected pin to the specified
value. 0-0x4000 (14 bits)
:returns: No return value
"""
task = asyncio.ensure_future(self.core.analog_write(pin, value))
self.loop.run_until_complete(task)
def digitalRead(self, pin):
"""
Retrieve the last data update for the specified digital pin.
It is intended for a polling application.
:param pin: Digital pin number
:returns: Last value reported for the digital pin
"""
task = asyncio.ensure_future(self.core.digital_read(pin))
value = self.loop.run_until_complete(task)
return value
def digitalPinWrite(self, pin, value=0):
"""
Set the specified digital input pin to the provided value
:param pin: Digital pin to be set
:param value: 0 or 1
:returns: No return value
"""
task = asyncio.ensure_future(self.core.digital_pin_write(pin, value))
self.loop.run_until_complete(task)
def digitalWrite(self, pin, value=0):
"""
Set the specified digital input pin to the provided value
:param pin: Digital pin to be set
:param value: 0 or 1
:returns: No return value
"""
task = asyncio.ensure_future(self.core.digital_write(pin, value))
self.loop.run_until_complete(task)
def disableAnalogReporting(self, pin):
"""
Disables analog reporting for a single analog pin.
:param pin: Analog pin number. For example for A0, the number is 0.
:returns: No return value
"""
task = asyncio.ensure_future(self.core.disable_analog_reporting(pin))
self.loop.run_until_complete(task)
def disableDigitalReporting(self, pin):
"""
Disables digital reporting. By turning reporting off for this pin,
reporting is disabled for all 8 bits in the "port"
:param pin: Pin and all pins for this port
:returns: No return value
"""
task = asyncio.ensure_future(self.core.disable_digital_reporting(pin))
self.loop.run_until_complete(task)
def configEncoder(self, pin_a, pin_b, cb=None, cb_type=None,
hall_encoder=False):
"""
This command enables the rotary encoder (2 pin + ground) and will
enable encoder reporting.
This is a FirmataPlus feature.
Encoder data is retrieved by performing a digital_read from
pin a (encoder pin 1)
:param pin_a: Encoder pin 1.
:param pin_b: Encoder pin 2.
:param cb: callback function to report encoder changes
:param cb_type: Constants.CB_TYPE_DIRECT = direct call
or Constants.CB_TYPE_ASYNCIO = asyncio coroutine
:param hall_encoder: wheel hall_encoder - set to True to select
hall encoder support support.
:returns: No return value
"""
task = asyncio.ensure_future(self.core.encoder_config(pin_a, pin_b,
cb, cb_type,
hall_encoder))
self.loop.run_until_complete(task)
def encoderRead(self, pin):
"""
This method retrieves the latest encoder data value.
It is a FirmataPlus feature.
:param pin: Encoder Pin
:returns: encoder data value
"""
try:
task = asyncio.ensure_future(self.core.encoder_read(pin))
value = self.loop.run_until_complete(task)
return value
except RuntimeError:
self.shutdown()
def enableAnalogReporting(self, pin):
"""
Enables analog reporting for a single analog pin,
:param pin: Analog pin number. For example for A0, the number is 0.
:returns: No return value
"""
task = asyncio.ensure_future(self.core.enable_analog_reporting(pin))
self.loop.run_until_complete(task)
def enableDigitalReporting(self, pin):
"""
Enables digital reporting. By turning reporting on for all
8 bits in the "port".
This is part of Firmata's protocol specification.
:param pin: Pin and all pins for this port
:returns: No return value
"""
task = asyncio.ensure_future(self.core.enable_digital_reporting(pin))
self.loop.run_until_complete(task)
def extendedAnalog(self, pin, data):
"""
This method will send an extended-data analog write command
to the selected pin..
:param pin: 0 - 127
:param data: 0 - 0-0x4000 (14 bits)
:returns: No return value
"""
task = asyncio.ensure_future(self.core.extended_analog(pin, data))
self.loop.run_until_complete(task)
def getAnalogLatchData(self, pin):
"""
A list is returned containing the latch state for the pin, the
latched value, and the time stamp
[pin_num, latch_state, latched_value, time_stamp]
If the the latch state is LATCH_LATCHED, the table is reset
(data and timestamp set to zero)
It is intended to be used for a polling application.
:param pin: Pin number.
:returns: [latched_state, threshold_type, threshold_value,
latched_data, time_stamp]
"""
task = asyncio.ensure_future(self.core.get_analog_latch_data(pin))
l_data = self.loop.run_until_complete(task)
return l_data
def getAnalogMap(self, cb=None):
"""
This method requests and returns an analog map.
:param cb: Optional callback reference
:returns: An analog map response or None if a timeout occurs
"""
task = asyncio.ensure_future(self.core.get_analog_map())
report = self.loop.run_until_complete(task)
if cb:
cb(report)
else:
return report
def getCapabilityReport(self, raw=True, cb=None):
"""
This method retrieves the Firmata capability report
:param raw: If True, it either stores or provides the callback
with a report as list.
If False, prints a formatted report to the console
:param cb: Optional callback reference to receive a raw report
:returns: capability report
"""
task = asyncio.ensure_future(self.core.get_capability_report())
report = self.loop.run_until_complete(task)
if raw:
if cb:
cb(report)
else:
return report
else:
# noinspection PyProtectedMember
self.core._format_capability_report(report)
def getDigitalLatchData(self, pin):
"""
A list is returned containing the latch state for the pin, the
latched value, and the time stamp
[pin_num, latch_state, latched_value, time_stamp]
If the the latch state is LATCH_LATCHED, the table is reset
(data and timestamp set to zero).
It is intended for use by a polling application.
:param pin: Pin number.
:returns: [latched_state, threshold_type, threshold_value,
latched_data, time_stamp]
"""
task = asyncio.ensure_future(self.core.get_digital_latch_data(pin))
l_data = self.loop.run_until_complete(task)
return l_data
def getFirmwareVersion(self, cb=None):
"""
This method retrieves the Firmata firmware version
:param cb: Reference to a callback function
:returns:If no callback is specified, the firmware version
"""
task = asyncio.ensure_future(self.core.get_firmware_version())
version = self.loop.run_until_complete(task)
if cb:
cb(version)
else:
return version
def getProtocolVersion(self, cb=None):
"""
This method retrieves the Firmata protocol version
:param cb: Optional callback reference.
:returns:If no callback is specified, the firmware version
"""
task = asyncio.ensure_future(self.core.get_protocol_version())
version = self.loop.run_until_complete(task)
if cb:
cb(version)
else:
return version
def getPinState(self, pin, cb=None):
"""
This method retrieves a pin state report for the specified pin
:param pin: Pin of interest
:param cb: optional callback reference
:returns: pin state report
"""
task = asyncio.ensure_future(self.core.get_pin_state(pin))
report = self.loop.run_until_complete(task)
if cb:
cb(report)
else:
return report
def get_pymata_version(self):
"""
This method retrieves the firmata version number
:returns: PyMata version number.
"""
task = asyncio.ensure_future(self.core.get_pymata_version())
self.loop.run_until_complete(task)
def configI2C(self, read_delay_time=0):
"""
This method configures Arduino i2c with an optional read delay time.
:param read_delay_time: firmata i2c delay time
:returns: No return value
"""
task = asyncio.ensure_future(self.core.i2c_config(read_delay_time))
self.loop.run_until_complete(task)
def readI2CData(self, address):
"""
Retrieve result of last data read from i2c device.
i2c_read_request should be called before trying to retrieve data.
It is intended for use by a polling application.
:param address: i2c
:returns: last data read or None if no data is present.
"""
task = asyncio.ensure_future(self.core.i2c_read_data(address))
value = self.loop.run_until_complete(task)
return value
def readI2CRequest(self, address, register, number_of_bytes, read_type,
cb=None, cb_type=None):
"""
This method issues an i2c read request for a single read,continuous
read or a stop, specified by the read_type.
Because different i2c devices return data at different rates,
if a callback is not specified, the user must first call this method
and then call i2c_read_data after waiting for sufficient time for the
i2c device to respond.
Some devices require that transmission be restarted
(e.g. MMA8452Q accelerometer).
Use I2C_READ | I2C_RESTART_TX for those cases.
:param address: i2c device
:param register: i2c register number
:param number_of_bytes: number of bytes to be returned
:param read_type: Constants.I2C_READ, Constants.I2C_READ_CONTINUOUSLY
or Constants.I2C_STOP_READING.
Constants.I2C_RESTART_TX may be OR'ed when required
:param cb: optional callback reference
:param cb_type: Constants.CB_TYPE_DIRECT = direct call or
Constants.CB_TYPE_ASYNCIO = asyncio coroutine
:returns: No return value
"""
task = asyncio.ensure_future(self.core.i2c_read_request(address, register,
number_of_bytes,
read_type,
cb,
cb_type))
self.loop.run_until_complete(task)
def writeI2CRequest(self, address, args):
"""
Write data to an i2c device.
:param address: i2c device address
:param args: A variable number of bytes to be sent to the device
passed in as a list.
:returns: No return value
"""
task = asyncio.ensure_future(self.core.i2c_write_request(address, args))
self.loop.run_until_complete(task)
def keepAlive(self, period=1, margin=.3):
"""
Periodically send a keep alive message to the Arduino.
Frequency of keep alive transmission is calculated as follows:
keep_alive_sent = period - (period * margin)
:param period: Time period between keepalives.
Range is 0-10 seconds. 0 disables
the keepalive mechanism.
:param margin: Safety margin to assure keepalives
are sent before period expires. Range is 0.1 to 0.9
:returns: No return value
"""
asyncio.ensure_future(self.core.keep_alive(period, margin))
def playTone(self, pin, tone_command, frequency, duration=None):
"""
This method will call the Tone library for the selected pin.
It requires FirmataPlus to be loaded onto the arduino
If the tone command is set to TONE_TONE, then the specified
tone will be played.
Else, if the tone command is TONE_NO_TONE, then any currently
playing tone will be disabled.
:param pin: Pin number
:param tone_command: Either TONE_TONE, or TONE_NO_TONE
:param frequency: Frequency of tone
:param duration: Duration of tone in milliseconds
:returns: No return value
"""
task = asyncio.ensure_future(self.core.play_tone(pin, tone_command,
frequency, duration))
self.loop.run_until_complete(task)
def tone(self, pin, frequency, duration=None):
return playTone(self, pin, Constant.TONE_TONE, frequency, duration);
def noTone(self, pin):
return playTone(self, pin, Constant.TONE_NO_TONE, 0);
def sendReset(self):
"""
Send a Firmata reset command
:returns: No return value
"""
task = asyncio.ensure_future(self.core.send_reset())
self.loop.run_until_complete(task)
def configServo(self, pin, min_pulse=544, max_pulse=2400):
"""
This method configures the Arduino for servo operation.
:param pin: Servo control pin
:param min_pulse: Minimum pulse width
:param max_pulse: Maximum pulse width
:returns: No return value
"""
task = asyncio.ensure_future(self.core.servo_config(pin, min_pulse,
max_pulse))
self.loop.run_until_complete(task)
def setAnalogLatch(self, pin, threshold_type, threshold_value,
cb=None, cb_type=None):
"""
This method "arms" an analog pin for its data to be latched and
saved in the latching table.
If a callback method is provided, when latching criteria is achieved,
the callback function is called with latching data notification.
:param pin: Analog pin number (value following an 'A' designator,
i.e. A5 = 5
:param threshold_type: Constants.LATCH_GT | Constants.LATCH_LT |
Constants.LATCH_GTE | Constants.LATCH_LTE
:param threshold_value: numerical value - between 0 and 1023
:param cb: callback method
:param cb_type: Constants.CB_TYPE_DIRECT = direct call or
Constants.CB_TYPE_ASYNCIO = asyncio coroutine
:returns: True if successful, False if parameter data is invalid
"""
task = asyncio.ensure_future(self.core.set_analog_latch(pin, threshold_type, threshold_value, cb, cb_type))
result = self.loop.run_until_complete(task)
return result
def setDigitalLatch(self, pin, threshold_value, cb=None, cb_type=None):
"""
This method "arms" a digital pin for its data to be latched and saved
in the latching table.
If a callback method is provided, when latching criteria is achieved,
the callback function is called
with latching data notification.
:param pin: Digital pin number
:param threshold_value: 0 or 1
:param cb: callback function
:param cb_type: Constants.CB_TYPE_DIRECT = direct call or
Constants.CB_TYPE_ASYNCIO = asyncio coroutine
:returns: True if successful, False if parameter data is invalid
"""
task = asyncio.ensure_future(self.core.set_digital_latch(pin, threshold_value, cb, cb_type))
result = self.loop.run_until_complete(task)
return result
def pinMode(self, pin_number, pin_state, callback=None, cb_type=None):
"""
This method sets the pin mode for the specified pin.
:param pin_number: Arduino Pin Number
:param pin_state: INPUT/OUTPUT/ANALOG/PWM/PULLUP - for SERVO use
servo_config()
:param callback: Optional: A reference to a call back function to be
called when pin data value changes
:param cb_type: Constants.CB_TYPE_DIRECT = direct call or
Constants.CB_TYPE_ASYNCIO = asyncio coroutine
:returns: No return value
"""
task = asyncio.ensure_future(self.core.set_pin_mode(pin_number, pin_state, callback, cb_type))
self.loop.run_until_complete(task)
def setSamplingInterval(self, interval):
"""
This method sets the sampling interval for the Firmata loop method
:param interval: time in milliseconds
:returns: No return value
"""
task = asyncio.ensure_future(self.core.set_sampling_interval(interval))
self.loop.run_until_complete(task)
def delay(self, milliseconds):
"""
Perform an asyncio sleep for the time specified in milliseconds.
:param milliseconds: time in milliseconds
:returns: No return value
"""
self.sleep(milliseconds * 0.001)
def sleep(self, time):
"""
Perform an asyncio sleep for the time specified in seconds. T
his method should be used in place of time.sleep()
:param time: time in seconds
:returns: No return value
"""
try:
task = asyncio.ensure_future(self.core.sleep(time))
self.loop.run_until_complete(task)
except asyncio.CancelledError:
pass
except RuntimeError:
pass
def shutdown(self):
"""
Shutdown the application and exit
:returns: No return value
"""
task = asyncio.ensure_future(self.core.shutdown())
self.loop.run_until_complete(task)
# noinspection PyUnusedLocal
def configSonar(self, trigger_pin, echo_pin, cb=None, ping_interval=50,
max_distance=200, cb_type=None):
"""
Configure the pins,ping interval and maximum distance for an HC-SR04
type device.
Single pin configuration may be used. To do so, set both the trigger
and echo pins to the same value.
Up to a maximum of 6 SONAR devices is supported
If the maximum is exceeded a message is sent to the console and the
request is ignored.
NOTE: data is measured in centimeters
This is FirmataPlus feature.
:param trigger_pin: The pin number of for the trigger (transmitter).
:param echo_pin: The pin number for the received echo.
:param cb: optional callback function to report sonar data changes
:param ping_interval: Minimum interval between pings. Lowest number
to use is 33 ms.Max is 127
:param max_distance: Maximum distance in cm. Max is 200.
:param cb_type: direct call or asyncio yield from
:returns: No return value
"""
task = asyncio.ensure_future(self.core.sonar_config(trigger_pin,
echo_pin, cb,
ping_interval,
max_distance, cb_type))
self.loop.run_until_complete(task)
def getDataFromSonar(self, trigger_pin, waitTime = 500):
"""
Retrieve Ping (HC-SR04 type) data. The data is presented as a
dictionary.
The 'key' is the trigger pin specified in sonar_config() and the
'data' is the current measured distance (in centimeters)
for that pin. If there is no data, the value is set to None.
This is a FirmataPlus feature.
:param trigger_pin: trigger pin specified in sonar_config
:returns: active_sonar_map
"""
task = asyncio.ensure_future(self.core.ping_read(trigger_pin))
self.loop.run_until_complete(task)
if(waitTime < 200):
waitTime = 200
self.delay(waitTime)
task = asyncio.ensure_future(self.core.ping_data_retrieve(trigger_pin))
sonar_data = self.loop.run_until_complete(task)
return sonar_data
def configStepper(self, steps_per_revolution, stepper_pins):
"""
Configure stepper motor prior to operation.
This is a FirmataPlus feature.
:param steps_per_revolution: number of steps per motor revolution
:param stepper_pins: a list of control pin numbers - either 4 or 2
:returns: No return value
"""
task = asyncio.ensure_future(self.core.stepper_config(steps_per_revolution,
stepper_pins))
self.loop.run_until_complete(task)
def stepStepper(self, motor_speed, number_of_steps):
"""
Move a stepper motor for the number of steps at the specified speed
This is a FirmataPlus feature.
:param motor_speed: 21 bits of data to set motor speed
:param number_of_steps: 14 bits for number of steps & direction
positive is forward, negative is reverse
"""
task = asyncio.ensure_future(self.core.stepper_step(motor_speed,
number_of_steps))
self.loop.run_until_complete(task)
def configNeopixel(self, pin, count=12, brightness=128):
"""
Configure Neopixel strip prior to operation.
This is an extended firmata feature.
:param pin: data pin of the neopixel strip
:param count: number of pixels in strip
:param brightnessss: brigheness of the pixels
:returns: No return value
"""
task = asyncio.ensure_future(self.core.neopixel_config(pin, count, brightness))
self.loop.run_until_complete(task)
def setNeopixelColor(self, index, r, g, b):
"""
Set the color of the pixel at specified index.
This is an extended firmata feature.
:param index: position of the pixel to set the color
:param r: red value of the pixel color
:param g: green value of the pixel color
:param b: blue value of the pixel color
"""
task = asyncio.ensure_future(self.core.neopixel(index,r, g, b))
self.loop.run_until_complete(task)
def configLCD(self, address=0x3F, row=2, col=16):
"""
Configure LCD1602 using I2C.
This is an extended firmata feature.
:param address: I2C adddress of the LCD
:param row: number of rows on the LCD
:param col: number of chars on each row
:returns: No return value
"""
task = asyncio.ensure_future(self.core.lcd_config(address, col, row))
self.loop.run_until_complete(task)
def printOnLCD(self, text, row = 0, col = 0, backlight=True):
"""
Show specifiedd text on the LCD.
This is an extended firmata feature.
:param address: I2C adddress of the LCD
:param row: on which row to show the text
:param col: from which character to print the text
:returns: No return value
"""
task = asyncio.ensure_future(self.core.lcd_print(text, row, col, backlight))
self.loop.run_until_complete(task)
def clearLCD(self):
"""
Clear the LCD screen.
This is an extended firmata feature.
:returns: No return value
"""
task = asyncio.ensure_future(self.core.lcd_clear())
self.loop.run_until_complete(task)
def configDHT(self, pin):
"""
Configure a DHT device.
This is an extended firmata feature.
:param pin: data pin of the DHT11
:returns: No return value
"""
task = asyncio.ensure_future(self.core.dht_read(pin))
self.loop.run_until_complete(task)
def getDataFromDHT(self, pin, waitTime=500):
"""
Read the value from a DHT device.
This is an extended firmata feature.
:param pin: data pin of the DHT11
:returns: No return value
"""
if(waitTime < 100):
waitTime = 100
self.delay(waitTime)
task = asyncio.ensure_future(self.core.dht_data_retrieve(pin))
dhtdata = self.loop.run_until_complete(task)
return dhtdata
def pixyInit(self, max_blocks=5, cb=None, cb_type=None):
"""
Initialize Pixy and will enable Pixy block reporting.
This is a FirmataPlusRB feature.
:param cb: callback function to report Pixy blocks
:param cb_type: Constants.CB_TYPE_DIRECT = direct call or
Constants.CB_TYPE_ASYNCIO = asyncio coroutine
:param max_blocks: Maximum number of Pixy blocks to report when many
signatures are found.
:returns: No return value.
"""
task = asyncio.ensure_future(self.core.pixy_init(max_blocks, cb, cb_type))
self.loop.run_until_complete(task)
def pixyGetBlocks(self):
"""
This method retrieves the latest Pixy data value
:returns: Pixy data
"""
return self.core.pixy_blocks
def pixySetServos(self, s0, s1):
"""
Sends the setServos Pixy command.
This method sets the pan/tilt servos that are plugged into Pixy's two servo ports.
:param s0: value 0 to 1000
:param s1: value 0 to 1000
:returns: No return value.
"""
task = asyncio.ensure_future(self.core.pixy_set_servos(s0, s1))
self.loop.run_until_complete(task)
def pixySetBrightness(self, brightness):
"""
Sends the setBrightness Pixy command.
This method sets the brightness (exposure) of Pixy's camera.
:param brightness: range between 0 and 255 with 255 being the
brightest setting
:returns: No return value.
"""
task = asyncio.ensure_future(self.core.pixy_set_brightness(brightness))
self.loop.run_until_complete(task)
def pixySetLed(self, r, g, b):
"""
Sends the setLed Pixy command.
This method sets the RGB LED on front of Pixy.
:param r: red range between 0 and 255
:param g: green range between 0 and 255
:param b: blue range between 0 and 255
:returns: No return value.
"""
task = asyncio.ensure_future(self.core.pixy_set_led(r, g, b))
self.loop.run_until_complete(task)

View File

@@ -0,0 +1,19 @@
"""
Copyright (c) 2018-2019 Kaiyu Liu All rights reserved.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
Version 3 as published by the Free Software Foundation; either
or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
print('__file__={0:<35} | __name__={1:<20} | __package__={2:<20}'.format(__file__,__name__,str(__package__)))
import pyboard.pyboard_core.PyBoardCore

View File

@@ -0,0 +1,31 @@
"""
Copyright (c) 2018-2019 Kaiyu Liu,, All rights reserved.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
Version 3 as published by the Free Software Foundation; either
or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
from pymata_aio.private_constants import PrivateConstants
class BoardConstants(PrivateConstants):
NEOPIXEL_CONFIG = 0xA2
NEOPIXEL = 0xA0
LCD_CONFIG = 0xA4
LCD_PRINT = 0xA5
LCD_CLEAR = 0xA6
DHT11_READ = 0xA7
DHT11_DATA = 0xA8
PING_READ = 0xA9
PING_DATA = 0xAA
def __init__():
PrivateConstants.__init__()

View File

@@ -0,0 +1,213 @@
"""
Copyright (c) 2018-2019 Kaiyu Liu, All rights reserved.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
Version 3 as published by the Free Software Foundation; either
or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
# noinspection PyCompatibility
import asyncio
import glob
import logging
import sys
import time
# noinspection PyPackageRequirements
import serial
from pymata_aio.pymata_core import PymataCore
from pymata_aio.pin_data import PinData
#from pymata_aio.private_constants import PrivateConstants
from pymata_aio.pymata_serial import PymataSerial
from pymata_aio.pymata_socket import PymataSocket
try:
from pyboard_constants import BoardConstants
except ImportError:
from .pyboard_constants import BoardConstants
# noinspection PyCallingNonCallable,PyCallingNonCallable,PyPep8,PyBroadException,PyBroadException,PyCompatibility
class PyBoardCore(PymataCore):
"""
This class exposes and implements the pymata_core asyncio API,
It includes the public API methods as well as
a set of private methods. If your application is using asyncio,
this is the API that you should use.
After instantiating this class, its "start" method MUST be called to
perform Arduino pin auto-detection.
"""
def __init__(self, arduino_wait=2, sleep_tune=0.0001, log_output=False,com_port=None,
ip_address=None, ip_port=2000,
ip_handshake='*HELLO*'):
PymataCore.__init__(self, arduino_wait, sleep_tune, log_output, com_port, ip_address, ip_port,ip_handshake)
# report query results are stored in this dictionary
self.query_reply_data[BoardConstants.DHT11_DATA] = None
self.command_dictionary[BoardConstants.DHT11_DATA] = self._dht_data
self.command_dictionary[BoardConstants.PING_DATA] = self._ping_data
self.dht_map = {}
self.ping_map = {}
async def neopixel_config(self, pin, count=12, brightness=64):
"""
Configure a pin as a neopixel pin. Set LED count and brightness.
Use this method (not set_pin_mode) to configure a pin for neopixel
operation.
:param pin: neopixel Pin.
:param count: number of LEDs on the strip.
:param brightness: brightness from 1 to 255.
:returns: No return value
"""
command = [pin, count & 0x7f, (count >> 7) & 0x7f, brightness & 0x7f, (brightness >> 7) & 0x7f]
await self._send_sysex(BoardConstants.NEOPIXEL_CONFIG, command)
async def neopixel(self, index, r, g, b):
"""
Set the color of the LED specified by index.
:param index: index of the LED
:param r: red
:param g: green
:param b: blue
:returns: No return value.
"""
data = [index & 0x7f, (index >> 7) & 0x7f, r & 0x7f, (r >> 7) & 0x7f, g & 0x7f, (g >> 7) & 0x7f, b & 0x7f, (b >> 7) & 0x7f]
await self._send_sysex(BoardConstants.NEOPIXEL, data)
async def lcd_config(self, address, row = 2, col = 16):
"""
:returns: No return value
"""
command = [address, col & 0x7f, row & 0x7f]
await self._send_sysex(BoardConstants.LCD_CONFIG, command)
async def lcd_print(self, text, row, col, backlight=True):
"""
:returns: No return value.
"""
data = [row, col]
chars = list(text)
testarray = []
for i in range(len(chars)):
testarray.append(ord(chars[i]))
data = data + testarray
await self._send_sysex(BoardConstants.LCD_PRINT, data)
async def lcd_clear(self):
"""
:returns: No return value.
"""
data = []
await self._send_sysex(BoardConstants.LCD_CLEAR, data)
async def dht_read(self, datapin):
"""
:param pin: pin number of the DHT11 data pin
:returns: No return value.
"""
data = [datapin & 0x7F]
self.dht_map[datapin] = [-1,-1,-1, 0, 0]
await self._send_sysex(BoardConstants.DHT11_READ, data)
async def dht_data_retrieve(self, datapin):
"""
Retrieve DHT11 data. The data is presented as a
dictionary.
The 'key' is the data pin specified in dht11_config()
and the 'data' is [status, temperature, humidity, temperatureDecimal, humidityDecimal ].
:param trigger_pin: key into sonar data map
:returns: dht_map
"""
dht_pin_entry = self.dht_map[datapin]
result = dht_pin_entry[0]
if(result == 255):
dht_pin_entry[0] = 0
if(result == 254):
print("Checksum error")
elif(result == 253):
print("Timeout error")
return dht_pin_entry
async def _dht_data(self, data):
data = data[1:-1]
pin_number = data[0]
result = data[1]
if(result > 128):
result -= 255
dht_pin_entry = self.dht_map[pin_number]
dht_pin_entry[0] = result
dht_pin_entry[1] = data[2]
dht_pin_entry[2] = data[4]
dht_pin_entry[3] = data[3]
dht_pin_entry[4] = data[5]
self.dht_map[pin_number] = dht_pin_entry
await asyncio.sleep(self.sleep_tune)
async def ping_read(self, triggerPin):
"""
:param triggerPin: pin number of the sonar trigger pin
:returns: No return value.
"""
data = [triggerPin & 0x7F]
self.ping_map[triggerPin] = -1
await self._send_sysex(BoardConstants.PING_READ, data)
async def _ping_data(self, data):
"""
This method handles the incoming sonar data message and stores
the data in the response table.
:param data: Message data from Firmata
:returns: No return value.
"""
# strip off sysex start and end
data = data[1:-1]
pin_number = data[0]
val = int((data[2] << 7) + data[1])
#ping_pin_entry = self.ping_map[pin_number]
#ping_pin_entry[1] = val
self.ping_map[pin_number] = val
await asyncio.sleep(self.sleep_tune)
async def ping_data_retrieve(self, datapin):
"""
Retrieve sonar data. The data is presented as a dictionary.
The 'key' is the data pin specified in sonar_config()
and the 'data' is [pin, distance].
:param trigger_pin: key into sonar data map
:returns: ping_map
"""
ping_pin_entry = self.ping_map[datapin]
return ping_pin_entry

View File

@@ -0,0 +1,749 @@
"""
Copyright (c) 2018-2019 Kaiyu Liu, All rights reserved.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
Version 3 as published by the Free Software Foundation; either
or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
import asyncio
import datetime
import json
import sys
import signal
import argparse
import websockets
from pymata_aio.constants import Constants
from pyboard_core import PyBoardCore
class PyboardIOT:
def __init__(self, my_core):
self.core = my_core
self.command_map = {
"analogRead": self.analogRead,
"analogWrite": self.analogWrite,
"digitalRead": self.digitalRead,
"digitalWrite": self.digitalWrite,
"disableAnalogReporting": self.disableAnalogReporting,
"disableDigitalReporting": self.disableDigitalReporting,
"enableAnalogReporting": self.disableAnalogReporting,
"enableDigitalReporting": self.disableDigitalReporting,
"configEncoder": self.configEncoder,
"encoderRead": self.encoderRead,
"getAnalogLatchData": self.getAnalogLatchData,
"getAnalogMap": self.getAnalogMap,
"getCapabilityReport": self.getCapabilityReport,
"getDigitalLatchData": self.getDigitalLatchData,
"getFirmwareVersion": self.getFirmwareVersion,
"getPinState": self.getPinState,
"getProtocolVersion": self.getProtocolVersion,
"get_pymata_version": self.get_pymata_version,
"configI2C": self.configI2C,
"readI2CData": self.readI2CData,
"readI2CRequest": self.readI2CRequest,
"writeI2CRequest": self.writeI2CRequest,
"keepAlive": self.keepAlive,
"configNeopixel": self.configNeopixel,
"setNeopixelColor": self.setNeopixelColor,
"playTone": self.playTone,
"setAnalogLatch": self.setAnalogLatch,
"setDigitalLatch": self.setDigitalLatch,
"pinMode": self.pinMode,
"setSamplingInterval": self.setSamplingInterval,
"configSonar": self.configSonar,
"sonar_read": self.sonar_read,
"configServo": self.configServo,
"configStepper": self.configStepper,
"stepStepper": self.stepStepper
}
self.websocket = None
# noinspection PyUnusedLocal
async def get_message(self, websocket, path):
"""
:param websocket: websocket
:param path: path
:return:
"""
self.websocket = websocket
try:
while True:
payload = await self.websocket.recv()
# cmd_dict = json.loads(payload.decode('utf8'))
cmd_dict = json.loads(payload)
client_cmd = cmd_dict.get("method")
if client_cmd in self.command_map:
cmd = self.command_map.get(client_cmd)
params = cmd_dict.get("params")
if params[0] != "null":
await cmd(params)
else:
await cmd()
except websockets.exceptions.ConnectionClosed:
sys.exit()
async def analogRead(self, command):
"""
This method reads and returns the last reported value for an analog pin.
Normally not used since analog pin updates will be provided automatically
as they occur with the analog_message_reply being sent to the client after pinMode is called.
(see enableAnalogReporting for message format).
:param command: {"method": "analogRead", "params": [ANALOG_PIN]}
:returns: {"method": "analogRead_reply", "params": [PIN, ANALOG_DATA_VALUE]}
"""
pin = int(command[0])
data_val = await self.core.analog_read(pin)
reply = json.dumps({"method": "analogRead_reply", "params": [pin, data_val]})
await self.websocket.send(reply)
async def analogWrite(self, command):
"""
This method writes a value to an analog pin.
It is used to set the output of a PWM pin or the angle of a Servo.
:param command: {"method": "analogWrite", "params": [PIN, WRITE_VALUE]}
:returns: No return message.
"""
pin = int(command[0])
value = int(command[1])
await self.core.analog_write(pin, value)
async def digitalRead(self, command):
"""
This method reads and returns the last reported value for a digital pin.
Normally not used since digital pin updates will be provided automatically
as they occur with the digital_message_reply being sent to the client after pinMode is called..
(see enableDigitalReporting for message format)
:param command: {"method": "digitalRead", "params": [PIN]}
:returns: {"method": "digitalRead_reply", "params": [PIN, DIGITAL_DATA_VALUE]}
"""
pin = int(command[0])
data_val = await self.core.digital_read(pin)
reply = json.dumps({"method": "digitalRead_reply", "params": [pin, data_val]})
await self.websocket.send(reply)
async def digitalWrite(self, command):
"""
This method writes a zero or one to a digital pin.
:param command: {"method": "digitalWrite", "params": [PIN, DIGITAL_DATA_VALUE]}
:returns: No return message..
"""
pin = int(command[0])
value = int(command[1])
await self.core.digital_write(pin, value)
async def disableAnalogReporting(self, command):
"""
Disable Firmata reporting for an analog pin.
:param command: {"method": "disableAnalogReporting", "params": [PIN]}
:returns: No return message..
"""
pin = int(command[0])
await self.core.disable_analog_reporting(pin)
async def disableDigitalReporting(self, command):
"""
Disable Firmata reporting for a digital pin.
:param command: {"method": "disableDigitalReporting", "params": [PIN]}
:returns: No return message.
"""
pin = int(command[0])
await self.core.disable_digital_reporting(pin)
async def enableAnalogReporting(self, command):
"""
Enable Firmata reporting for an analog pin.
:param command: {"method": "enableAnalogReporting", "params": [PIN]}
:returns: {"method": "analog_message_reply", "params": [PIN, ANALOG_DATA_VALUE]}
"""
pin = int(command[0])
await self.core.enable_analog_reporting(pin)
async def enableDigitalReporting(self, command):
"""
Enable Firmata reporting for a digital pin.
:param command: {"method": "enableDigitalReporting", "params": [PIN]}
:returns: {"method": "digital_message_reply", "params": [PIN, DIGITAL_DATA_VALUE]}
"""
pin = int(command[0])
await self.core.enable_digital_reporting(pin)
async def configEncoder(self, command):
"""
Configure 2 pins for FirmataPlus encoder operation.
:param command: {"method": "configEncoder", "params": [PIN_A, PIN_B]}
:returns: {"method": "encoder_data_reply", "params": [ENCODER_DATA]}
"""
pin_a = int(command[0])
pin_b = int(command[1])
await self.core.encoder_config(pin_a, pin_b, self.encoder_callback)
async def encoderRead(self, command):
"""
This is a polling method to read the last cached FirmataPlus encoder value.
Normally not used. See encoder config for the asynchronous report message format.
:param command: {"method": "encoderRead", "params": [PIN_A]}
:returns: {"method": "encoderRead_reply", "params": [PIN_A, ENCODER_VALUE]}
"""
pin = int(command[0])
val = await self.core.encoder_read(pin)
reply = json.dumps({"method": "encoderRead_reply", "params": [pin, val]})
await self.websocket.send(reply)
async def getAnalogLatchData(self, command):
"""
This method retrieves a latch table entry for an analog pin.
See constants.py for definition of reply message parameters.
:param command: {"method": "getAnalogLatchData", "params": [ANALOG_PIN]}
:returns: {"method": "getAnalogLatchData_reply", "params": [ANALOG_PIN, LATCHED_STATE, THRESHOLD_TYPE,\
THRESHOLD_TARGET, DATA_VALUE, TIME_STAMP ]}
"""
pin = int(command[0])
data_val = await self.core.get_analog_latch_data(pin)
if data_val:
data_val = data_val[0:-1]
reply = json.dumps({"method": "getAnalogLatchData_reply", "params": [pin, data_val]})
await self.websocket.send(reply)
async def getAnalogMap(self):
"""
This method retrieves the Firmata analog map.
Refer to: http://firmata.org/wiki/Protocol#Analog_Mapping_Query to interpret the reply
The command JSON format is: {"method":"getAnalogMap","params":["null"]}
:returns: {"method": "analog_map_reply", "params": [ANALOG_MAP]}
"""
value = await self.core.get_analog_map()
if value:
reply = json.dumps({"method": "analog_map_reply", "params": value})
else:
reply = json.dumps({"method": "analog_map_reply", "params": "None"})
await self.websocket.send(reply)
async def getCapabilityReport(self):
"""
This method retrieves the Firmata capability report.
Refer to http://firmata.org/wiki/Protocol#Capability_Query
The command format is: {"method":"getCapabilityReport","params":["null"]}
:returns: {"method": "capability_report_reply", "params": [RAW_CAPABILITY_REPORT]}
"""
value = await self.core.get_capability_report()
await asyncio.sleep(.1)
if value:
reply = json.dumps({"method": "capability_report_reply", "params": value})
else:
reply = json.dumps({"method": "capability_report_reply", "params": "None"})
await self.websocket.send(reply)
async def getDigitalLatchData(self, command):
"""
This method retrieves a latch table entry for a digital pin.
See constants.py for definition of reply message parameters.
:param command: {"method": "getDigitalLatchData", "params": [DPIN]}
:returns: {"method": "getDigitalLatchData_reply", "params": [DIGITAL_PIN, LATCHED_STATE, THRESHOLD_TYPE,\
THRESHOLD_TARGET, DATA_VALUE, TIME_STAMP ]}
"""
pin = int(command[0])
data_val = await self.core.get_digital_latch_data(pin)
if data_val:
data_val = data_val[0:-1]
reply = json.dumps({"method": "getDigitalLatchData_reply", "params": [pin, data_val]})
await self.websocket.send(reply)
async def getFirmwareVersion(self):
"""
This method retrieves the Firmata firmware version.
See: http://firmata.org/wiki/Protocol#Query_Firmware_Name_and_Version
JSON command: {"method": "getFirmwareVersion", "params": ["null"]}
:returns: {"method": "firmware_version_reply", "params": [FIRMWARE_VERSION]}
"""
value = await self.core.get_firmware_version()
if value:
reply = json.dumps({"method": "firmware_version_reply", "params": value})
else:
reply = json.dumps({"method": "firmware_version_reply", "params": "Unknown"})
await self.websocket.send(reply)
async def getPinState(self, command):
"""
This method retrieves a Firmata pin_state report for a pin..
See: http://firmata.org/wiki/Protocol#Pin_State_Query
:param command: {"method": "getPinState", "params": [PIN]}
:returns: {"method": "getPinState_reply", "params": [PIN_NUMBER, PIN_MODE, PIN_STATE]}
"""
pin = int(command[0])
value = await self.core.get_pin_state(pin)
if value:
reply = json.dumps({"method": "pin_state_reply", "params": value})
else:
reply = json.dumps({"method": "pin_state_reply", "params": "Unknown"})
await self.websocket.send(reply)
async def getProtocolVersion(self):
"""
This method retrieves the Firmata protocol version.
JSON command: {"method": "getProtocolVersion", "params": ["null"]}
:returns: {"method": "protocol_version_reply", "params": [PROTOCOL_VERSION]}
"""
value = await self.core.get_protocol_version()
if value:
reply = json.dumps({"method": "protocol_version_reply", "params": value})
else:
reply = json.dumps({"method": "protocol_version_reply", "params": "Unknown"})
await self.websocket.send(reply)
async def get_pymata_version(self):
"""
This method retrieves the PyMata release version number.
JSON command: {"method": "get_pymata_version", "params": ["null"]}
:returns: {"method": "pymata_version_reply", "params":[PYMATA_VERSION]}
"""
value = await self.core.get_pymata_version()
if value:
reply = json.dumps({"method": "pymata_version_reply", "params": value})
else:
reply = json.dumps({"method": "pymata_version_reply", "params": "Unknown"})
await self.websocket.send(reply)
async def configI2C(self, command):
"""
This method initializes the I2c and sets the optional read delay (in microseconds).
It must be called before doing any other i2c operations for a given device.
:param command: {"method": "configI2C", "params": [DELAY]}
:returns: No Return message.
"""
delay = int(command[0])
await self.core.i2c_config(delay)
async def readI2CData(self, command):
"""
This method retrieves the last value read for an i2c device identified by address.
This is a polling implementation and readI2CRequest and readI2CRequest_reply may be
a better alternative.
:param command: {"method": "readI2CData", "params": [I2C_ADDRESS ]}
:returns:{"method": "readI2CData_reply", "params": i2c_data}
"""
address = int(command[0])
i2c_data = await self.core.i2c_read_data(address)
reply = json.dumps({"method": "readI2CData_reply", "params": i2c_data})
await self.websocket.send(reply)
async def readI2CRequest(self, command):
"""
This method sends an I2C read request to Firmata. It is qualified by a single shot, continuous
read, or stop reading command.
Special Note: for the read type supply one of the following string values:
"0" = I2C_READ
"1" = I2C_READ | I2C_END_TX_MASK"
"2" = I2C_READ_CONTINUOUSLY
"3" = I2C_READ_CONTINUOUSLY | I2C_END_TX_MASK
"4" = I2C_STOP_READING
:param command: {"method": "readI2CRequest", "params": [I2C_ADDRESS, I2C_REGISTER,
NUMBER_OF_BYTES, I2C_READ_TYPE ]}
:returns: {"method": "readI2CRequest_reply", "params": [DATA]}
"""
device_address = int(command[0])
register = int(command[1])
number_of_bytes = int(command[2])
if command[3] == "0":
read_type = Constants.I2C_READ_CONTINUOUSLY
elif command[3] == "1":
read_type = Constants.I2C_READ
elif command[3] == "2":
read_type = Constants.I2C_READ | Constants.I2C_END_TX_MASK
elif command[3] == "3":
read_type = Constants.I2C_READ_CONTINUOUSLY | Constants.I2C_END_TX_MASK
else: # the default case stop reading valid request or invalid request
read_type = Constants.I2C_STOP_READING
await self.core.i2c_read_request(device_address, register, number_of_bytes, read_type,
self.readI2CRequest_callback)
await asyncio.sleep(.1)
async def writeI2CRequest(self, command):
"""
This method performs an I2C write at a given I2C address,
:param command: {"method": "writeI2CRequest", "params": [I2C_DEVICE_ADDRESS, [DATA_TO_WRITE]]}
:returns:No return message.
"""
device_address = int(command[0])
params = command[1]
params = [int(i) for i in params]
await self.core.i2c_write_request(device_address, params)
async def keepAlive(self, command):
"""
Periodically send a keep alive message to the Arduino.
Frequency of keep alive transmission is calculated as follows:
keepAlive_sent = period - (period * margin)
:param command: {"method": "keepAlive", "params": [PERIOD, MARGIN]}
Period is time period between keepalives. Range is 0-10 seconds. 0 disables the keepalive mechanism.
Margin is a safety margin to assure keepalives are sent before period expires. Range is 0.1 to 0.9
:returns: No return value
"""
period = int(command[0])
margin = int(command[1])
await self.core.keep_alive(period, margin)
async def playTone(self, command):
"""
This method controls a piezo device to play a tone. It is a FirmataPlus feature.
Tone command is TONE_TONE to play, TONE_NO_TONE to stop playing.
:param command: {"method": "playTone", "params": [PIN, TONE_COMMAND, FREQUENCY(Hz), DURATION(MS)]}
:returns:No return message.
"""
pin = int(command[0])
if command[1] == "TONE_TONE":
tone_command = Constants.TONE_TONE
else:
tone_command = Constants.TONE_NO_TONE
frequency = int(command[2])
duration = int(command[3])
await self.core.play_tone(pin, tone_command, frequency, duration)
async def setAnalogLatch(self, command):
"""
This method sets the an analog latch for a given analog pin, providing the threshold type, and
latching threshold.
:param command: {"method": "setAnalogLatch", "params": [PIN, THRESHOLD_TYPE, THRESHOLD_VALUE]}
:returns:{"method": "analog_latch_data_reply", "params": [PIN, DATA_VALUE_LATCHED, TIMESTAMP_STRING]}
"""
pin = int(command[0])
threshold_type = int(command[1])
threshold_value = int(command[2])
await self.core.set_analog_latch(pin, threshold_type, threshold_value, self.analog_latch_callback)
async def setDigitalLatch(self, command):
"""
This method sets the a digital latch for a given digital pin, the threshold type, and latching threshold.
:param command:{"method": "setDigitalLatch", "params": [PIN, THRESHOLD (0 or 1)]}
:returns:{"method": digital_latch_data_reply", "params": [PIN, DATA_VALUE_LATCHED, TIMESTAMP_STRING]}
"""
pin = int(command[0])
threshold_value = int(command[1])
await self.core.set_digital_latch(pin, threshold_value, self.digital_latch_callback)
async def pinMode(self, command):
"""
This method sets the pin mode for the selected pin. It handles: Input, Analog(Input) PWM, and OUTPUT. Servo
is handled by configServo().
:param command: {"method": "pinMode", "params": [PIN, MODE]}
:returns:No return message.
"""
pin = int(command[0])
mode = int(command[1])
if mode == Constants.INPUT:
cb = self.digital_callback
elif mode == Constants.ANALOG:
cb = self.analog_callback
else:
cb = None
await self.core.set_pin_mode(pin, mode, cb)
async def setSamplingInterval(self, command):
"""
This method sets the Firmata sampling interval in ms.
:param command:{"method": "setSamplingInterval", "params": [INTERVAL]}
:returns:No return message.
"""
sample_interval = int(command[0])
await self.core.set_sampling_interval(sample_interval)
async def configSonar(self, command):
"""
This method configures 2 pins to support HC-SR04 Ping devices.
This is a FirmataPlus feature.
:param command: {"method": "configSonar", "params": [TRIGGER_PIN, ECHO_PIN, PING_INTERVAL(default=50),
MAX_DISTANCE(default= 200 cm]}
:returns:{"method": "sonar_data_reply", "params": [DISTANCE_IN_CM]}
"""
trigger = int(command[0])
echo = int(command[1])
interval = int(command[2])
max_dist = int(command[3])
await self.core.sonar_config(trigger, echo, self.sonar_callback, interval, max_dist)
async def sonar_read(self, command):
"""
This method retrieves the last sonar data value that was cached.
This is a polling method. After sonar config, sonar_data_reply messages will be sent automatically.
:param command: {"method": "sonar_read", "params": [TRIGGER_PIN]}
:returns:{"method": "sonar_read_reply", "params": [TRIGGER_PIN, DATA_VALUE]}
"""
pin = int(command[0])
val = await self.core.sonar_data_retrieve(pin)
reply = json.dumps({"method": "sonar_read_reply", "params": [pin, val]})
await self.websocket.send(reply)
async def configNeopixel(self, command):
"""
This method configures a digital pin to support configNeopixel devices.
This is a FirmataPlus feature.
:param command: {"method": "configNeopixel", "params": [datapin,, count, brightness]}
:returns:No message returned.
"""
pin = int(command[0])
ledcount = int(command[1])
brightness = int(command[2])
await self.core.neopixel_config(pin, ledcount, brightness)
async def setNeopixelColor(self, command):
"""
This method retrieves the last sonar data value that was cached.
This is a polling method. After sonar config, sonar_data_reply messages will be sent automatically.
:param command: {"method": "sonar_read", "params": [TRIGGER_PIN]}
:returns:{"method": "sonar_read_reply", "params": [TRIGGER_PIN, DATA_VALUE]}
"""
index = int(command[0])
red = int(command[1])
green = int(command[2])
blue = int(command[3])
await self.core.neopixel(index, red, green, blue)
async def configServo(self, command):
"""
This method configures a pin for servo operation. The servo angle is set by using analogWrite().
:param command: {"method": "configServo", "params": [PIN, MINIMUM_PULSE(ms), MAXIMUM_PULSE(ms)]}
:returns:No message returned.
"""
pin = int(command[0])
min_pulse = int(command[1])
max_pulse = int(command[2])
await self.core.servo_config(pin, min_pulse, max_pulse)
async def configStepper(self, command):
"""
This method configures 4 pins for stepper motor operation.
This is a FirmataPlus feature.
:param command: {"method": "configStepper", "params": [STEPS_PER_REVOLUTION, [PIN1, PIN2, PIN3, PIN4]]}
:returns:No message returned.
"""
steps_per_revs = int(command[0])
pins = command[1]
pin1 = int(pins[0])
pin2 = int(pins[1])
pin3 = int(pins[2])
pin4 = int(pins[3])
await self.core.stepper_config(steps_per_revs, [pin1, pin2, pin3, pin4])
async def stepStepper(self, command):
"""
This method activates a stepper motor motion.
This is a FirmataPlus feature.
:param command: {"method": "stepper_step", "params": [SPEED, NUMBER_OF_STEPS]}
:returns:No message returned.
"""
speed = int(command[0])
num_steps = int(command[1])
await self.core.stepper_step(speed, num_steps)
def analog_callback(self, data):
"""
This method handles the analog message received from pymata_core
:param data: analog callback message
:returns:{"method": "analog_message_reply", "params": [PIN, DATA_VALUE}
"""
reply = json.dumps({"method": "analog_message_reply", "params": [data[0], data[1]]})
asyncio.ensure_future(self.websocket.send(reply))
def analog_latch_callback(self, data):
"""
This method handles analog_latch data received from pymata_core
:param data: analog latch callback message
:returns:{"method": "analog_latch_data_reply", "params": [ANALOG_PIN, VALUE_AT_TRIGGER, TIME_STAMP_STRING]}
"""
ts = data[2]
st = datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S')
reply = json.dumps({"method": "analog_latch_data_reply", "params": [data[0], data[1], st]})
asyncio.ensure_future(self.websocket.send(reply))
def digital_callback(self, data):
"""
This method handles the digital message received from pymata_core
:param data: digital callback message
:returns:{"method": "digital_message_reply", "params": [PIN, DATA_VALUE]}
"""
reply = json.dumps({"method": "digital_message_reply", "params": [data[0], data[1]]})
asyncio.ensure_future(self.websocket.send(reply))
def digital_latch_callback(self, data):
"""
This method handles the digital latch data message received from pymata_core
:param data: digital latch callback message
:returns:s{"method": "digital_latch_data_reply", "params": [PIN, DATA_VALUE_AT_TRIGGER, TIME_STAMP_STRING]}
"""
ts = data[2]
st = datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S')
reply = json.dumps({"method": "digital_latch_data_reply", "params": [data[0], data[1], st]})
asyncio.ensure_future(self.websocket.send(reply))
def encoder_callback(self, data):
"""
This method handles the encoder data message received from pymata_core
:param data: encoder data callback message
:returns:{"method": "encoder_data_reply", "params": [ENCODER VALUE]}
"""
reply = json.dumps({"method": "encoder_data_reply", "params": data})
asyncio.ensure_future(self.websocket.send(reply))
def readI2CRequest_callback(self, data):
"""
This method handles the i2c read data message received from pymata_core.
:param data: i2c read data callback message
:returns:{"method": "readI2CRequest_reply", "params": [DATA_VALUE]}
"""
reply = json.dumps({"method": "readI2CRequest_reply", "params": data})
asyncio.ensure_future(self.websocket.send(reply))
def readI2CData_callback(self, data):
"""
This method handles the i2c cached read data received from pymata_core.
:param data: i2c read cached data callback message
:returns:{"method": "readI2CData_reply", "params": [DATA_VALUE]}
"""
reply = json.dumps({"method": "readI2CData_reply", "params": data})
asyncio.ensure_future(self.websocket.send(reply))
def sonar_callback(self, data):
"""
This method handles sonar data received from pymata_core.
:param data: sonar data callback message
:returns:{"method": "sonar_data_reply", "params": [DATA_VALUE]}
"""
reply = json.dumps({"method": "sonar_data_reply", "params": data})
asyncio.ensure_future(self.websocket.send(reply))
"""
usage: pymata_iot.py [-h] [-host HOSTNAME] [-port PORT] [-wait WAIT]
[-comport COM] [-sleep SLEEP] [-log LOG]
optional arguments:
-h, --help show this help message and exit
-host HOSTNAME Server name or IP address
-port PORT Server port number
-wait WAIT Arduino wait time
-comport COM Arduino COM port
-sleep SLEEP sleep tune in ms.
-log LOG True = send output to file, False = send output to console
-ardIPAddr ADDR Wireless module ip address (WiFly)
-ardPort PORT Wireless module ip port (Wifly)
-handshake STR Wireless device handshake string (WiFly)
"""
parser = argparse.ArgumentParser()
parser.add_argument("-host", dest="hostname", default="localhost", help="Server name or IP address")
parser.add_argument("-port", dest="port", default="9000", help="Server port number")
parser.add_argument("-wait", dest="wait", default="2", help="Arduino wait time")
parser.add_argument("-comport", dest="com", default="None", help="Arduino COM port")
parser.add_argument("-sleep", dest="sleep", default=".001", help="sleep tune in ms.")
parser.add_argument("-log", dest="log", default="False", help="redirect console output to log file")
parser.add_argument("-ardIPAddr", dest="aIPaddr", default="None", help="Arduino IP Address (WiFly")
parser.add_argument("-ardPort", dest="aIPport", default="2000", help="Arduino IP port (WiFly")
parser.add_argument("-handshake", dest="handshake", default="*HELLO*", help="IP Device Handshake String")
args = parser.parse_args()
ip_addr = args.hostname
ip_port = args.port
if args.com == 'None':
comport = None
else:
comport = args.com
if args.log == 'True':
log = True
else:
log = False
ard_ip_addr = args.aIPaddr
ard_ip_port = args.aIPport
ard_handshake = args.handshake
core = PyBoardCore(int(args.wait), float(args.sleep), log, comport,
ard_ip_addr, ard_ip_port, ard_handshake)
# core = PymataCore()
core.start()
# Signal handler to trap control C
# noinspection PyUnusedLocal,PyUnusedLocal
def _signal_handler(sig, frame):
if core is not None:
print('\nYou pressed Ctrl+C')
task = asyncio.ensure_future(core.shutdown())
asyncio.get_event_loop().run_until_complete(task)
sys.exit(1)
signal.signal(signal.SIGINT, _signal_handler)
signal.signal(signal.SIGTERM, _signal_handler)
server = PyboardIOT(core)
try:
start_server = websockets.serve(server.get_message, '127.0.0.1', 9000)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()
except websockets.exceptions.ConnectionClosed:
sys.exit()
except RuntimeError:
sys.exit()