[Tutor] making use of input

Steven D'Aprano steve at pearwood.info
Sun Nov 4 23:15:37 CET 2012


On 05/11/12 07:52, richard kappler wrote:
> Me again. :-)
>
> Today was a good day, I accomplished a lot, but I'm stumbling now. I
> assembled the sensor array, coded the Arduino board to read the sensors and
> send the data out to serial, played with formatting the data, learned
> enough about the python serial library to be a little dangerous.

[...]

> I'm pretty happy with the accomplishments of the day, but am at a bit of a
> loss as to how to proceed next. I hope that all made sense. I might be
> suffering a bit from "forest for trees" syndrome.
>
> Does anyone have any guidance?

Yeah... I'm not sure exactly what your question is, so it's a bit hard to
answer it. Try asking explicit questions.

I'm going to try guessing what questions you might be asking. If my guesses
are wrong, please be more explicit with your request.

You say:


> import serial
> arduino = serial.Serial('/dev/ttyACM0', 9600)
> while 1:
>      arduino.readline()
>
> /dev/ttyACM0 is the serial port being read and 9600 is the baud rate I set
> for now.
>
> This little bit of code gives me data in the form of 'Sensor1:120\r\n' one
> line for each sensor in a continuous loop.

My guess: "How do I go from this input to values in Python?"

If you want a single set of values which is continuously updated, try this:

# initialize a dict of keys and values
data = dict.fromkeys(  # values initially set to None
         'Sonar1 Sonar2 Sonar3 Sonar4 Temperature Humidity Dewpoint Light'.split()
         )

while True:
     line = arduino.readline().strip()
     key, value = line.split(':')
     value = int(value)
     data[key] = value
     process(data)  # whatever you like

This let's you do something each time a single key:value pair is received
from the Arduino. If the order that the keys are received matters, you
could use collections.OrderedDict instead of dict.


Alternatively, if you only need to process the data once you have captured
a full set of eight key:value pairs at a time, try something like this:


while True:
     d = {}
     for i in range(8):  # 8 different keys sent, one after another
         line = arduino.readline().strip()
         key, value = line.split(':')
         d[key] = int(value)
     if len(d) != 8:
         raise ValueError('expected 8 keys, but got %d' % len(d))
     process(d)


That's pretty minimal error checking. For production code, I'd want to
make sure that I got not just any old eight keys, but that I always got
the *right* eight keys, e.g. that the Arduino never skips a key, or
sends an unexpected one.


> I can also get it to python in the following form {Sensor1:120, Sensor2:89,
> etc...}
> Right. Like a dictionary. but a continuous read, in other words, python
> prints a new dictionary with the same keys but different data each time the
> Arduino loops which is what Arduino's do.


Hmmm... not really. You're transmitting this data over the serial port, so
it cannot be sending a dictionary. It must be sending bytes. Those bytes might
be formatted to look something like a dict, but they're still bytes.


> I tried the following:
>
> import serial
> arduino = serial.Serial('/dev/ttyACM0', 9600)
> sensor = arduino.readline()
>
> and then ran
>
> print sensor
>
> I got only part of it, eg something like {,Sonar4:120,temperature:71, etc
> on to the end of the sensor list} so python just captured whatever was
> scrolling through serial at the moment the readline was executed, not the
> full set of data.

Well of course :) You can't expect the serial module to capture data that's
already been and gone do you?

I assume that the Arduino is sending a newline after the closing brace },
and guess that you actually want help in processing data in this format.
If so, you can do this:


while True:
     line = arduino.readline().strip()
     if not (line.startswith('{') and line.endswith('}')):
         # partial record received, skip it
         continue
     d = makedict(line)
     process(d)


where makedict is a function you defined earlier:


def makedict(line):
     """Convert a line like '{ key:value, key:value, ... }' to a dict.
     Whitespace between text elements is optional.
     """
     line = line.strip()
     if not (line.startswith('{') and line.endswith('}')):
         raise ValueError('invalid record')
     line = line.lstrip('{').rstrip('}').strip()
     d = {}
     for item in line.split(','):
         item = item.strip()
         key, value = item.split(':')
         key = key.strip()
         value = value.strip()
         d[key] = int(value)
     return d



> What I NEED to do is have each parameter incoming to python through serial
> be assigned to a name and be available within the python program to use in
> other parts of the program. For example if temperature>70 print "It's
> bloody well getting warm in here!" type thing.

"NEED" is an awfully strong word. You *could* do that, but that's an unsafe
practice which becomes outright dangerous if you can't trust what data is
coming out of the Arduino. Much safer to use a dict of key:value pairs
as above, and instead of:

     if temperature > 70: ...

use

     if data['temperature'] > 70: ...


But if you insist, here's how to go from a pair of key:value to a global
variable:

     globals()[key] = value


But really, don't do that. And especially don't do this:

     eval("%s = %d" % (key, value))

because that goes from merely unsafe to downright suicidal. The Arduino
can now do *anything* with your computer. Anything you could do at the
command prompt, it can do. I hope you trust it, because you've just
given it your username and password :)



-- 
Steven


More information about the Tutor mailing list