DexterInd / GrovePi

GrovePi is an open source platform for connecting Grove Sensors to the Raspberry Pi.
https://www.dexterindustries.com/grovepi/
Other
490 stars 486 forks source link

Speed up firmware by supporting polling #168

Closed joemarshall closed 6 years ago

joemarshall commented 8 years ago

Quite a few sensors take a long time to return. This means if you have (say) two ultrasonic sensors, 3 analog sensors, you're limited to about 1hz sampling.

The way grovepi currently handles this is by just sleeping for ages (e.g .2 of a second for ultrasonic read, .1 for analogRead).We can make those sleeps tighter (.06 is the minimum for full range on ultrasound I think, and analogRead can live with .02), but what would be ideal would be if it was possible to tell if a value had been read yet by the sensor, so you didn't have to wait.

Thanks to a quirk in the current firmware, this would be pretty trivial to implement, because for most slow commands, it returns a buffer b, which always has byte zero ignored, which could instead be used as a status byte.

So,in receivedata, where it sets index=0, it could set b[0]==0xff. Then in the analogread handling, once it has read the value into the buffer, it could zero b[0].

void receiveData(int byteCount)
{
    while(Wire.available())
    {
      if(Wire.available()==4)
      {
        flag=0; 
        index=0;
        run_once=1;
        b[0]=0xff; // mark that we haven't read the value yet 
      }
        cmd[index++] = Wire.read();
    }
}

else if(cmd[0]==3)
{
  aRead=analogRead(cmd[1]);
  b[1]=aRead/256;
  b[2]=aRead%256;
  b[0]=0; //value is ready for consumption 
}

This would allow the python side code to be much faster, like:

def fasterAnalogRead(pin):
    bus.write_i2c_block_data(address, 1, aRead_cmd + [pin, unused, unused])
    while True:
        time.sleep(.01)
        bus.read_byte(address)
        number = bus.read_i2c_block_data(address, 1)
        if number[0]==0:
            return number[1] * 256 + number[2]

It's not a massive difference with analogread, but ultrasound read speed really slows down things for our students. Ultrasonic read time is dependent on distance, slower for longer distances. We could also take advantage of the knowledge that if we're only interested in say up to 50cm, we can just put a very short delay in, and set to maximum if no value is returned in time..

def fasterUltrasonicRead(pin):
    write_i2c_block(address, uRead_cmd + [pin, unused, unused])
    while True:
            time.sleep(.01)
        read_i2c_byte(address)
        number = read_i2c_block(address)
            if number[0]==0:
                return (number[1] * 256 + number[2])

def shortUltrasonicRead(pin):
    write_i2c_block(address, uRead_cmd + [pin, unused, unused])
    time.sleep(.05)
    read_i2c_byte(address)
    number = read_i2c_block(address)
        if number[0]==0xff:
           return 50
    else:
            return (number[1] * 256 + number[2])       
karan259 commented 8 years ago

This really looks like a great use of that byte. We'll try it out and add it in the next firmware version.

thebigbuilder commented 8 years ago

Nice! Tnx.

Sent from my iPhone

On Nov 17, 2015, at 7:01 AM, joemarshall notifications@github.com wrote:

Quite a few sensors take a long time to return. This means if you have (say) two ultrasonic sensors, 3 analog sensors, you're limited to about 1hz sampling.

The way grovepi currently handles this is by just sleeping for ages (e.g .2 of a second for ultrasonic read, .1 for analogRead).We can make those sleeps tighter (.06 is the minimum for full range on ultrasound I think, and analogRead can live with .02), but what would be ideal would be if it was possible to tell if a value had been read yet by the sensor, so you didn't have to wait.

Thanks to a quirk in the current firmware, this would be pretty trivial to implement, because for most slow commands, it returns a buffer b, which always has byte zero ignored, which could instead be used as a status byte.

So,in receivedata, where it sets index=0, it could set b[0]==0xff. Then in the analogread handling, once it has read the value into the buffer, it could zero b[0].

void receiveData(int byteCount) { while(Wire.available()) { if(Wire.available()==4) { flag=0; index=0; run_once=1; b[0]=0xff; // mark that we haven't read the value yet } cmd[index++] = Wire.read(); } }

else if(cmd[0]==3) { aRead=analogRead(cmd[1]); b[1]=aRead/256; b[2]=aRead%256; b[0]=0; //value is ready for consumption } This would allow the python side code to be much faster, like:

def fasterAnalogRead(pin): bus.write_i2c_block_data(address, 1, aRead_cmd + [pin, unused, unused]) while True: time.sleep(.01) bus.read_byte(address) number = bus.read_i2c_block_data(address, 1) if number[0]==0: return number[1] * 256 + number[2] It's not a massive difference with analogread, but ultrasound read speed really slows down things for our students. Ultrasonic read time is dependent on distance, slower for longer distances. We could also take advantage of the knowledge that if we're only interested in say up to 50cm, we can just put a very short delay in, and set to maximum if no value is returned in time..

def fasterUltrasonicRead(pin): write_i2c_block(address, uRead_cmd + [pin, unused, unused]) while True: time.sleep(.01) read_i2c_byte(address) number = read_i2c_block(address) if number[0]==0: return (number[1] * 256 + number[2])

def shortUltrasonicRead(pin): write_i2c_block(address, uRead_cmd + [pin, unused, unused]) time.sleep(.05) read_i2c_byte(address) number = read_i2c_block(address) if number[0]==0xff: return 50 else: return (number[1] * 256 + number[2])
— Reply to this email directly or view it on GitHub.