Exemplo n.º 1
0
class Thermometer:
    """
    Touch the temperature sensor on the playground express.
    As the temperature increases, pixels will illuminate to
    indicate the temperature.
    """

    def __init__(self):
        # a dictionary of pixels as the key and
        # associated rgb color values
        self.pix_d = {
            0: [153, 76, 0],
            1: [153, 0, 0],
            2: [153, 153, 0],
            3: [204, 204, 0],
            4: [0, 153, 0],
            5: [0, 153, 153],
            6: [0, 0, 233],
            7: [153, 0, 153],
            8: [255, 0, 255],
            9: [255, 255, 255],
        }

        # save the previous pixel number that was illuminated
        self.last_pixel_used = 0

        # save the first temperature read as the ambient temperature
        # and use that as the basis of comparison.
        self.ambient = 0

        # instantiate pymatacpx
        self.p = PyMataCpx()

        # clear the pixels before monitoring the light
        # sensor values
        self.p.cpx_pixels_clear()
        # set pixel 0 to be the initial pixel and set its rgb
        # from the dictionary
        self.p.cpx_pixel_set(0, *self.pix_d[0])

        self.p.cpx_pixels_show()

        # enable the temperature sensor and provide a callback.
        self.p.cpx_temperature_start(self.temp_callback)

        print()
        print('Touch the temperature sensor. As the temperature changes')
        print('Different neopixels will light up.')
        print()
        print('The current temperature and pixel selected will be shown on the console.')
        print()

        while True:
            # just kill time waiting for a light data to arrive
            try:
                time.sleep(.1)
            except KeyboardInterrupt:
                # If you press control-C, cleanly exit
                self.p.cpx_close_and_exit()

    # This is the callback to process light sensor data
    def temp_callback(self, data):
        """
        Light sensor data processor
        :param data: data[0] = 2 analog mode
                     data[1] = pin 0
                     data[2] = temp in degrees C
        """
        # the current temperature
        the_current_temperature = data[2]

        # save the temperature the first time
        # through as ambient
        if not self.ambient:
            self.ambient = the_current_temperature

        # select the pixel based on the current temperature
        if self.ambient < the_current_temperature < (self.ambient + .6):
            pixel = 0
        elif (self.ambient + 0.6) < the_current_temperature < (self.ambient + 1.2):
            pixel = 1
        elif (self.ambient < the_current_temperature + 1.2) < the_current_temperature < (self.ambient + 1.8):
            pixel = 2
        elif (self.ambient < the_current_temperature + 1.8) < the_current_temperature < (self.ambient + 2.4):
            pixel = 3
        elif (self.ambient < the_current_temperature + 2.4) < the_current_temperature < (self.ambient + 3.0):
            pixel = 4
        elif (self.ambient < the_current_temperature + 3.0) < the_current_temperature < (self.ambient + 3.6):
            pixel = 5
        elif (self.ambient < the_current_temperature + 3.6) < the_current_temperature < (self.ambient + 4.2):
            pixel = 6
        elif (self.ambient < the_current_temperature + 4.2) < the_current_temperature < (self.ambient + 4.8):
            pixel = 7
        elif (self.ambient < the_current_temperature + 4.8) < the_current_temperature < (self.ambient + 5.4):
            pixel = 8
        else:
            pixel = 9

        # get the rgb value for the pixel
        rgb = self.pix_d[pixel]

        # if this is not the same pixel as the last one enabled
        # then manipulate the pixels.

        # turn off the current pixel and turn on the new one.
        if pixel != self.last_pixel_used:
            # extinguish the previous pixel
            self.p.cpx_pixel_set(self.last_pixel_used, 0, 0, 0)

            # set the new pixel
            self.p.cpx_pixel_set(pixel, *rgb)

            # control the pixels
            self.p.cpx_pixels_show()
            self.last_pixel_used = pixel

        print('ambient: {} current: {} pixel{}'.format(round(self.ambient, 3),
                                                       round(the_current_temperature, 3),
                                                       pixel))
