Improving dust sensor resolution from 30 seconds to 10 seconds

Hi,

Does anyone know if it is possible to reduce the resolution of the dust sensor’s measurements from every 30 seconds to every 10 seconds? If so, can you point me to the place in the code where this parameter can be changed?

Thanks

Hi @edrjeffery,

You could do that by altering the source of the firmware at this location:

Notice that the time unit is milliseconds, so you’d have to play around with thousands instead.

Then, compile it and flash it with avrdude.

Thank you!

Hi @RobertLucian,

Are you able to briefly describe how I compile and flash the file please?

I’ve tried myself, but I get an “AVR device not responding” error from avrdude. I’ve listed the steps I took below in case that helps at all:

  1. Compiled my edited grove_pi_v1_2_7.ino file using the Arduino IDE
  2. Tried to flash using the command: avrdude -c gpio -p m328p -U flash:w:grove_pi_v1_2_7.cpp.hex. This gave me the error “Can’t find programmer id ‘gpio’”
  3. Checked that linuxgpio was enabled and then uncommented the programmer in the /etc/avrdude.conf file
  4. Tried to flash using the command: avrdude -c linuxgpio -p m328p -U flash:w:grove_pi_v1_2_7.cpp.hex. This gave me the error “AVR device not responding”.

Do you have any ideas of what is going wrong?

Thanks!

Hi @edrjeffery,

Well first of all, you need to have the following configuration for the gpio programmer in /etc/avrdude.conf configuration file:

programmer
  id    = "gpio";
  desc  = "Use sysfs interface to bitbang GPIO lines";
  type  = gpio;
  reset = 8;
  sck   = 11;
  mosi  = 10;
  miso  = 9;
;

And second of all, when burning the hex file to the grovepi, you need to run the following commands:

avrdude -c gpio -p m328p -U lfuse:w:0xFF:m
avrdude -c gpio -p m328p -U hfuse:w:0xDA:m
avrdude -c gpio -p m328p -U efuse:w:0x05:m
avrdude -c gpio -p m328p -U flash:w:grove_pi_firmware.hex

This is all you need to do in order to flash a compiled program to the GrovePi.

Please let me know if you have other issues.

Thank you!

Hi @RobertLucian,

I followed the steps above, but I now get an IO error when I run the grove_dust_sensor.py code. The error code is 121 (Remote I/O error). I have double checked that Remote IO and I2C are both on in the raspi-config settings. I have also turned off the Pi, disconnected the GrovePi and tried again, but that hasn’t helped. I couldn’t find a solution online so I have run the GrovePi troubleshooting script, with the output shown below:

Check space left
================
Filesystem      Size  Used Avail Use% Mounted on
/dev/root       7.2G  4.6G  2.3G  68% /
devtmpfs        213M     0  213M   0% /dev
tmpfs           217M     0  217M   0% /dev/shm
tmpfs           217M  3.5M  214M   2% /run
tmpfs           5.0M  4.0K  5.0M   1% /run/lock
tmpfs           217M     0  217M   0% /sys/fs/cgroup
/dev/mmcblk0p1   43M   22M   21M  51% /boot
tmpfs            44M  4.0K   44M   1% /run/user/1000

Check for dependencies
======================
python 2.7.13-2 install ok installed
python-pip 9.0.1-2+rpt2 install ok installed
git 1:2.11.0-3+deb9u3 install ok installed
libi2c-dev 3.1.2-3 install ok installed
python-serial 3.2.1-1 install ok installed
python-rpi.gpio 0.6.3~stretch-1 install ok installed
i2c-tools 3.1.2-3 install ok installed
python-smbus 3.1.2-3 install ok installed
arduino 2:1.0.5+dfsg2-4.1 install ok installed
minicom 2.7-1.1 install ok installed
scratch 1.4.0.6~dfsg1-5 install ok installed

find: ‘/run/user/1000/gvfs’: Permission denied
wiringPi Not Found (ERR)
find: ‘/run/user/1000/gvfs’: Permission denied
I2C still in blacklist (ERR)
SPI still in blacklist (ERR)


