Setting up ADC for line following? Help needed!


I quite noob, but I hope you can bear with me and answer some of my (dumb?)questions :smile: 
I have a Particle Photon, and Poloulu QTR-8A (analog) line sensor (hooked up form A0 to A7). I am building a line folowing robot (it is built I just need to make the code work)
I would like help to configure right settings for the ADC to get best results. The smartsensor library seems awesome for that purpose, but there is so much variables to set...
Reading the STM32 documentation
I can find that the STM32 in Photon has 2 DACs and many different modes. How are the DACs configured in Viperize can the modes be changed? I know that Spark(Particle) core used (or still may use) Dual slow interleaved mode to increase the ADC impeadance, but it totaly messed up reading of some sensors.

I would like to sample every sensor from 500 to 1000hz (8 sensors x 1000hz = 8000hz) is it possible or am I asking too much from the ADCs?


  • GiacomoGiacomo Member
    edited November 2015
    Hello potato and welcome,

    Viper supports multichannel sequential read implemented in hardware for the STM32 families. This means that you just need to specify the list of pins you want to read from and the Viper adc drivers tells the adc unit to read them one after the other as fast as possible (if I remember correctly it is around 1MHz, i.e. 1 million samples per second). 

    You can try something like this:

    import adc 
    pins = [A0,A1,A2,A3,A4,A5,A6,A7]
    while True:
    #do something with "values" here

    In the above code, values should be a shortarray (like a bytearray but with values going from 0 to 65535) containing the raw readings of your pins in order. Please note however, that while "acquiring" data is fast (1 MHz), your bottleneck could be the "processing" of the acquired data to determine if you are on a line. For that, I guess you should keep in memory some of the old readings and compare them with the new ones...maybe taking an average...etc...

    I will also ping Lorenzo, the author of the sensor library, he might help you better on that.

    Keep us posted on you progress :)

    Giacomo Baldi
    Zerynth Head of Software Development
  • Thanks

    I got it working. I ported the QTR-8a arduino library over to photon. Seems to be working fine. I get around 11KHz loop speed (includes reading 8 sensors, calculating error and then there is PID and sending signal to two rc ESC's) 

  • Hello potato, for what concerns sampling simultaneously from 8 sensors, you could take advantage of the smartsensors.sensorPool module this way:

    • declare 8 analog smartsensors, one for each pin
    • declare 1 sensorPool containing the declared smartsensors
    • startSampling specifying different sampling times for each sensor in the pool
    example code:
    pins = [A0,A1,...,A7]
    analog_sensors = []
    for pin in pins:
    pool = sensorPool.SensorPool(analog_sensors)
    # sampling times are in milliseconds

    you can define specific actions for each sensor through my_sensor.doEverySample(...) (before starting to sample) to handle acquired data.

    Take a look at these examples: :wink: 

    And please let me know if I didn't get the point :smile: 

    Lorenzo Rizzello
    Zerynth Support Team

  • potatopotato Member
    edited November 2015
    Hi Lorenzo and thanks for answering 
    Yes I think you got the point :smile: 

    But don't know if I understand the difference between regular and sensorPool. Here are few more specific questions:
    I think I understand that (in the code that Giacomo gave me) does sample every sensor independently. reads A1, saves the value, reads A2, saves the value, so on, till A7 

    But sensorPool does it sample all pins at the same time? Like all pins form A1 to A7 at once? For example, if I would set the sampling time to 1(ms) for every pin, then it would read all sensors in 1ms and give me the value(s)?

    Also setting sampling time to 1ms would mean it could do only 1000 samples per second (then it is slower than
    shouldn't the sampling time be in μs (microseconds)? it is supported by the fact that Giacomo said that adc can do 1 million samples per second!?!
    what is the smallest samping time I can set for sensorPool?
    what is the sampling time for

    Here is how my code looks right now
    #Some other stuff up there
    import adc
    sensors = [A0, A1, A2, A3, A4, A5, A6, A7] onLine = False lastValue = 0 while True: sensorValues = sum = 0 avg = 0 onLine = False for x in range(0, 8): light = sensorValues[x] lightCal = ((light) / 4095) * 1000 if(lightCal > 170): # Noise threshold onLine = True if(lightCal > 50): # We see black lines sum += lightCal avg += (lightCal) * (x * 1000) if(onLine): lastValue = avg/sum else: if(lastValue > (len(sensors) * 500)): lastValue = len(sensors) * 1000 else: lastValue = 0
    #PID starts here....
    error = lastValue - 3500 # error = last sensor readings - 3500 (center point)
  • Hi potato, as you correctly said samples sequentially from all the pins in the list but at the same frequency. sensorPool aims to provide an higher level abstraction to easily manage sampling from different pins (and different sensors) at different frequencies. The process of sampling is built on top of, so it does not read data simultaneously, and of timers, whose minimum interval is 1ms (from that the limit of 1kHz).
    As you can easily understand sensorPool has been written for quite slow sampling processes, where computation time is considered much less than period. So if you want to sample from 2 sensors, one at 1kHz and one of 500Hz, what sensorPool does is simply to cycle at 1kHz and read from both at the first cycle and from only one at the second one: of course this is not good for strict real time applications, but is ok if you have to read a temperature in a room and a light level from a photoresistor.
    I hope to add in the future more precise and faster behaviour through the use of hwtimers.
    That said maybe in your case at the moment could be better a raw implementation like the one you posted than a easier to manage, but slower, like a sensorPool one. :smile: 
    Lorenzo Rizzello
    Zerynth Support Team

  • potatopotato Member
    edited May 2016

    I updated from 0.3.0 to 1.0.2 (latest at this time)
    Then I did some optimizations to my old code, and fixed some errors that were introduced by new naming differences.
    Under the adc programming guide I can now see init(drvnamesamples_per_second=800000) It says that the default is a sampling frequency is 0.8 MHz. Can this be increased for photon? I read somewhere that stm32 adc could sample up to 2mhz with relative ease... 
    init (drvname, 2000000) could this work? 
    What is the drvname for photon adc? is it just "adc", so init (adc, 2000000)? 

    Edit: I must be doing something wrong, because I am only getting syntax errors :smile: 


  • potatopotato Member
    The STM32f2xx datasheet states that 2 Msps is possible using the single (normal) adc config that is used by Zerynth. 
    Can somebody please help me with this?

  • GiacomoGiacomo Member

    yes, on the stm32f2 2Msps can be possible; the approach you outlined is right.
    However I first need to enable you to call an adc.done() function which at the moment is not exposed.

    Give me a couple of days and I'll let you try the 2Msps  :)
    Giacomo Baldi
    Zerynth Head of Software Development
  • potatopotato Member
    edited May 2016
    Thanks for taking a look into this! There's no hurry, but have you made any progress?

    Edit: I can see you released an update on 27th with the changes to adc. 
    Can you give me an example how to use this new feature?
  • potatopotato Member
    Hi @Giacomo ;

    So the new command adc.done(drvname) it should unload the default adc driver, but can you tell me what the default drvname for Photon ADC is? As this command is basically useless unless I know what the default drvname. 
    I have tried guessing the name, but no luck yet :smile: :disappointed: 

    after adc.done() I should reload the driver like this: adc.init(drvname2000000), to set it to 2Msps?


  • Ooops, my bad  :s

    The drvname is ADC0

    ...updating docs...
    Giacomo Baldi
    Zerynth Head of Software Development
Sign In or Register to comment.

ZERYNTH Community

@ 2016, all rights reserved.