From 42d7f54fad8c09d21e1793e6d87d3761b4c441c5 Mon Sep 17 00:00:00 2001 From: Yoni Date: Sun, 26 Mar 2023 23:22:14 -0400 Subject: [PATCH 1/4] resolve #15 --- app/src/xrp_blocks_python.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/xrp_blocks_python.js b/app/src/xrp_blocks_python.js index 78c414f..a489ed0 100644 --- a/app/src/xrp_blocks_python.js +++ b/app/src/xrp_blocks_python.js @@ -136,7 +136,7 @@ Blockly.Python['xrp_straight_effort'] = function (block) { Blockly.Python['xrp_button_pressed'] = function (block) { var dropdown_pin = block.getFieldValue('PIN'); var code = `buttons.is_${dropdown_pin}_pressed()`; - return code; + return [code, Blockly.Python.ORDER_NONE]; }; Blockly.Python['xrp_stop_motors'] = function (block) { From 39ad8d118f66e245408b0f2e30637a9b132ce1d6 Mon Sep 17 00:00:00 2001 From: Yoni Date: Mon, 27 Mar 2023 00:08:01 -0400 Subject: [PATCH 2/4] Add XRP Library at 476a0bb --- app/lib/WPILib/WPILib.py | 44 ++++ .../__pycache__/encoded_motor.cpython-39.pyc | Bin 0 -> 2361 bytes app/lib/WPILib/_adafruit_hcsr04.py | 172 ++++++++++++++ app/lib/WPILib/_analog_reflectance.py | 34 +++ app/lib/WPILib/_buttons.py | 30 +++ app/lib/WPILib/_drivetrain.py | 221 ++++++++++++++++++ app/lib/WPILib/_encoded_motor.py | 43 ++++ app/lib/WPILib/_encoder.py | 35 +++ app/lib/WPILib/_grove_reflectance.py | 30 +++ app/lib/WPILib/_grove_ultrasonic.py | 185 +++++++++++++++ app/lib/WPILib/_led.py | 36 +++ app/lib/WPILib/_reflectance_wrapper.py | 56 +++++ app/lib/WPILib/_servo.py | 21 ++ app/lib/WPILib/_ultrasonic_wrapper.py | 47 ++++ 14 files changed, 954 insertions(+) create mode 100755 app/lib/WPILib/WPILib.py create mode 100755 app/lib/WPILib/__pycache__/encoded_motor.cpython-39.pyc create mode 100644 app/lib/WPILib/_adafruit_hcsr04.py create mode 100755 app/lib/WPILib/_analog_reflectance.py create mode 100755 app/lib/WPILib/_buttons.py create mode 100755 app/lib/WPILib/_drivetrain.py create mode 100755 app/lib/WPILib/_encoded_motor.py create mode 100755 app/lib/WPILib/_encoder.py create mode 100755 app/lib/WPILib/_grove_reflectance.py create mode 100755 app/lib/WPILib/_grove_ultrasonic.py create mode 100755 app/lib/WPILib/_led.py create mode 100644 app/lib/WPILib/_reflectance_wrapper.py create mode 100755 app/lib/WPILib/_servo.py create mode 100644 app/lib/WPILib/_ultrasonic_wrapper.py diff --git a/app/lib/WPILib/WPILib.py b/app/lib/WPILib/WPILib.py new file mode 100755 index 0000000..0f8c68f --- /dev/null +++ b/app/lib/WPILib/WPILib.py @@ -0,0 +1,44 @@ +import board as _board +from _drivetrain import Drivetrain +from _encoded_motor import EncodedMotor +from _ultrasonic_wrapper import Ultrasonic +from _reflectance_wrapper import Reflectance +from _servo import Servo +from _buttons import Buttons +from _led import RGBLED + +import time + +# hidden motor variables +""" +If you need to change the encoder counts, alter the ticksPerRev values in the following two constructors. +Most robots have either 144 or 288 ticks per revolution, depending on which motors are used. +""" +_leftMotor = EncodedMotor( + encoderPinA=_board.GP4, + encoderPinB=_board.GP5, + motorPin1=_board.GP8, + motorPin2=_board.GP9, + doFlip=True, + ticksPerRev=288) + +_rightMotor = EncodedMotor( + encoderPinA=_board.GP2, + encoderPinB=_board.GP3, + motorPin1=_board.GP10, + motorPin2=_board.GP11, + doFlip=False, + ticksPerRev=288) + +# Publicly-accessible objects +drivetrain = Drivetrain(_leftMotor, _rightMotor) # units in cm +reflectance = Reflectance() +sonar = Ultrasonic() +led = RGBLED(_board.GP18) +servo = Servo(_board.GP12, actuationRange = 135) +buttons = Buttons() + +def set_legacy_mode(is_legacy: bool = True): + drivetrain.set_legacy_mode(is_legacy) + sonar.set_legacy_mode(is_legacy) + reflectance.set_legacy_mode(is_legacy) \ No newline at end of file diff --git a/app/lib/WPILib/__pycache__/encoded_motor.cpython-39.pyc b/app/lib/WPILib/__pycache__/encoded_motor.cpython-39.pyc new file mode 100755 index 0000000000000000000000000000000000000000..d20c1da1081890121696da9a517e1de2e36029da GIT binary patch literal 2361 zcmZuz%Wm676y@+GQkE6Rah!J1f^AT=YGVs6P@s#Vh#ztqB$kEPK~OGA(3){fn&&I%>FK(G_D{Rfzgjbl ze~G!+Y%sUci{CMDgR|5au#hpL&D0D{JzJrrXFIepTd6Z}Ls##y)7robJ!af7xWnCJ zgS)~zvO*txjeFp|BQvaXbJJ+~r_f=vSm_LMmCG(Dt9N%5likTigFdZZYz)k8^kNQ! zFha%+*u!9p2|Fy@XqW!7jQ7RX!BCvi_EQr>x8;;x6lL*1L{V9fqCw6_DbbB6dNPXB zvlVCg!P31>>8xxlZEn3W4!8D%$;g@FE@#Q}JRY%|{#yMZ1T?c^0QZPv(1~k6oKvo%P^Onr}mRH%n9!tK_L@ zS7f@dvKtpgQY^$7Ux22CRq<4$`H(cUANB4+)D~Gk=Yqo-#BOnTARUTTNA#W0EDHEwbHm}0537TUt$wO5RL+&{KMSJZf&PrWjEgHMC^c#~f_HbS4z z@vD3m=jyzU_+Kv@oyx3rvg+-$o`Q!NMVPPkfw)N{Q(g2G^kNso*cls~iy^4H+nmma_Mpu|;;iH2nuny~xdExi4Or=P zPyd3x7SKCPvL!t(hf^x)?-$6_1M#$UdA^n=!(`@b0%+*(NbzKZh#$24(k5rhS`~j8 z)+&!>Y46}f=?tF@l00;KkJdLvN)rCU+Ukl9MCpp1om?uq1VGv;r_L_v!HkQ{a_Q@| zLjL_b@`upwLbaTwJI(ba^yPOTl}(5V=Lz5xMZ^`f(=c5&$6T|?n!v=2*)$KYS0;09 z)Z+PxK0TQ8Ml=CRXXjTC3h~=xN2mk}RY4)fd)5)VVVp@!EwNuaCy-2}mX^Bb5*>ZJ zd+2v`7=j=;^m};$^ye8!iuh3QAZXPPMww(vUW4Y+DwLErG=+GT?qi{FN?wQL4U?Z> z`bZR{7|JO642y-Xc&yH5**y9s@2j0Hrd9MQVdstLNf5pTGe*f78FH4uv2C_YyeS)@_-kkFr!1z|xgMjbwJoC2~vW^(PQxrjXZtQ_d@M&w>MHWM`ji zsRQXPNb&k0t!19_I1un><)GO;y!SBM&+=zkpmhc)t70+O+D!_Oc>X~@&VoEk4}xtG zz{o)5fiAJp`o?M}zy@7uHMiLEb-9vLu&Ihxee?dtqiD6Wvh*9ui|EG(RJes~U`JSA zLybOLoH}2G&eHEZ?~5TJ>O8dH(2bK?UOkfr%$a;CZeha&Bn<$iVIF?^VT32UFFlh< zI093L3bGJ@Y;J?&u~sp@lp<{it8NCfT)y? z*@^y6ZyJ(3I#XV{a23zxEE=UX#MAk}j0J2bEPULI$$3(>hIc{+wWg$pNm3P;`_, it's pretty straightforward +to calculate how far away the object is by timing how long the signal took to +go round-trip and do some simple arithmetic, which is handled for you by this +library. + +.. warning:: + + The HC-SR04 uses 5V logic, so you will have to use a `level shifter + `_ or simple + voltage divider between it and your CircuitPython board (which uses 3.3V logic) + +* Authors: + + - Mike Mabey + - Jerry Needell - modified to add timeout while waiting for echo (2/26/2018) + - ladyada - compatible with `distance` property standard, renaming, Pi compat +""" + +import time +import math +from digitalio import DigitalInOut, Direction + +_USE_PULSEIO = False +try: + from pulseio import PulseIn + + _USE_PULSEIO = True +except ImportError: + pass # This is OK, we'll try to bitbang it! + +__version__ = "0.0.0-auto.0" +__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_HCSR04.git" + +class AdafruitUltrasonic: + """Control a HC-SR04 ultrasonic range sensor. + + Example use: + + :: + + import time + import board + + import adafruit_hcsr04 + + sonar = adafruit_hcsr04.HCSR04(trigger_pin=board.D2, echo_pin=board.D3) + + + while True: + try: + print((sonar.distance,)) + except RuntimeError: + print("Retrying!") + pass + time.sleep(0.1) + """ + + def __init__(self, trigger_pin, echo_pin, *, timeout=0.1): + """ + :param trigger_pin: The pin on the microcontroller that's connected to the + ``Trig`` pin on the HC-SR04. + :type trig_pin: microcontroller.Pin + :param echo_pin: The pin on the microcontroller that's connected to the + ``Echo`` pin on the HC-SR04. + :type echo_pin: microcontroller.Pin + :param float timeout: Max seconds to wait for a response from the + sensor before assuming it isn't going to answer. Should *not* be + set to less than 0.05 seconds! + """ + self.MAX_VALUE = 65535 + self._timeout = timeout + self._trig = DigitalInOut(trigger_pin) + self._trig.direction = Direction.OUTPUT + + if _USE_PULSEIO: + self._echo = PulseIn(echo_pin) + self._echo.pause() + self._echo.clear() + else: + self._echo = DigitalInOut(echo_pin) + self._echo.direction = Direction.INPUT + + def __enter__(self): + """Allows for use in context managers.""" + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + """Automatically de-initialize after a context manager.""" + self.deinit() + + def _deinit(self): + """De-initialize the trigger and echo pins.""" + self._trig.deinit() + self._echo.deinit() + + def get_distance(self): + """ + Return the distance measured by the sensor in cm. + + This is the function that will be called most often in user code. The + distance is calculated by timing a pulse from the sensor, indicating + how long between when the sensor sent out an ultrasonic signal and when + it bounced back and was received again. + + If no signal is received, we'll throw a RuntimeError exception. This means + either the sensor was moving too fast to be pointing in the right + direction to pick up the ultrasonic signal when it bounced back (less + likely), or the object off of which the signal bounced is too far away + for the sensor to handle. In my experience, the sensor can detect + objects over 460 cm away. + + :return: Distance in centimeters. + :rtype: float + """ + return self._dist_two_wire() # at this time we only support 2-wire meausre + + def _dist_two_wire(self): + if _USE_PULSEIO: + self._echo.clear() # Discard any previous pulse values + self._trig.value = True # Set trig high + time.sleep(0.00001) # 10 micro seconds 10/1000/1000 + self._trig.value = False # Set trig low + + pulselen = None + timestamp = time.monotonic() + if _USE_PULSEIO: + self._echo.resume() + while not self._echo: + # Wait for a pulse + if (time.monotonic() - timestamp) > self._timeout: + self._echo.pause() + #raise RuntimeError("Timed out") + return self.MAX_VALUE + self._echo.pause() + pulselen = self._echo[0] + else: + # OK no hardware pulse support, we'll just do it by hand! + # hang out while the pin is low + while not self._echo.value: + if time.monotonic() - timestamp > self._timeout: + #raise RuntimeError("Timed out") + return self.MAX_VALUE + timestamp = time.monotonic() + # track how long pin is high + while self._echo.value: + if time.monotonic() - timestamp > self._timeout: + #raise RuntimeError("Timed out") + return self.MAX_VALUE + pulselen = time.monotonic() - timestamp + pulselen *= 1000000 # convert to us to match pulseio + if pulselen >= 65535: + #raise RuntimeError("Timed out") + return self.MAX_VALUE + + # positive pulse time, in seconds, times 340 meters/sec, then + # divided by 2 gives meters. Multiply by 100 for cm + # 1/1000000 s/us * 340 m/s * 100 cm/m * 2 = 0.017 + return pulselen * 0.017 diff --git a/app/lib/WPILib/_analog_reflectance.py b/app/lib/WPILib/_analog_reflectance.py new file mode 100755 index 0000000..45a4353 --- /dev/null +++ b/app/lib/WPILib/_analog_reflectance.py @@ -0,0 +1,34 @@ +from analogio import AnalogIn + +class AnalogReflectance: + + """ + Implements for the new reflectance sensor. + Reads from analog in and converts to a float from 0 (white) to 1 (black) + """ + + def __init__(self, leftPin, rightPin): + self._leftReflectance = AnalogIn(leftPin) + self._rightReflectance = AnalogIn(rightPin) + + def _get_value(self, sensor: AnalogIn) -> float: + MAX_ANALOG_VALUE: int = 65535 + return sensor.value / MAX_ANALOG_VALUE + + # Implements AbstractReflectance + def get_left(self) -> float: + """ + Gets the the reflectance of the left reflectance sensor + :return: The reflectance ranging from 0 (white) to 1 (black) + :rtype: float + """ + return self._get_value(self._leftReflectance) + + # Implements AbstractReflectance + def get_right(self) -> float: + """ + Gets the the reflectance of the right reflectance sensor + :return: The reflectance ranging from 0 (white) to 1 (black) + :rtype: float + """ + return self._get_value(self._rightReflectance) \ No newline at end of file diff --git a/app/lib/WPILib/_buttons.py b/app/lib/WPILib/_buttons.py new file mode 100755 index 0000000..81ab51e --- /dev/null +++ b/app/lib/WPILib/_buttons.py @@ -0,0 +1,30 @@ +import board +from digitalio import DigitalInOut, Direction, Pull + +class Buttons: + + def _createButton(self, buttonPin): + btn = DigitalInOut(buttonPin) + btn.direction = Direction.INPUT + btn.pull = Pull.UP + return btn + + def __init__(self): + self._buttonGP20 = self._createButton(board.GP20) + self._buttonGP21 = self._createButton(board.GP21) + + def is_GP20_pressed(self) -> bool: + """ + Get whether the GP20 button on the RP2040 microcontroller has been pressed + : return: if the GP20 button was pressed + : rtype: bool + """ + return not self._buttonGP20.value + + def is_GP21_pressed(self) -> bool: + """ + Get whether the GP21 button on the RP2040 microcontroller has been pressed + : return: if the GP21 button was pressed + : rtype: bool + """ + return not self._buttonGP21.value diff --git a/app/lib/WPILib/_drivetrain.py b/app/lib/WPILib/_drivetrain.py new file mode 100755 index 0000000..4b4af57 --- /dev/null +++ b/app/lib/WPILib/_drivetrain.py @@ -0,0 +1,221 @@ +import math +import time +from . import _encoded_motor + + +def _isTimeout(startTime, timeout): + + if timeout is None: + return False + return time.time() >= startTime+timeout + + +# Encapsulates the left and right motor objects and provides high-level functionality to manipulate robot locomotion. + +class Drivetrain: + + def __init__(self, left_encoded_motor: _encoded_motor, right_encoded_motor: _encoded_motor): # wheelDiameter and wheelSpacing in cm + + self.leftMotor = left_encoded_motor + self.rightMotor = right_encoded_motor + + self._LEGACY_DIAMETER: float = 6.5 # diameter of old robot wheel in cm + self._NEW_DIAMETER: float = 6.5 # diameter of new robot wheel in cm + + self._LEGACY_WHEEL_SPACING: float = 16 # distance between old robot wheels in cm + self._NEW_WHEEL_SPACING: float = 13.5 # distance between new robot wheels in cm + + self._LEGACY_TICKS_PER_REV: int = 144 # distance between old robot wheels in cm + self._NEW_TICKS_PER_REV: int = 288 # distance between new robot wheels in cm + + self.wheelDiameter = self._NEW_DIAMETER + self.wheelSpacing = self._NEW_WHEEL_SPACING + + + self.set_encoder_position(0, 0) + + def _set_wheel_diameter(self, diameter: float): + """ + Set the wheel diameter + + :param diameter: The diameter of the drive wheels in centimeters + type diameter: float + """ + self.wheelDiameter = diameter + + def _set_wheel_spacing(self, wheel_spacing: float): + """ + Set the space between wheels + + :param wheel_spacing: The distance between the drive wheels in centimeters + type wheel_spacing: float + """ + self.wheelSpacing = wheel_spacing + + def _set_encoder_ticks_per_rev(self, ticks_per_revolution: int): + """ + Set the space between wheels + + :param ticks_per_revolution: The number of encoder ticks per full revolution of the wheel + type ticks_per_revolution: int + """ + self.leftMotor._set_encoder_ticks_per_rev(ticks_per_revolution) + self.rightMotor._set_encoder_ticks_per_rev(ticks_per_revolution) + + def set_legacy_mode(self, is_legacy: bool = True): + if is_legacy: + self._set_wheel_diameter(self._LEGACY_DIAMETER) + self._set_wheel_spacing(self._LEGACY_WHEEL_SPACING) + self._set_encoder_ticks_per_rev(self._LEGACY_TICKS_PER_REV) + else: + self._set_wheel_diameter(self._NEW_DIAMETER) + self._set_wheel_spacing(self._NEW_WHEEL_SPACING) + self._set_encoder_ticks_per_rev(self._NEW_TICKS_PER_REV) + + # Go forward the specified distance in centimeters, and exit function when distance has been reached. + # Speed is bounded from -1 (reverse at full speed) to 1 (forward at full speed) + def straight(self, distance: float, speed: float = 0.5, timeout: float = None) -> bool: + """ + Go forward the specified distance in centimeters, and exit function when distance has been reached. + Speed is bounded from -1 (reverse at full speed) to 1 (forward at full speed) + + : param distance: The distance for the robot to travel (In Centimeters) + : type distance: float + : param speed: The speed for which the robot to travel (Bounded from -1 to 1). Default is half speed forward + : type speed: float + : param timeout: The amount of time before the robot stops trying to move forward and continues to the next step (In Seconds) + : type timeout: float + : return: if the distance was reached before the timeout + : rtype: bool + """ + # ensure distance is always positive while speed could be either positive or negative + if distance < 0: + speed *= -1 + distance *= -1 + + startTime = time.time() + startingLeft = self.get_left_encoder_position() + startingRight = self.get_right_encoder_position() + + KP = 5 + + rotationsToDo = distance / (self.wheelDiameter * math.pi) + + while True: + + leftPosition = self.get_left_encoder_position() + rightPosition = self.get_right_encoder_position() + leftDelta = leftPosition - startingLeft + rightDelta = rightPosition - startingRight + + if _isTimeout(startTime, timeout) or abs(leftDelta + rightDelta)/2 >= rotationsToDo: + break + + error = KP * (leftDelta - rightDelta) # positive if bearing right + + self.set_effort(speed - error, speed + error) + + time.sleep(0.01) + + self.stop() + + if timeout is None: + return True + else: + return time.time() < startTime+timeout + + def turn(self, turn_degrees: float, speed: float = 0.5, timeout: float = None) -> bool: + """ + Turn the robot some relative heading given in turnDegrees, and exit function when the robot has reached that heading. + Speed is bounded from -1 (turn counterclockwise the relative heading at full speed) to 1 (turn clockwise the relative heading at full speed) + + : param turnDegrees: The number of angle for the robot to turn (In Degrees) + : type turnDegrees: float + : param speed: The speed for which the robot to travel (Bounded from -1 to 1). Default is half speed forward. + : type speed: float + : param timeout: The amount of time before the robot stops trying to turn and continues to the next step (In Seconds) + : type timeout: float + : return: if the distance was reached before the timeout + : rtype: bool + """ + + # ensure distance is always positive while speed could be either positive or negative + if turn_degrees < 0: + speed *= -1 + turn_degrees *= -1 + + rotationsToDo = (turn_degrees/360) * self.wheelSpacing / self.wheelDiameter + + startTime = time.time() + startingLeft = self.get_left_encoder_position() + startingRight = self.get_right_encoder_position() + + KP = 5 + + while True: + + leftPosition = self.get_left_encoder_position() + rightPosition = self.get_right_encoder_position() + leftDelta = leftPosition - startingLeft + rightDelta = rightPosition - startingRight + + if _isTimeout(startTime, timeout) or abs(leftDelta - rightDelta)/2 >= rotationsToDo: + break + + error = KP * (leftDelta + rightDelta) + + self.set_effort(speed - error, -speed - error) + + time.sleep(0.01) + + self.stop() + + if timeout is None: + return True + else: + return time.time() < startTime+timeout + + def set_effort(self, left_effort: float, right_effort: float) -> None: + """ + Set the raw effort of both motors individually + + : param leftEffort: The power (Bounded from -1 to 1) to set the left motor to. + : type leftEffort: float + : param rightEffort: The power (Bounded from -1 to 1) to set the right motor to. + : type rightEffort: float + """ + + self.leftMotor.setEffort(left_effort) + self.rightMotor.setEffort(right_effort) + + def stop(self) -> None: + """ + Stops both drivetrain motors + """ + self.set_effort(0,0) + + def set_encoder_position(self, left_degrees: float, right_degrees: float) -> None: + """ + Set the position of the motors' encoders in degrees. Note that this does not actually move the motor but just recalibrates the stored encoder value. + If only one encoder position is specified, the encoders for each motor will be set to that position. + + : param leftDegrees: The distance to recalibrate the left encoder to. + : type leftDegrees: float + : param rightDegrees: The distance to recalibrate the left encoder to. + : type rightDegrees: float + """ + + self.leftMotor.setPos(left_degrees) + self.rightMotor.setPos(right_degrees) + + def get_left_encoder_position(self) -> float: + """ + Return the current position of the left motor's encoder in revolutions. + """ + return self.leftMotor.getPos() + + def get_right_encoder_position(self) -> float: + """ + Return the current position of the right motor's encoder in revolutions. + """ + return self.rightMotor.getPos() \ No newline at end of file diff --git a/app/lib/WPILib/_encoded_motor.py b/app/lib/WPILib/_encoded_motor.py new file mode 100755 index 0000000..ba23532 --- /dev/null +++ b/app/lib/WPILib/_encoded_motor.py @@ -0,0 +1,43 @@ +from adafruit_motor import motor +import pwmio +import time +from . import _encoder + +class EncodedMotor: + def __init__(self, encoderPinA, encoderPinB , motorPin1, motorPin2, Name="Motor Unnamed", doFlip=False, ticksPerRev=288): + + self.name = Name + self.encoder = _encoder.Encoder(pinA=encoderPinA, pinB=encoderPinB, ticksPerRev=ticksPerRev, doFlip=doFlip) + self.flip = doFlip + + MA = pwmio.PWMOut(motorPin1, frequency=10000) + MB = pwmio.PWMOut(motorPin2, frequency=10000) + if doFlip: + self.motor = motor.DCMotor(MB, MA) + else: + self.motor = motor.DCMotor(MA, MB) + + self.effort = None + + def __repr__(self): + print(self.name) + print("Effort " + str(self.effort)) + print("Position " + str(self.encoder.getPos())) + print("Flipped " + str(self.flip)) + pass + + # set motor throttle (effort) betwen [-1, 1] + def setEffort(self, effort: float) -> None: + if effort is None: + self.motor.throttle = None + else: + self.motor.throttle = min(1, max(-1, effort)) # bound effort between [-1, 1] + + def getPos(self) -> float: + return self.encoder.getPos() + + def setPos(self, pos: float = 0) -> None: + self.encoder.setPos(pos) + + def _set_encoder_ticks_per_rev(self, ticks_per_revolution: int): + self.encoder._set_encoder_ticks_per_rev(ticks_per_revolution) \ No newline at end of file diff --git a/app/lib/WPILib/_encoder.py b/app/lib/WPILib/_encoder.py new file mode 100755 index 0000000..409ea7c --- /dev/null +++ b/app/lib/WPILib/_encoder.py @@ -0,0 +1,35 @@ + +import rotaryio + +class Encoder: + + def __init__(self, pinA, pinB, ticksPerRev, doFlip=False): + self.ticksPerRev = ticksPerRev + self.reverse = doFlip + self.encoder = rotaryio.IncrementalEncoder(pinA, pinB) + + def getPos(self) -> float: + """ + Retrieves the position of the encoder in revolutions + + :return: The position of the encoder + :rtype: float + """ + r = self.encoder.position / self.ticksPerRev + if self.reverse: + return -r + else: + return r + + + def setPos(self, pos: float = 0): + """ + Recalibrates the encoder to the specified position + :param pos: The number of rotations to set encoder to + :type pos: float + :return: void + """ + self.encoder.position = round(pos * self.ticksPerRev) + + def _set_encoder_ticks_per_rev(self, ticks_per_revolution: int): + self.ticksPerRev = ticks_per_revolution diff --git a/app/lib/WPILib/_grove_reflectance.py b/app/lib/WPILib/_grove_reflectance.py new file mode 100755 index 0000000..ce76e89 --- /dev/null +++ b/app/lib/WPILib/_grove_reflectance.py @@ -0,0 +1,30 @@ +from . import _grove_ultrasonic + +class GroveReflectance(): + + """ + Supports the old reflectance sensor. A wrapper for the GroveUltrasonic class, because + for some reason the implementation for the reflectance sensor is identical to GroveUltrasonic + """ + + def __init__(self, leftPin, rightPin): + self._leftReflectance = _grove_ultrasonic.GroveUltrasonic(leftPin, timeout = 1) + self._rightReflectance = _grove_ultrasonic.GroveUltrasonic(rightPin, timeout = 1) + + # Implements AbstractReflectance + def get_left(self) -> float: + """ + Gets the the reflectance of the left reflectance sensor + :return: The reflectance ranging from 0 (white) to 1 (black) + :rtype: float + """ + return self._leftReflectance.get_distance() + + # Implements AbstractReflectance + def get_right(self) -> float: + """ + Gets the the reflectance of the right reflectance sensor + :return: The reflectance ranging from 0 (white) to 1 (black) + :rtype: float + """ + return self._rightReflectance.get_distance() diff --git a/app/lib/WPILib/_grove_ultrasonic.py b/app/lib/WPILib/_grove_ultrasonic.py new file mode 100755 index 0000000..fb47584 --- /dev/null +++ b/app/lib/WPILib/_grove_ultrasonic.py @@ -0,0 +1,185 @@ +#!/usr/bin/env python3 + +# The MIT License (MIT) +# +# Copyright (c) 2017 Mike Mabey +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +""" +`grove_ultrasonic` +==================================================== + +A CircuitPython library for the Grove ultrasonic range sensor. +Based on the CircuitPython library for the HC-SR04 ultrasonic range sensor. + +The HC-SR04 functions by sending an ultrasonic signal, which is reflected by +many materials, and then sensing when the signal returns to the sensor. Knowing +that sound travels through dry air at `343.2 meters per second (at 20 °C) +`_, it's pretty straightforward +to calculate how far away the object is by timing how long the signal took to +go round-trip and do some simple arithmetic, which is handled for you by this +library. + +* Original authors: + + - Mike Mabey + - Jerry Needell - modified to add timeout while waiting for echo (2/26/2018) + - ladyada - compatible with `distance` property standard, renaming, Pi compat +""" + +import time +from digitalio import DigitalInOut, Direction + +# Took this idea from adafruit_debouncer.py +# Find out whether the current CircuitPython supports time.monotonic_ns(), +# which doesn't have the accuracy limitation. +if hasattr(time, "monotonic_ns"): + TICKS_PER_SEC = 1_000_000_000 + MONOTONIC_TICKS = time.monotonic_ns +else: + TICKS_PER_SEC = 1 + MONOTONIC_TICKS = time.monotonic + +_USE_PULSEIO = False +try: + from pulseio import PulseIn + + _USE_PULSEIO = True +except ImportError: + pass # This is OK, we'll try to bitbang it! + +__version__ = "1.0.0" +__repo__ = "https://github.com/derhexenmeister/GroveUltrasonicRanger.git" + + +class GroveUltrasonic: + """Control a Grove ultrasonic range sensor. + + Example use: + + :: + + import time + import board + + import grove_ultrasonic_ranger + + sonar = grove_ultrasonic_ranger.GroveUltrasonicRanger(sig_pin=board.D2) + + + while True: + try: + print((sonar.distance,)) + except RuntimeError as e: + print("Retrying due to exception =", e) + pass + time.sleep(0.1) + """ + + def __init__(self, sig_pin, unit=1.0, timeout=1.0): + """ + :param sig_pin: The pin on the microcontroller that's connected to the + ``Sig`` pin on the GroveUltrasonicRanger. + :type sig_pin: microcontroller.Pin + :param float unit: pass in conversion factor for unit conversions from cm + for example 2.54 would convert to inches. + :param float timeout: Max seconds to wait for a response from the + sensor before assuming it isn't going to answer. Should *not* be + set to less than 0.05 seconds! + """ + print("init grove with ", sig_pin) + self._unit = unit + self._timeout = timeout*TICKS_PER_SEC + print("Using pulseio: ", _USE_PULSEIO) + if _USE_PULSEIO: + self._sig = PulseIn(sig_pin) + self._sig.pause() + self._sig.clear() + else: + self._sig = DigitalInOut(sig_pin) + self._sig.direction = Direction.OUTPUT + self._sig.value = False # Set trig low + + def __enter__(self): + #Allows for use in context managers. + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + #Automatically de-initialize after a context manager. + self._deinit() + + def _deinit(self): + #De-initialize the sig pin. + self._sig.deinit() + + def get_distance(self) -> float: + """ + Return the distance measured by the sensor in cm. + + :return: Distance in centimeters + :rtype: float + """ + return self._dist_one_wire() + + def _dist_one_wire(self): + if _USE_PULSEIO: + self._sig.pause() + self._sig.clear() # Discard any previous pulse values + else: + #self._sig.direction = Direction.OUTPUT + self._sig.value = True # Set trig high + time.sleep(0.00001) # 10 micro seconds 10/1000/1000 + self._sig.value = False # Set trig low + self._sig.direction = Direction.INPUT + + pulselen = None + timestamp = MONOTONIC_TICKS() + if _USE_PULSEIO: + self._sig.resume(10) + while not self._sig: + # Wait for a pulse + if (MONOTONIC_TICKS() - timestamp) > self._timeout: + self._sig.pause() + raise RuntimeError("Timed out (pulseio waiting for a pulse)") + self._sig.pause() + pulselen = self._sig[0] + else: + # OK no hardware pulse support, we'll just do it by hand! + # hang out while the pin is low + while not self._sig.value: + if MONOTONIC_TICKS() - timestamp > self._timeout: + self._sig.direction = Direction.OUTPUT + raise RuntimeError("Timed out (gpio, waiting for pulse leading edge)") + timestamp = MONOTONIC_TICKS() + # track how long pin is high + while self._sig.value: + if MONOTONIC_TICKS() - timestamp > self._timeout: + self._sig.direction = Direction.OUTPUT + raise RuntimeError("Timed out (gpio, waiting for pulse trailing edge)") + pulselen = MONOTONIC_TICKS() - timestamp + self._sig.direction = Direction.OUTPUT + pulselen *= (1000000/TICKS_PER_SEC) # convert to us to match pulseio + if pulselen >= 65535: + raise RuntimeError("Timed out (unreasonable pulse length)") + + # positive pulse time, in seconds, times 340 meters/sec, then + # divided by 2 gives meters. Multiply by 100 for cm + # 1/1000000 s/us * 340 m/s * 100 cm/m * 2 = 0.017 + # Divide by the supplied unit conversion factor + return (pulselen * 0.017)/self._unit diff --git a/app/lib/WPILib/_led.py b/app/lib/WPILib/_led.py new file mode 100755 index 0000000..4c5f9ea --- /dev/null +++ b/app/lib/WPILib/_led.py @@ -0,0 +1,36 @@ +import neopixel + +class RGBLED: + + def __init__(self, pin): + self._pixels = neopixel.NeoPixel(pin, 2) + self._pixels.fill(0xFFFFFF) + self._pixels.brightness = 0 + + def set_color(self, red: int, green: int, blue: int): + """ + Sets the color of the led by taking in the RGB representation of the color + + :param red: The red value (0-255) + :type red: int + :param green: The green value (0-255) + :type green: int + :param blue: The blue value (0-255) + :type blue: int + """ + new_color = red*16**4+green*16**2+blue + self._pixels.fill(new_color) + + def set_brightness(self, brightness: float): + """ + Sets the brightness of the two LEDs on the board + + :param brightness: The brightness to set the LEDs to [Bound from 0 (off) to 1 (max)] + :type brightness: float + """ + brightness = min(1,max(0,brightness)) + self._pixels.brightness = brightness + + + + \ No newline at end of file diff --git a/app/lib/WPILib/_reflectance_wrapper.py b/app/lib/WPILib/_reflectance_wrapper.py new file mode 100644 index 0000000..0861b43 --- /dev/null +++ b/app/lib/WPILib/_reflectance_wrapper.py @@ -0,0 +1,56 @@ +from . import _analog_reflectance +from . import _grove_reflectance +import board + +class Reflectance: + + """ + A wrapper for an object that stores either the legacy or new implementation of the reflectance sensor + User can set one of two modes, and get the value of either reflectance sensor + """ + + def __init__(self): + + # Default to new sensor + self._reflectanceObject = None + self._isLegacyMode = False + + def set_legacy_mode(self, is_legacy: bool = True) -> None: + """ + Set whether the reflectance sensor is the old or the new analog one + :param is_legacy: True if using the old version, False if using the new one + :type is_legacy: bool + """ + self._isLegacyMode = is_legacy + + + def _possibly_instantiate_object(self): + # If self._reflectanceObject is not defined, define it + + if self._reflectanceObject is not None: + return + + if self._isLegacyMode: + self._reflectanceObject = _grove_reflectance.GroveReflectance(board.GP26, board.GP27) + else: + self._reflectanceObject = _analog_reflectance.AnalogReflectance(board.GP27, board.GP26) + + + def get_left(self) -> float: + """ + Gets the the reflectance of the left reflectance sensor + :return: The reflectance ranging from 0 (white) to 1 (black) + :rtype: float + """ + self._possibly_instantiate_object() + return self._reflectanceObject.get_left() + + + def get_right(self) -> float: + """ + Gets the the reflectance of the right reflectance sensor + :return: The reflectance ranging from 0 (white) to 1 (black) + :rtype: float + """ + self._possibly_instantiate_object() + return self._reflectanceObject.get_right() \ No newline at end of file diff --git a/app/lib/WPILib/_servo.py b/app/lib/WPILib/_servo.py new file mode 100755 index 0000000..a610106 --- /dev/null +++ b/app/lib/WPILib/_servo.py @@ -0,0 +1,21 @@ +import pwmio +from adafruit_motor import servo +import time + +class Servo: + + def __init__(self, servoPin, actuationRange: int): + + pwm = pwmio.PWMOut(servoPin, duty_cycle=2 ** 15, frequency=50) + self._servo = servo.Servo(pwm) + self._range = actuationRange + self._servo.actuation_range = self._range # Bound servo motor between [0, actuationRange] degrees + + def set_degrees(self, degrees: int): + """ + Tells the servo to move to the specified position + + :param degrees: The angle for the servo to move to, bound in [0,135] + :type degrees: int + """ + self._servo.angle = max(0, min(self._range, degrees)) diff --git a/app/lib/WPILib/_ultrasonic_wrapper.py b/app/lib/WPILib/_ultrasonic_wrapper.py new file mode 100644 index 0000000..677fac1 --- /dev/null +++ b/app/lib/WPILib/_ultrasonic_wrapper.py @@ -0,0 +1,47 @@ +from . import _adafruit_hcsr04 +from . import _grove_ultrasonic +import board + +class Ultrasonic: + """ + A wrapper for an object that stores either the legacy or new implementation of the ultrasonic sensor + User can set one of two modes, and get the value of either ultrasonic sensor + """ + + def __init__(self): + + # Default to new sensor + self._sonarObject = None + self._isLegacyMode = False + + def set_legacy_mode(self, is_legacy: bool = True) -> None: + """ + Set whether the reflectance sensor is the old or the new analog one + :param is_legacy: True if using the old version, False if using the new one + :type is_legacy: bool + """ + self._isLegacyMode = is_legacy + + + def _possibly_instantiate_object(self): + # If self._reflectanceObject is not defined, define it + + if self._sonarObject is not None: + return + + if self._isLegacyMode: + self._sonarObject = _grove_ultrasonic.GroveUltrasonic(board.GP28) + else: + self._sonarObject = _adafruit_hcsr04.AdafruitUltrasonic(board.GP7, board.GP28) + + + def get_distance(self) -> float: + """ + Return the distance measured by the sensor in cm. + If the distance is too far, it returns a maximum value of 65535. + + :return: Distance in centimeters + :rtype: float + """ + self._possibly_instantiate_object() + return self._sonarObject.get_distance() From a178ee86bfcb3a17b646851e4820d91b8e1e4129 Mon Sep 17 00:00:00 2001 From: Yoni Date: Mon, 27 Mar 2023 00:08:26 -0400 Subject: [PATCH 3/4] Close #16 increment package release --- app/electron/apis.js | 39 ++++++++++++++---- app/electron/main.js | 2 +- index.html | 2 +- package-lock.json | 97 +++++++++++++++++++++++++++++++++++--------- package.json | 3 +- 5 files changed, 114 insertions(+), 29 deletions(-) diff --git a/app/electron/apis.js b/app/electron/apis.js index 0b9ce94..5e61fce 100644 --- a/app/electron/apis.js +++ b/app/electron/apis.js @@ -1,6 +1,6 @@ const { dialog } = require('electron'); const path = require('path'); -const fs = require('fs'); +const fs = require('fs-extra'); const drivelist = require('drivelist'); /* List of all APIS */ @@ -16,8 +16,8 @@ global.share.ipcMain.handle('open-file', handleOpenFile); async function handleSaveCode(event, req) { const appState = JSON.parse(fs.readFileSync(path.join(__dirname, "../state.json"))); if (appState.fullPath != "") { - let filePath = path.join(appState.fullPath, req.filename); - fs.writeFileSync(filePath, req.content); + let filePath = path.join(appState.fullPath, req.filename); + fs.writeFileSync(filePath, req.content); return { status: 200, payload: path.basename(filePath, path.extname(filePath)), @@ -61,15 +61,16 @@ async function handleSaveAsCode(event, req) { }; // Uploads Code to robot -async function handleUploadCode (event, code) { +async function handleUploadCode(event, code) { try { let drives = await drivelist.list(); - + let first_bot = drives.find(x => x.description.includes('Maker Pi RP2040')); let first_bot_drive = first_bot.mountpoints[0].path; let output_filepath = path.join(first_bot_drive, 'code.py'); + let initLib = initializeCodeLibrary(first_bot_drive) fs.writeFileSync(output_filepath, code); - + return { status: 201, message: "Code Uploaded" @@ -80,8 +81,32 @@ async function handleUploadCode (event, code) { message: `Internal Error: ${e}` }; } + }; + +function initializeCodeLibrary(fpath) { + try { + const wpilibLoc = path.join(__dirname, "../lib/WPILib"); + let output_filepath = path.join(fpath, 'WPILib'); + fs.pathExists(output_filepath).then(exists => { + if (exists) { + return true + } else { + fs.copy(wpilibLoc, output_filepath).then(() => { + return true + }) + .catch(err => { + console.error(err) + }) + } + } + ); + } catch (e) { + return false; + } +} + // Opens File async function handleOpenFile(event, req) { const filePath = dialog.showOpenDialogSync({ @@ -93,7 +118,7 @@ async function handleOpenFile(event, req) { if (filePath) { let inputFile = fs.readFileSync(filePath[0], { encoding: 'utf8' }); - + return { status: 200, payload: { diff --git a/app/electron/main.js b/app/electron/main.js index 0f9cc7e..b90a37c 100644 --- a/app/electron/main.js +++ b/app/electron/main.js @@ -3,7 +3,7 @@ // Modules to control application life and create native browser window const { app, BrowserWindow, ipcMain, dialog } = require('electron'); const path = require('path'); -const fs = require('fs'); +const fs = require('fs-extra'); const drivelist = require('drivelist'); const { SerialPort } = require('serialport'); var AsyncPolling = require('async-polling'); diff --git a/index.html b/index.html index 565d123..700df4a 100644 --- a/index.html +++ b/index.html @@ -834,7 +834,7 @@
-