Check for addition in /modules
==============================
I2C-dev already there
i2c-bcm2708 already there
spi-dev already there


Hardware revision
=================
gpio version: 2.44
Copyright (c) 2012-2017 Gordon Henderson
This is free software with ABSOLUTELY NO WARRANTY.
For details type: gpio -warranty

Raspberry Pi Details:
  Type: Pi Zero, Revision: 03, Memory: 512MB, Maker: Sony 
  * Device tree is enabled.
  *--> Raspberry Pi Zero Rev 1.3
  * This Raspberry Pi supports user-level GPIO access.


Check the /dev folder
=====================
i2c-1
ttyAMA0

USB device status
=================
Bus 001 Device 006: ID 0bda:8152 Realtek Semiconductor Corp. 
Bus 001 Device 005: ID 0a5c:bd1e Broadcom Corp. 
Bus 001 Device 004: ID 413c:2107 Dell Computer Corp. 
Bus 001 Device 003: ID 046d:c52b Logitech, Inc. Unifying Receiver
Bus 001 Device 002: ID 1a40:0101 Terminus Technology Inc. Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

/:  Bus 01.Port 1: Dev 1, Class=root_hub, Driver=dwc_otg/1p, 480M
    |__ Port 1: Dev 2, If 0, Class=Hub, Driver=hub/4p, 480M
        |__ Port 1: Dev 3, If 1, Class=Human Interface Device, Driver=usbhid, 12M
        |__ Port 1: Dev 3, If 2, Class=Human Interface Device, Driver=usbhid, 12M
        |__ Port 1: Dev 3, If 0, Class=Human Interface Device, Driver=usbhid, 12M
        |__ Port 2: Dev 4, If 0, Class=Human Interface Device, Driver=usbhid, 1.5M
        |__ Port 3: Dev 5, If 0, Class=Vendor Specific Class, Driver=brcmfmac, 480M
        |__ Port 4: Dev 6, If 0, Class=Vendor Specific Class, Driver=r8152, 480M
Raspbian for Robots Version
===========================
cat: /home/pi/di_update/Raspbian_For_Robots/Version: No such file or directory


Hostname
========
raspberrypi


Checking for Atmega chip
========================


avrdude: Version 6.3
         Copyright (c) 2000-2005 Brian Dean, http://www.bdmicro.com/
         Copyright (c) 2007-2014 Joerg Wunsch

         System wide configuration file is "/etc/avrdude.conf"
         User configuration file is "/root/.avrduderc"
         User configuration file does not exist or is not a regular file, skipping


avrdude: Can't find programmer id "gpio"