Exemplo n.º 2
0
class CpxGateway(GatewayBase, threading.Thread):
    """
    This class is the interface class for the Circuit Playground
    Express supporting Scratch 3.
    """
    def __init__(self,
                 *subscriber_list,
                 back_plane_ip_address=None,
                 subscriber_port='43125',
                 publisher_port='43124',
                 process_name='CpxGateway',
                 publisher_topic=None,
                 log=False):
        """
        :param subscriber_list: a tuple or list of subscription topics.
        :param back_plane_ip_address:
        :param subscriber_port:
        :param publisher_port:
        :param process_name:
        """

        # initialize parent
        super(CpxGateway,
              self).__init__(subscriber_list=subscriber_list,
                             back_plane_ip_address=back_plane_ip_address,
                             subscriber_port=subscriber_port,
                             publisher_port=publisher_port,
                             process_name=process_name)
        self.log = log
        if self.log:
            fn = str(pathlib.Path.home()) + "/cpxgw.log"
            self.logger = logging.getLogger(__name__)
            logging.basicConfig(filename=fn, filemode='w', level=logging.DEBUG)
            sys.excepthook = self.my_handler

        self.publisher_topic = publisher_topic
        self.cpx = PyMataCpx()
        atexit.register(self.shutdown)

        # hold the time of the last analog data to be received.
        # use to determine if connectivity is gone.
        self.last_analog_data_time = None

        # start up all the sensors
        self.cpx.cpx_accel_start(self.tilt_callback)
        self.cpx.cpx_button_a_start(self.switch_callback)
        self.cpx.cpx_button_b_start(self.switch_callback)
        self.cpx.cpx_slide_switch_start(self.switch_callback)

        self.cpx.cpx_light_sensor_start(self.analog_callback)
        self.cpx.cpx_microphone_start(self.analog_callback)
        self.cpx.cpx_temperature_start(self.analog_callback)
        for touch_pad in range(1, 8):
            self.cpx.cpx_cap_touch_start(touch_pad, self.touchpad_callback)

        threading.Thread.__init__(self)
        self.daemon = True

        # start the watchdog thread
        self.start()
        # start the banyan receive loop
        try:
            self.receive_loop()
        except:
            pass
        # except KeyboardInterrupt:
        # except KeyboardInterrupt:
        # self.cpx.cpx_close_and_exit()
        # sys.exit(0)
        #     os._exit(1)

    def init_pins_dictionary(self):
        pass

    def play_tone(self, topic, payload):
        """
        This method plays a tone on a piezo device connected to the selected
        pin at the frequency and duration requested.
        Frequency is in hz and duration in milliseconds.

        Call set_mode_tone before using this method.
        :param topic: message topic
        :param payload: {"command": "play_tone", "pin": “PIN”, "tag": "TAG",
                         “freq”: ”FREQUENCY”, duration: “DURATION”}
        """
        self.cpx.cpx_tone(payload['freq'], payload['duration'])

    def additional_banyan_messages(self, topic, payload):
        if payload['command'] == 'pixel':
            self.set_pixel(payload)

    def set_pixel(self, payload):
        self.cpx.cpx_pixel_set(payload['pixel'], payload['red'],
                               payload['green'], payload['blue'])
        self.cpx.cpx_pixels_show()

    def digital_write(self, topic, payload):
        """
        This method performs a digital write to the board LED
        :param topic: message topic
        :param payload: {"command": "digital_write", "pin": “PIN”, "value": “VALUE”}
        """
        if payload['value']:
            self.cpx.cpx_board_light_on()
        else:
            self.cpx.cpx_board_light_off()

    # The CPX sensor callbacks

    def tilt_callback(self, data):
        """
        Report the tilt of the express board

        Take the raw xyz data and transform it to
        positional strings.
        :param data: data [0] = data mode 32 is analog.
                     data[1] = the pin number - this is a pseudo pin number
                     data[2] = x value
                     data[3] = y value
                     data[4] = z value
        """
        x = data[2]
        y = data[3]
        z = data[4]

        # Convert raw Accelerometer values to degrees
        x_angle = int((math.atan2(y, z) + math.pi) * (180 / math.pi))
        y_angle = int((math.atan2(z, x) + math.pi) * (180 / math.pi))

        h = v = -1

        if 175 < x_angle < 185 and 265 < y_angle < 275:
            h = v = 0  # 'flat'

        elif h or v:
            if 180 <= x_angle <= 270:
                v = 1  # up

            elif 90 <= x_angle <= 180:
                v = 2  # down

            if 180 <= y_angle <= 270:
                h = 3  # left

            elif 270 <= y_angle <= 360:
                h = 4  # right

        payload = {'report': 'tilted', 'value': [v, h]}
        self.publish_payload(payload, 'from_cpx_gateway')

    def switch_callback(self, data):
        """
        This handles switches a, b, and slide
        :param data: data[1] - a=4 b=5, slice=7,
        """
        if data[1] == 4:
            switch = 'a'
        elif data[1] == 5:
            switch = 'b'
        else:
            # 0 = right, 1 = left
            switch = 'slide'

        payload = {'report': switch, 'value': data[2]}
        self.publish_payload(payload, 'from_cpx_gateway')

    def analog_callback(self, data):
        """
        This handles the light, temperature and sound sensors.

        It also sets up a "watchdog timer" and if there is no activity
        for > 1 second will exit.


        :param data: data[1] - 8 = light, temp = 9, 10 = sound,
        """

        self.last_analog_data_time = time.time()

        if data[1] == 8:
            sensor = 'light'
        elif data[1] == 9:
            sensor = 'temp'
        else:
            sensor = 'sound'
        payload = {'report': sensor, 'value': round(data[2], 2)}
        self.publish_payload(payload, 'from_cpx_gateway')

    def touchpad_callback(self, data):
        """
        Build and send a banyan message for the pad and value
        :param data: data[1] = touchpad and data[2] = boolean value
        """
        payload = {'report': 'touch' + str(data[1]), 'value': int(data[2])}
        self.publish_payload(payload, 'from_cpx_gateway')

    def shutdown(self):
        try:
            self.cpx.cpx_close_and_exit()
            sys.exit(0)
        except serial.serialutil.SerialException:
            pass

    def my_handler(self, xtype, value, tb):
        """
        for logging uncaught exceptions
        :param xtype:
        :param value:
        :param tb:
        :return:
        """
        self.logger.exception("Uncaught exception: {0}".format(str(value)))

    def run(self):
        if not self.last_analog_data_time:
            self.last_analog_data_time = time.time()
        while True:
            if time.time() - self.last_analog_data_time > 1.0:
                print('Watchdog timed out - exiting.')
                os._exit(1)

            time.sleep(1)