v0.0.4

+

v0.0.5

diff --git a/package-lock.json b/package-lock.json index b726f0e..dca6b87 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "XRP-Blockly", - "version": "0.0.4", + "version": "0.0.5", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "XRP-Blockly", - "version": "0.0.4", + "version": "0.0.5", "license": "ISC", "dependencies": { "async-polling": "^0.2.1", @@ -14,6 +14,7 @@ "drivelist": "^11.0.0", "electron-reloader": "^1.2.3", "electron-squirrel-startup": "^1.0.0", + "fs-extra": "^11.1.1", "serialport": "^10.5.0" }, "devDependencies": { @@ -1070,6 +1071,20 @@ "global-tunnel-ng": "^2.7.1" } }, + "node_modules/@electron/get/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, "node_modules/@electron/universal": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-1.3.0.tgz", @@ -4783,17 +4798,35 @@ "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" }, "node_modules/fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dev": true, + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", + "integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==", "dependencies": { "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, "engines": { - "node": ">=6 <7 || >=8" + "node": ">=14.14" + } + }, + "node_modules/fs-extra/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/fs-extra/node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "engines": { + "node": ">= 10.0.0" } }, "node_modules/fs-minipass": { @@ -5163,8 +5196,7 @@ "node_modules/graceful-fs": { "version": "4.2.10", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", - "dev": true + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" }, "node_modules/graceful-readlink": { "version": "1.0.1", @@ -9101,6 +9133,19 @@ "progress": "^2.0.3", "semver": "^6.2.0", "sumchecker": "^3.0.1" + }, + "dependencies": { + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + } } }, "@electron/universal": { @@ -11920,14 +11965,29 @@ "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" }, "fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dev": true, + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", + "integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==", "requires": { "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "dependencies": { + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" + } } }, "fs-minipass": { @@ -12234,8 +12294,7 @@ "graceful-fs": { "version": "4.2.10", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", - "dev": true + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" }, "graceful-readlink": { "version": "1.0.1", diff --git a/package.json b/package.json index be17302..e7a8795 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "XRP-Blockly", - "version": "0.0.4", + "version": "0.0.5", "description": "XRP Blockly", "main": "app/electron/main.js", "scripts": { @@ -31,6 +31,7 @@ "drivelist": "^11.0.0", "electron-reloader": "^1.2.3", "electron-squirrel-startup": "^1.0.0", + "fs-extra": "^11.1.1", "serialport": "^10.5.0" }, "config": { From 827a43b5be99ff0a8150d740e5bd8f9db350996d Mon Sep 17 00:00:00 2001 From: Yoni Date: Mon, 27 Mar 2023 00:11:32 -0400 Subject: [PATCH 4/4] v0.0.6 --- index.html | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/index.html b/index.html index 700df4a..5c13ebe 100644 --- a/index.html +++ b/index.html @@ -834,7 +834,7 @@
-

v0.0.5

+

v0.0.6

diff --git a/package-lock.json b/package-lock.json index dca6b87..527a733 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "XRP-Blockly", - "version": "0.0.5", + "version": "0.0.6", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "XRP-Blockly", - "version": "0.0.5", + "version": "0.0.6", "license": "ISC", "dependencies": { "async-polling": "^0.2.1", diff --git a/package.json b/package.json index e7a8795..1e7e68b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "XRP-Blockly", - "version": "0.0.5", + "version": "0.0.6", "description": "XRP Blockly", "main": "app/electron/main.js", "scripts": {