Valid programmers are:
  2232HIO          = FT2232H based generic programmer [/etc/avrdude.conf:412]
  4232h            = FT4232H based generic programmer [/etc/avrdude.conf:440]
  arduino          = Arduino                        [/etc/avrdude.conf:360]
  arduino-ft232r   = Arduino: FT232R connected to ISP [/etc/avrdude.conf:764]
  atmelice         = Atmel-ICE (ARM/AVR) in JTAG mode [/etc/avrdude.conf:1114]
  atmelice_dw      = Atmel-ICE (ARM/AVR) in debugWIRE mode [/etc/avrdude.conf:1130]
  atmelice_isp     = Atmel-ICE (ARM/AVR) in ISP mode [/etc/avrdude.conf:1138]
  atmelice_pdi     = Atmel-ICE (ARM/AVR) in PDI mode [/etc/avrdude.conf:1122]
  avr109           = Atmel AppNote AVR109 Boot Loader [/etc/avrdude.conf:885]
  avr910           = Atmel Low Cost Serial Programmer [/etc/avrdude.conf:721]
  avr911           = Atmel AppNote AVR911 AVROSP    [/etc/avrdude.conf:892]
  avrftdi          = FT2232D based generic programmer [/etc/avrdude.conf:386]
  avrisp           = Atmel AVR ISP                  [/etc/avrdude.conf:608]
  avrisp2          = Atmel AVR ISP mkII             [/etc/avrdude.conf:628]
  avrispmkII       = Atmel AVR ISP mkII             [/etc/avrdude.conf:622]
  avrispv2         = Atmel AVR ISP V2               [/etc/avrdude.conf:615]
  buspirate        = The Bus Pirate                 [/etc/avrdude.conf:633]
  buspirate_bb     = The Bus Pirate (bitbang interface, supports TPI) [/etc/avrdude.conf:640]
  butterfly        = Atmel Butterfly Development Board [/etc/avrdude.conf:878]
  butterfly_mk     = Mikrokopter.de Butterfly       [/etc/avrdude.conf:906]
  bwmega           = BitWizard ftdi_atmega builtin programmer [/etc/avrdude.conf:751]
  C232HM           = FT232H based module from FTDI and Glyn.com.au [/etc/avrdude.conf:509]
  c2n232i          = serial port banging, reset=dtr sck=!rts mosi=!txd miso=!cts [/etc/avrdude.conf:1294]
  dasa             = serial port banging, reset=rts sck=dtr mosi=txd miso=cts [/etc/avrdude.conf:1266]
  dasa3            = serial port banging, reset=!dtr sck=rts mosi=txd miso=cts [/etc/avrdude.conf:1280]
  diecimila        = alias for arduino-ft232r       [/etc/avrdude.conf:775]
  dragon_dw        = Atmel AVR Dragon in debugWire mode [/etc/avrdude.conf:1041]
  dragon_hvsp      = Atmel AVR Dragon in HVSP mode  [/etc/avrdude.conf:1032]
  dragon_isp       = Atmel AVR Dragon in ISP mode   [/etc/avrdude.conf:1014]
  dragon_jtag      = Atmel AVR Dragon in JTAG mode  [/etc/avrdude.conf:1005]
  dragon_pdi       = Atmel AVR Dragon in PDI mode   [/etc/avrdude.conf:1050]
  dragon_pp        = Atmel AVR Dragon in PP mode    [/etc/avrdude.conf:1023]
  flip1            = FLIP USB DFU protocol version 1 (doc7618) [/etc/avrdude.conf:1161]
  flip2            = FLIP USB DFU protocol version 2 (AVR4023) [/etc/avrdude.conf:1168]
  ft232r           = FT232R Synchronous BitBang     [/etc/avrdude.conf:739]
  ft245r           = FT245R Synchronous BitBang     [/etc/avrdude.conf:728]
  jtag1            = Atmel JTAG ICE (mkI)           [/etc/avrdude.conf:919]
  jtag1slow        = Atmel JTAG ICE (mkI)           [/etc/avrdude.conf:924]
  jtag2            = Atmel JTAG ICE mkII            [/etc/avrdude.conf:954]
  jtag2avr32       = Atmel JTAG ICE mkII im AVR32 mode [/etc/avrdude.conf:987]
  jtag2dw          = Atmel JTAG ICE mkII in debugWire mode [/etc/avrdude.conf:969]
  jtag2fast        = Atmel JTAG ICE mkII            [/etc/avrdude.conf:948]
  jtag2isp         = Atmel JTAG ICE mkII in ISP mode [/etc/avrdude.conf:960]
  jtag2pdi         = Atmel JTAG ICE mkII PDI mode   [/etc/avrdude.conf:996]
  jtag2slow        = Atmel JTAG ICE mkII            [/etc/avrdude.conf:943]
  jtag3            = Atmel AVR JTAGICE3 in JTAG mode [/etc/avrdude.conf:1058]
  jtag3dw          = Atmel AVR JTAGICE3 in debugWIRE mode [/etc/avrdude.conf:1074]
  jtag3isp         = Atmel AVR JTAGICE3 in ISP mode [/etc/avrdude.conf:1082]
  jtag3pdi         = Atmel AVR JTAGICE3 in PDI mode [/etc/avrdude.conf:1066]
  jtagkey          = Amontec JTAGKey, JTAGKey-Tiny and JTAGKey2 [/etc/avrdude.conf:447]
  jtagmkI          = Atmel JTAG ICE (mkI)           [/etc/avrdude.conf:911]
  jtagmkII         = Atmel JTAG ICE mkII            [/etc/avrdude.conf:935]
  jtagmkII_avr32   = Atmel JTAG ICE mkII im AVR32 mode [/etc/avrdude.conf:978]
  linuxgpio        = Use the Linux sysfs interface to bitbang GPIO lines [/etc/avrdude.conf:1188]
  linuxspi         = Use Linux SPI device in /dev/spidev* [/etc/avrdude.conf:1215]
  lm3s811          = Luminary Micro LM3S811 Eval Board (Rev. A) [/etc/avrdude.conf:570]
  mib510           = Crossbow MIB510 programming board [/etc/avrdude.conf:672]
  mkbutterfly      = Mikrokopter.de Butterfly       [/etc/avrdude.conf:900]
  nibobee          = NIBObee                        [/etc/avrdude.conf:847]
  o-link           = O-Link, OpenJTAG from www.100ask.net [/etc/avrdude.conf:541]
  openmoko         = Openmoko debug board (v3)      [/etc/avrdude.conf:552]
  pavr             = Jason Kyle's pAVR Serial Programmer [/etc/avrdude.conf:1147]
  pickit2          = MicroChip's PICkit2 Programmer [/etc/avrdude.conf:1154]
  ponyser          = design ponyprog serial, reset=!txd sck=rts mosi=dtr miso=cts [/etc/avrdude.conf:1244]
  siprog           = Lancos SI-Prog <http://www.lancos.com/siprogsch.html> [/etc/avrdude.conf:1257]
  stk500           = Atmel STK500                   [/etc/avrdude.conf:658]
  stk500hvsp       = Atmel STK500 V2 in high-voltage serial programming mode [/etc/avrdude.conf:693]
  stk500pp         = Atmel STK500 V2 in parallel programming mode [/etc/avrdude.conf:686]
  stk500v1         = Atmel STK500 Version 1.x firmware [/etc/avrdude.conf:665]
  stk500v2         = Atmel STK500 Version 2.x firmware [/etc/avrdude.conf:679]
  stk600           = Atmel STK600                   [/etc/avrdude.conf:700]
  stk600hvsp       = Atmel STK600 in high-voltage serial programming mode [/etc/avrdude.conf:714]
  stk600pp         = Atmel STK600 in parallel programming mode [/etc/avrdude.conf:707]
  ttl232r          = FTDI TTL232R-5V with ICSP adapter [/etc/avrdude.conf:811]
  tumpa            = TIAO USB Multi-Protocol Adapter [/etc/avrdude.conf:591]
  UM232H           = FT232H based module from FTDI and Glyn.com.au [/etc/avrdude.conf:482]
  uncompatino      = uncompatino with all pairs of pins shorted [/etc/avrdude.conf:787]
  usbasp           = USBasp, http://www.fischl.de/usbasp/ [/etc/avrdude.conf:822]
  usbasp-clone     = Any usbasp clone with correct VID/PID [/etc/avrdude.conf:858]
  usbtiny          = USBtiny simple USB programmer, http://www.ladyada.net/make/usbtinyisp/ [/etc/avrdude.conf:869]
  wiring           = Wiring                         [/etc/avrdude.conf:353]
  xplainedmini     = Atmel AVR XplainedMini in ISP mode [/etc/avrdude.conf:1098]
  xplainedmini_dw  = Atmel AVR XplainedMini in debugWIRE mode [/etc/avrdude.conf:1106]
  xplainedpro      = Atmel AVR XplainedPro in JTAG mode [/etc/avrdude.conf:1090]


