Esempio n. 1
0
    def __init__(self) -> None:
        self.logging = Logging("events")
        self.rgb = RgbLedBlock()

        self.ir_block = IrBlock()
        self.ir_block.add_remote(IrNumericRemote())

        #to demonstrate equal to
        self.ir_block.value.equal_to(RemoteKey("0"), True, self.light_off)
        Ble.value_remote.equal_to(RemoteKey("0"), True, self.light_off)
        #to demonstrate changed
        self.ir_block.value.updated(True, self.changed, self.ir_block.value)
        Ble.value_remote.updated(True, self.changed, Ble.value_remote)
Esempio n. 2
0
class IrBlock(BlockBase):
  _no_data_ready = RemoteKey.get_default()

  def __init__(self, address=None, measurement_period: float=0.1):
    BlockBase.__init__(self, BlockTypes.ir, address)
    self.value = ActiveVariable(RemoteKey.get_default(), measurement_period, self._get_value)
    self._remotes = list()

  def _get_value(self):
    ready = self._tiny_read(_ir_data_ready_command, None, 1)
    if ready and ready[0]:
      data = self._tiny_read(_ir_data_command, None, 4)
      hex_addr = ''.join(['{:02x}'.format(b) for b in data[0:2]])
      self.logging.debug("address ", hex_addr)
      raw_address = data[0] + (data[1] << 8)
      scan_code = data[2] #data[3] (repeat ) is not used yet
      remote = self._get_near_remote(raw_address)
      if remote:
        self.logging.info("scan_code:%d, raw_address:%d", scan_code, raw_address)
        return RemoteKey(remote.find_name_by_scan_code(scan_code), scan_code, remote.get_address())
    return self._no_data_ready #None is reserved for the case that block do not answer

  def add_remote(self, remote_control:RemoteKeyboardBase):
    self._remotes.append(remote_control)

  def _get_near_remote(self, received) -> RemoteKeyboardBase:
    for remote in self._remotes:
      address = remote.get_address()
      count = 0
      for index in range(16):
        if (address >> index) & 0x01 != (received >> index) & 0x01:
          count += 1
      if count <= remote.address_bit_tolerance:
        return remote
    return None
Esempio n. 3
0
    def process_remote_key(cls, scan_code, key_name):
        if not cls._keyboard:
            from remote_control.virtual_keyboard import VirtualKeyboard
            cls._keyboard = VirtualKeyboard()

        cls.value_remote.set(
            RemoteKey(key_name, scan_code, cls._keyboard.get_address()))
Esempio n. 4
0
    def __init__(self) -> None:
        self.speed = 0
        self.pwm = 50

        Ble.value_remote.equal_to(RemoteKey("a"), True, self.pwm_down)
        Ble.value_remote.equal_to(RemoteKey("d"), True, self.pwm_up)
        Ble.value_remote.equal_to(RemoteKey("w"), True, self.speed_up)
        Ble.value_remote.equal_to(RemoteKey("s"), True, self.slow_down)
        Ble.value_remote.equal_to(RemoteKey("z"), True, self.stop)
        Ble.value_remote.equal_to(RemoteKey("o"), True, self.full_speed_up)
        Ble.value_remote.equal_to(RemoteKey("l"), True, self.full_speed_down)

        self.motor_driver_front = MotorDriverBlock(0x20)
        #self.motor_driver_front.change_block_address(0x20)
        self.motor_driver_front.turn_clockwise(MotorDriverBlock.motor1_id)
        self.motor_driver_front.turn_opposite(MotorDriverBlock.motor2_id)

        self.motor_driver_rear = MotorDriverBlock(0x21)
        #self.motor_driver_rear.change_block_address(0x21)
        self.motor_driver_rear.turn_clockwise(MotorDriverBlock.motor1_id)
        self.motor_driver_rear.turn_opposite(MotorDriverBlock.motor2_id)

        self.motor_driver_front.sensor_power_on()
        self.motor_driver_rear.sensor_power_on()
        self.motor_driver_front.reset_sensor_counter(
            MotorDriverBlock.motor1_id)
        self.motor_driver_front.reset_sensor_counter(
            MotorDriverBlock.motor2_id)
        self.motor_driver_rear.reset_sensor_counter(MotorDriverBlock.motor1_id)
        self.motor_driver_rear.reset_sensor_counter(MotorDriverBlock.motor2_id)

        Planner.repeat(1, self.print_counters)
