Device-Chip-PMS5003

 view release on metacpan or  search on metacpan

lib/Device/Chip/PMS5003.pm  view on Meta::CPAN


   use Device::Chip::PMS5003;
   use Future::AsyncAwait;

   my $chip = Device::Chip::PMS5003->new;
   await $chip->mount( Device::Chip::Adapter::...->new );

   $chip->start;

   my $readings = await $chip->read_all;

   printf "Particulate matter readings are %d / %d / %d\n",
      @{$readings->{concentration}}{qw( pm1 pm2_5 pm10 )};

=head1 DESCRIPTION

This L<Device::Chip> subclass provides specific communication to a
F<PLANTOWER> F<PMS5003> particle concentration sensor attached to a computer
via a UART adapter. (Though if the communication protocol is the same, it is
likely also useful for a variety of other related sensors too).

The reader is presumed to be familiar with the general operation of this chip;
the documentation here will not attempt to explain or define chip-specific
concepts or features, only the use of this module to access them.

=cut

=head1 METHODS

The following methods documented in an C<await> expression return L<Future>
instances.

=cut

method UART_options
{
   return
      baudrate => 9600;
}

async method _loop
{
   my $buf = "";
   while(1) {
      # A complete notification is 32bytes long
      $buf .= await $self->protocol->read( 32 - length $buf );

      # A notification begins "\x42\x4D"
      $buf =~ s{.*(?=\x42\x4D)}{}s or next;

      # A notification should be header, length, data
      my ( $sof, $len ) = unpack( "a2 s>", $buf );
      # Buffer length should be 28
      $len == 28 or goto next_packet;
      4 + length $buf >= $len or next;

      my @data = unpack( "s>*", substr $buf, 4, 28 );
      my $got_checksum = pop @data;

      my $want_checksum = 0;
      $want_checksum += ord for split m//, substr $buf, 0, 30;

      if( $got_checksum != $want_checksum ) {
         # Checksum failed
         goto next_packet;
      }

      substr( $buf, 0, 32 ) = "";

      $self->on_data( @data );

      next;

next_packet:
      # It's possible the header we found was not a real header but in fact
      # spurious data in the middle of packet.
      substr( $buf, 0, 2 ) = "";

      # Trim down to the next plausible start
      $buf =~ s{^.*\x42\x4D}{}s or $buf = "";
      next;
   }
}

field $_reading_count = 0;
field $_latest_reading;
field $_next_reading_f;

method on_data ( @data )
{
   # From observation, the chip outputs a reading every second but only
   # updates its values every 3. We'll therefore ignore the next two
   if( !$_reading_count ) {
      $_latest_reading = {
         concentration => {
            pm1   => $data[0],
            pm2_5 => $data[1],
            pm10  => $data[2],
         },
         atmos => {
            pm1   => $data[3],
            pm2_5 => $data[4],
            pm10  => $data[5],
         },
         particles => {
            pm0_3 => $data[6],
            pm0_5 => $data[7],
            pm1   => $data[8],
            pm2_5 => $data[9],
            pm5   => $data[10],
            pm10  => $data[11],
         }
      };

      my $f = $_next_reading_f; undef $_next_reading_f;
      $f->done if $f;
   }

   $_reading_count++;
   $_reading_count = 0 if $_reading_count > 2;
}



( run in 0.507 second using v1.01-cache-2.11-cpan-71847e10f99 )