Checking I2C bus for devices
============================

Checking I2C bus 0
==================
Error: Could not open file `/dev/i2c-0' or `/dev/i2c/0': No such file or directory

Checking I2C bus 1
==================
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- 04 -- -- -- -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- --                         

Checking for firmware version
=============================
Traceback (most recent call last):
  File "/home/pi/GrovePi/Software/Python/grove_firmware_version_check.py", line 40, in <module>
    print("GrovePi has firmware version: %s" %grovepi.version())
  File "/home/pi/GrovePi/Software/Python/grovepi.py", line 266, in version
    return "%s.%s.%s" % (number[1], number[2], number[3])
TypeError: 'int' object has no attribute '__getitem__'

@RobertLucian My guess is that it is a firmware issue (given the GrovePi is detected by i2cdetect). All I have done is change the 30000 to 10000, compiled using the Arduino IDE and then flashed using your instructions above, so I’m not sure what could have gone wrong…

I also forgot to mention that when creating a new ‘gpio’ programmer, I kept on getting error messages saying the avrdude.conf file could not be read. So I edited the ‘linuxgpio’ programmer to be exactly the same, but just a different name. I’m not sure if this will have made a difference, but it seemed to work when flashing an old (unchanged) hex file.

@RobertLucian,

Sorry for another message, but I’ve been working on this over the weekend so far and wanted to provide an update. I think I have narrowed the issue down to the compiling of the grove_pi_v1_2_7.ino file.

  • I copied the compiled hex file from your GitHub page (https://github.com/DexterInd/GrovePi/blob/master/Firmware/Source/v1.2/grove_pi_v1_2_7/grove_pi_v1_2_7.cpp.hex) and then used the avrdude commands you gave me to flash it to the GrovePi. This worked and I can run the grove_dust_sensor.py file.
  • I then compiled the UNEDITED .ino file using the Arduino IDE. This outputted a hex file to the /tmp folder, which I then flashed to the GrovePi using the same avrdude commands. This caused the grove_dust_sensor.py to NOT work.
  • I then compiled the EDITED .ino file using the Arduino IDE. This outputted a hex file to the /tmp folder, which I then flashed to the GrovePi using the same avrdude commands. This caused the grove_dust_sensor.py to NOT work.

From this, I have concluded that it must be an issue with the compiling, since your already compiled hex file can be flashed and works, but when I compile the relevant .ino file myself and flash it does not work.

Hope that helps the troubleshooting in some way.

I have an idea for how to solve this: would you please compile the edited .ino file (10000ms instead of 30000ms) at your end and send the hex file over to me? If you have the correct Arduino IDE set up, then this should then work on my GrovePi once it is flashed. What do you think?

Ed

Hi @edrjeffery,

Yep, I’ve compiled it on my own and I’ve changed the timing from 30 seconds to just 10. I haven’t tested it, though it should work.
firmware.cpp (33.5 KB)

Because I can’t upload executables in here, I changed the program’s extension to cpp, so keep this in mind.

Let me know how it goes.

Thank you!

Hi @RobertLucian,

Thank you for sending that over. I have flashed it and I no longer get the error, which is a good sign. However, I do not get a reading every 10 seconds. It seems more random, and it can take over a minute between measurements. Any idea what is going on?

Thanks!

Hi @edrjeffery,

Not yet, but I’m already investigating the problem. Which by the way, if you happen to find a solution faster than me, I welcome it. So please let me know if you find something.

Thank you!

Hi @edrjeffery,

I think I have something you can toy around. So for the GrovePi I have created 3 additional functions which give you the following functionalities:

  1. setDustSensorInterval(interval_ms) - sets the dust’s sensor interval for the sample rate (i.e. 30,000ms or 10,000ms or such).

  2. getDustSensorInterval() - retrieves you the current interval set for the dust sensor. Be aware that on each power up the GrovePi will default to 30,000 ms so you’ll have to reset it in a python script.

  3. dustSensorReadMore(blocking = True) - retrieves you the Low Pulse Occupancy variable, the percentage which reflects the LPO time with respect to the entire interval spent for a sample and the concentration measured in pcs/238ml (=0.01cf). If there’s anyone who wants to better understand the relationship between all these 3 returned variables, check the added graph.

Now, since all these are not a part of the official firmware yet, here are the functions you need to add to the grovepi module:

dust_sensor_int_cmd=[9]
dust_sensor_read_int_cmd=[6]

def dustSensorRead():
	"""
	By default, the sample rate is set to 1 at every 30 seconds and this
	function was written only for that interval.

	If you wish to use a different
	interval, then use dustSensorReadMore function. To set a
	different interval, use setDustSensrInterval function.
	"""
	write_i2c_block(address, dus_sensor_read_cmd + [unused, unused, unused])
	time.sleep(.2)
	data_back= bus.read_i2c_block_data(address, 1)[0:6]
	if data_back[0]!=255:
		lowpulseoccupancy=(data_back[3] * 65536 + data_back[2] * 256 + data_back[1])
		return [data_back[0], lowpulseoccupancy]
	else:
		return [-1,-1]

def setDustSensorInterval(interval_ms):
	byte1 = interval_ms & 0xFF
	byte2 = interval_ms >> 8
	write_i2c_block(address, dust_sensor_int_cmd + [byte1, byte2] + [unused])
	time.sleep(.2)

def getDustSensorInterval():
	write_i2c_block(address, dust_sensor_read_int_cmd + 3 * [unused])
	time.sleep(.2)
	data_back = bus.read_i2c_block_data(address, 1)[0:2]

	if -1 in data_back: return -1

	interval = data_back[0] + data_back[1] * 256
	return interval

def dustSensorReadMore(blocking = True):
	sampletime_ms = getDustSensorInterval()
	found, lpo = dustSensorRead()
	while found in [0, -1] and blocking is True:
		time.sleep(1.0)
		found, lpo = dustSensorRead()

	if found in [0, -1] and blocking is False:
		return (-1, -1, -1)

	percentage = lpo * 100.0 / sampletime_ms
	concetration = 1.1 * percentage ** 3 - 3.8 * percentage ** 2 + 520 * percentage + 0.62

	return (lpo, percentage, concetration)

And the firmware you need to burn in order for all these 3 new additional functions to work is uploaded here: firmware.cpp (33.8 KB)

One thing I have noticed is that if you pool the information too fast, the found variable (or data_back[0]) is no longer set to 1 when a new value is calculated. I think this happens because the current firmware doesn’t separate the ISR and the main loop the right way and thus, at some point dust_latest variable gets set to 0 before it’s sent to master (the Raspberry Pi).

Let me know if you encounter any issues. I’m waiting for some feedback from you.

Thank you!

Thank you very much @RobertLucian. Currently working on it and will let you know the results.

@RobertLucian, how do I edit the grovepi module itself? I have updated thegrovepi.py file with your additional code, but I realise this isn’t the actual python module. The python module is located /usr/local/lib/python2.7/dist-packages/grovepi-1.0.0-py2.7.egg/grovepi.py, which I cannot see a way to edit.

@RobertLucian I found the setup.py script, which I ran. I now get global name 'dust_sensor_read_int_cmd' is not defined error. Do I need to add this variable to the code?

Hi @edrjeffery,

You need to run the setup.py like in the following:

sudo python setup.py install --force # for python 2
sudo python3 setup.py install --force # for python 3

These 2 commands will install you the grovepi package for both versions of Python. You might also be interested in using the reload built-in function to reload a module when running a python instance from the command line.

As for those variable(s) that you’re getting errors for, I have edited my previous answer with the variables you need to add.

Thank you!

Hi @RobertLucian,

I added in the missing variables, but in python form rather than C form. Or was that not correct? I then ran both the commands listed above and I don’t get an errors.

However, there still seems to be an issue with the timing. This is my test code:

grovepi.dust_sensor_en()
sample_ms = grovepi.getDustSensorInterval()
print("Sample ms:", sample_ms)
grovepi.setDustSensorInterval(10000)
sample_ms = grovepi.getDustSensorInterval()
print("Sample ms:", sample_ms)
while True:
    try:
        lpo, percentage, concentration = grovepi.dustSensorReadMore()
        print(lpo, percentage, concentration)

    except IOError:
        print("Error")

My sample_ms print statements both print out 65280 (?). I timed the interval between the measurements returned and it is still about 30 seconds. Am I doing something wrong?

Thanks

Hi @edrjeffery,

Must have been the tiredness I had. I gave you the “constants” for the firmware and not those of the library in Python. I edited the answer again.

Let me know if this time it works for you.

Thank you!

Hi @RobertLucian,

Sorry, I should have been clearer. I had made those changes already as I based the syntax on the other constant definitions in the file. So even with those changes, the actual interval between measurements is around 30 seconds and the getDustSensorInterval still returns 65280.

Hi @RobertLucian,

Do you have any other ideas for what to try now?

Thanks