Esempio n. 5
0
 def _get_value(self):
   ready = self._tiny_read(_ir_data_ready_command, None, 1)
   if ready and ready[0]:
     data = self._tiny_read(_ir_data_command, None, 4)
     hex_addr = ''.join(['{:02x}'.format(b) for b in data[0:2]])
     self.logging.debug("address ", hex_addr)
     raw_address = data[0] + (data[1] << 8)
     scan_code = data[2] #data[3] (repeat ) is not used yet
     remote = self._get_near_remote(raw_address)
     if remote:
       self.logging.info("scan_code:%d, raw_address:%d", scan_code, raw_address)
       return RemoteKey(remote.find_name_by_scan_code(scan_code), scan_code, remote.get_address())
   return self._no_data_ready #None is reserved for the case that block do not answer
Esempio n. 6
0
    def __init__(self) -> None:
        self.logging = Logging("events")
        self.chassis = Chassis(power_measurement_period=0.3)
        self.rgb = RgbLedBlock()
        self.display = DisplayBlock()
        self.distance = DistanceBlock(measurement_period=0.1)
        self.button = ButtonBlock(measurement_period=0.1)
        self.ir_block = IrBlock()
        self.ir_block.add_remote(IrNumericRemote())

        self.button.value.equal_to(True, True, self.change_patrol)

        self.near_barrier_event = None
        self.far_bariier_event = None
        self.enough_space_event = None
        self.open_space_event = None
        self.patrol = False

        self.display.contrast(0)
        Ble.value_remote.equal_to(RemoteKey("a"), True, self.turn_left)
        Ble.value_remote.equal_to(RemoteKey("d"), True, self.turn_right)
        Ble.value_remote.equal_to(RemoteKey("w"), True, self.speed_up)
        Ble.value_remote.equal_to(RemoteKey("s"), True, self.slow_down)
        Ble.value_remote.equal_to(RemoteKey("z"), True, self.stop)
        Ble.value_remote.equal_to(RemoteKey("x"), True, self.reverse)
        Ble.value_remote.equal_to(RemoteKey("p"), True, self.change_patrol)

        self.ir_block.value.equal_to(IrNumericRemote.key_left, True,
                                     self.turn_left)
        self.ir_block.value.equal_to(IrNumericRemote.key_right, True,
                                     self.turn_right)
        self.ir_block.value.equal_to(IrNumericRemote.key_top, True,
                                     self.speed_up)
        self.ir_block.value.equal_to(IrNumericRemote.key_bottom, True,
                                     self.slow_down)
        self.ir_block.value.equal_to(IrNumericRemote.key_ok, True, self.stop)
        self.ir_block.value.equal_to(IrNumericRemote.key_hash, True,
                                     self.reverse)
        self.ir_block.value.equal_to(IrNumericRemote.key_star, True,
                                     self.change_patrol)

        self.counter = 0
        Planner.repeat(0.5, self.print_power_info)
        PowerMgmt.set_plan(PowerPlan.get_max_performance_plan())

        self.current_smoother = SmoothedVariable(
            3, SmoothingType.average, self.chassis.power.battery_current_mA)
        self.current_smoother.more_than(600, True, self.stop_for_a_while, 1)
        self.heart_beat = False
Esempio n. 7
0
  def __init__(self) -> None:
    self.logging = Logging("events")
    self.chassis = Chassis(0x20, 0x21)

    Ble.value_remote.equal_to(RemoteKey("a"), True, self.turn_left)
    Ble.value_remote.equal_to(RemoteKey("d"), True, self.turn_right)
    Ble.value_remote.equal_to(RemoteKey("w"), True, self.speed_up)
    Ble.value_remote.equal_to(RemoteKey("s"), True, self.slow_down)
    Ble.value_remote.equal_to(RemoteKey("z"), True, self.stop)
    Ble.value_remote.equal_to(RemoteKey("x"), True, self.reverse)

    Planner.repeat(1, self.print_power_info)
Esempio n. 8
0
 def __init__(self):
   self.value = ActiveVariable(RemoteKey.get_default())
class IrNumericRemote(RemoteKeyboardBase):
    """
  this class describes a cheap remote control with a numeric keyboard and a navigation cross
  """
    address = 65280

    key_1 = RemoteKey("1", 69, address)
    key_2 = RemoteKey("2", 70, address)
    key_3 = RemoteKey("3", 71, address)
    key_4 = RemoteKey("4", 68, address)
    key_5 = RemoteKey("5", 64, address)
    key_6 = RemoteKey("6", 67, address)
    key_7 = RemoteKey("7", 7, address)
    key_8 = RemoteKey("8", 21, address)
    key_9 = RemoteKey("9", 9, address)
    key_0 = RemoteKey("0", 25, address)
    key_star = RemoteKey("*", 22, address)
    key_hash = RemoteKey("#", 13, address)
    key_ok = RemoteKey(SpecialKeys.ok, 28, address)
    key_left = RemoteKey(SpecialKeys.left, 8, address)
    key_right = RemoteKey(SpecialKeys.right, 90, address)
    key_up = RemoteKey(SpecialKeys.up, 24, address)
    key_down = RemoteKey(SpecialKeys.down, 82, address)

    _keys = (
        key_1,
        key_2,
        key_3,
        key_4,
        key_5,
        key_6,
        key_7,
        key_8,
        key_9,
        key_0,
        key_star,
        key_hash,
        key_ok,
        key_left,
        key_right,
        key_up,
        key_down,
    )

    def get_address(self):
        return self.address

    def find_name_by_scan_code(self, scan_code):
        for key in self._keys:
            if key.scan_code == scan_code:
                return key.name
        return None
Esempio n. 10
0
 def __init__(self, address=None, measurement_period: float=0.1):
   BlockBase.__init__(self, BlockTypes.ir, address)
   self.value = ActiveVariable(RemoteKey.get_default(), measurement_period, self._get_value)
   self._remotes = list()
Esempio n. 11
0
class Ble():
    _ble = None
    _shell = None
    _remote_value = None
    _keyboard = None
    _command_handle = None
    _log_handle = None
    _keyboard_handle = None
    _initial_time_up = None
    _running_time_up = None
    _time_down = None
    _time_to_power_save = None
    value_remote: ActiveVariable = ActiveVariable(RemoteKey.get_default())

    @classmethod
    def init(cls) -> None:
        PowerMgmt.register_management_change_callback(
            cls._set_power_save_timeouts)
        cls._set_power_save_timeouts(
            PowerMgmt.get_plan())  # to be set defaults
        Planner.plan(cls._check_time_to_power_save, True)

        Logging.add_logger(BleLogger)
        cls._start_ble(
        )  #to be allocated big blocks in the beginning it should prevent memory fragmentation
        cls.just_initialized = True

    @classmethod
    def _set_power_save_timeouts(cls, power_plan: PowerPlan):
        cls._initial_time_up = power_plan.ble_plan.initial_time_up
        cls._running_time_up = power_plan.ble_plan.running_time_up
        cls._time_down = power_plan.ble_plan.time_down
        if cls._time_to_power_save != 0:  #if ble connection is not established already
            cls._time_to_power_save = cls._initial_time_up

    @classmethod
    def _check_time_to_power_save(cls, wake_up):
        #micropython.mem_info(False)
        go_to_power_save = False
        try:  #to do not be _check_time_to_power_save interupted due to a BLE issue
            if wake_up:
                PowerMgmt.block_power_save()
                if not cls._time_to_power_save:  #can be reset from constructor
                    cls._time_to_power_save = cls._running_time_up
                if cls.just_initialized:
                    cls.just_initialized = False
                else:
                    cls._start_ble()
                #cls._advertise() #just for sure it is turned on

                print("BLE power-save blocked")
            else:
                if cls._time_to_power_save:  #if time was not reset externally (e.g. ble connection)
                    cls._time_to_power_save -= 1
                    if cls._time_to_power_save == 0:  #if time has been reset by decreasing
                        go_to_power_save = True
                        # ble is disabled automatically when power save is activated and is enabled again when the program runs again
                        # but it is not reliable (advertisement si not started) - lets do it manually
                        #cls.disconnect()
                        cls._stop_ble()
                        PowerMgmt.unblock_power_save()
                        print("BLE power-save allowed")

        except Exception as error:
            print("BLE error")
            #pylint: disable=no-member ;implemented in micropython
            sys.print_exception(error)
            #micropython.mem_info(0)

        delay = cls._time_down if go_to_power_save else 1
        Planner.postpone(delay, cls._check_time_to_power_save,
                         go_to_power_save)

    @classmethod
    def _start_ble(cls):
        cls._ble = None
        cls._ble = bluetooth.BLE()
        cls._ble.active(True)
        #cls._ble.config(rxbuf=_BMS_MTU)
        cls._ble.irq(cls._irq)

        #cls._ble.config(mtu=_BMS_MTU)
        ((cls._command_handle, cls._log_handle,
          cls._keyboard_handle), ) = cls._ble.gatts_register_services(
              (_HUGO_SERVICE, ))
        cls._connections = set()
        cls._payload = cls.advertising_payload(
            name="HuGo",
            services=[_HUGO_SERVICE],
            appearance=_ADV_APPEARANCE_GENERIC_THERMOMETER)

        cls._advertise()

    @classmethod
    def _stop_ble(cls):
        cls.disconnect()
        cls._ble.active(False)

    @classmethod
    def get_shell(
        cls
    ):  #saving RAM shell and remote_values are ussually not loaded together
        if not cls._shell:
            from basal.shell import Shell
            cls._shell = Shell
        return cls._shell

    @classmethod
    def get_remote_value(cls):
        if not cls._remote_value:
            from basal.remote_value import RemoteValue
            cls._remote_value = RemoteValue
        return cls._remote_value

    @classmethod
    def process_remote_key(cls, scan_code, key_name):
        if not cls._keyboard:
            from remote_control.virtual_keyboard import VirtualKeyboard
            cls._keyboard = VirtualKeyboard()

        cls.value_remote.set(
            RemoteKey(key_name, scan_code, cls._keyboard.get_address()))

    @classmethod
    def process_command(cls, conn_handle, value_handle, command, data):
        if command < ble_ids.cmd_shell_last:
            command_solver = cls.get_shell()
        else:
            command_solver = cls.get_remote_value()

        ret_data = command_solver.command_request(command, data)
        if ret_data is not None:
            cls._ble.gatts_notify(conn_handle, value_handle, ret_data)

    @classmethod
    def _irq(cls, event, data):
        # Track connections so we can send notifications.
        if event == _IRQ_CENTRAL_CONNECT:
            conn_handle, _, _ = data
            #NOTE: use when mtu is necessary to change
            connected = False
            for _ in range(3):  #sometimes attempts to exchange mtu fails
                try:
                    cls._ble.gattc_exchange_mtu(conn_handle)
                    connected = True
                    cls._time_to_power_save = 0  # disable power save while a connection is active
                    break
                except Exception:
                    print("Error: gattc_exchange_mtu failed")
            if connected:
                cls._connections.add(conn_handle)
                print("BLE new connection: " + str(conn_handle))
            else:
                cls._ble.gap_disconnect(conn_handle)

        elif event == _IRQ_CENTRAL_DISCONNECT:
            print("disconnect")
            conn_handle, _, _ = data
            cls._connections.remove(conn_handle)
            if not cls._connections:
                cls._time_to_power_save = cls._initial_time_up  # enable power save with initial time out - to be possible to reconnect
            print("BLE disconnected " + str(conn_handle))
            # Start advertising again to allow a new connection.
            cls._advertise()
        elif event == _IRQ_GATTS_INDICATE_DONE:
            conn_handle, value_handle, _status = data

        elif event == _IRQ_GATTS_WRITE:
            conn_handle, value_handle = data
            value = cls._ble.gatts_read(value_handle)
            if value_handle == cls._command_handle and value and len(
                    value) > 0:
                command = value[0]
                data = value[1:]

                Planner.plan(cls.process_command, conn_handle, value_handle,
                             command, data)

            if value_handle == cls._keyboard_handle:
                scan_code = int.from_bytes(value[0:2], "big", True)
                key_name = value[2:].decode("utf-8")
                print("scan_code: {}, key_name: {}".format(
                    str(scan_code), str(key_name)))
                Planner.plan(cls.process_remote_key, scan_code, key_name)

        elif event == _IRQ_MTU_EXCHANGED:
            _conn_handle, mtu = data
            print("_IRQ_MTU_EXCHANGED mtu:", mtu)
        elif event == _IRQ_SET_SECRET:
            sec_type, key, value = data
            print("_IRQ_SET_SECRET sec_type:", sec_type, " key:", key, "value",
                  value)
        else:
            print("unhandled event: " + str(event))

    @classmethod
    def advertising_payload(limited_disc=False,
                            br_edr=False,
                            name=None,
                            services=None,
                            appearance=0):
        payload = bytearray()

        def _append(adv_type, value):
            nonlocal payload
            payload += struct.pack("BB", len(value) + 1, adv_type) + value

        _append(
            _ADV_TYPE_FLAGS,
            struct.pack("B", (0x01 if limited_disc else 0x02) +
                        (0x18 if br_edr else 0x04)),
        )

        if name:
            _append(_ADV_TYPE_NAME, name)

        if services:
            _append(_ADV_TYPE_UUID16_COMPLETE, b"HuGo")

        # See org.bluetooth.characteristic.gap.appearance.xml
        if appearance:
            _append(_ADV_TYPE_APPEARANCE, struct.pack("<h", appearance))

        return payload

    @classmethod
    def _advertise(cls, interval_us=100000):
        cls._ble.gap_advertise(interval_us, adv_data=cls._payload)

    @classmethod
    def disconnect(cls):
        for connection in cls._connections:
            cls._ble.gap_disconnect(connection)

    @classmethod
    def notify_log(cls, message):
        try:
            for connection in cls._connections:
                cls._ble.gatts_notify(connection, cls._log_handle, message)
        except Exception as error:
            print("notify_log error: ", error)