Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation
 
PerlMonks  

comment on

( [id://3333]=superdoc: print w/replies, xml ) Need Help??

In today's episode of Cool Uses for Perl, loosely inspired by this thread, I'm going to show how to set up an Arduino (Uno in this test case) with a pseudo-register that allows toggling one if its digital pins on and off, and another pseudo-register to read an analog pin that the digital pin is connected to, over I2C. Because it's digital to analog, the only possible values of the analog read will be 0 (off) or 1023 (full on, ie. 5v). This is an exceptionally basic example, but with some thought, one can imagine the possibilities (read/write EEPROM, set PWM etc etc).

We'll then use RPi::I2C to toggle the digital pin and read the analog pin over the I2C bus. Note I'm not using the encompassing RPi::WiringPi distribution in this case. The benefit to using that is to clean up Raspberry Pi's GPIO pins, which we aren't using any. In fact, any Linux device with I2C can be used for this example, I just so happen to be using one of my Pi 3 boards.

First, the simple Arduino sketch. All I2C devices require putting themselves on the "wire" with a unique address. I'm using 0x04, which is how the Pi will identify the Arduino on the I2C bus.

#include <Wire.h> // Arduino I2C address #define SLAVE_ADDR 0x04 // pseudo register addresses #define READ_A0 0x05 #define WRITE_D2 0x0A uint8_t reg = 0; void read_analog_pin (){ switch (reg){ case READ_A0: { __read_analog(A0); break; } } } void __read_analog (int pin){ int val = analogRead(pin); uint8_t buf[2]; // reverse endian so we're little endian going out buf[0] = (val >> 8) & 0xFF; buf[1] = val & 0xFF; Wire.write(buf, 2); } void write_digital_pin (int num_bytes){ reg = Wire.read(); // global register addr while(Wire.available()){ uint8_t state = Wire.read(); switch (reg){ case WRITE_D2: { digitalWrite(2, state); break; } } } } void setup(){ Wire.begin(SLAVE_ADDR); // set up the I2C callbacks Wire.onReceive(write_digital_pin); Wire.onRequest(read_analog_pin); // set up the pins pinMode(2, OUTPUT); pinMode(A0, INPUT); } void loop(){ delay(10000); }

Now, I'll show a simple script that loops 10 times, toggling the digital pin then displaying the value from the analog pin. Arudino's Wire library sends data a byte at a time, so we have to do some bit manipulation to turn the two bytes returned in the read_block() call back together into a single 16-bit integer. I wrote the merge() sub to take care of this job.

use warnings; use strict; use RPi::Const qw(:all); use RPi::I2C; use constant { ARDUINO_ADDR => 0x04, READ_REGISTER => 0x05, WRITE_REGISTER => 0x0A, }; my $device = RPi::I2C->new(ARDUINO_ADDR); for (0..9){ my (@bytes_read, $value); $device->write_byte(HIGH, WRITE_REGISTER); @bytes_read = $device->read_block(2, READ_REGISTER); $value = merge(@bytes_read); print "$value\n"; # 1023 $device->write_byte(LOW, WRITE_REGISTER); @bytes_read = $device->read_block(2, READ_REGISTER); $value = merge(@bytes_read); print "$value\n"; # 0 } sub merge { return ($_[0] << 8) & 0xFF00 | ($_[1] & 0xFF); }

Output:

1023 0 1023 0 1023 0 1023 0 1023 0 1023 0 1023 0 1023 0 1023 0 1023 0

update: I must acknowledge Slava Volkov (SVOLKOV) for the actual XS code. Most of the low-level hardware code I've been working on over the last year has been wrapping C/C++ libraries, a decent chunk of it has had me following datasheets to write my own, but in this case, I bit the whole XS file from Device::I2C and just presented a new Perl face to it so it fit in under the RPi::WiringPi umbrella. It just worked.


In reply to Reading/writing Arduino pins over I2C with Perl by stevieb

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post; it's "PerlMonks-approved HTML":



  • Are you posting in the right place? Check out Where do I post X? to know for sure.
  • Posts may use any of the Perl Monks Approved HTML tags. Currently these include the following:
    <code> <a> <b> <big> <blockquote> <br /> <dd> <dl> <dt> <em> <font> <h1> <h2> <h3> <h4> <h5> <h6> <hr /> <i> <li> <nbsp> <ol> <p> <small> <strike> <strong> <sub> <sup> <table> <td> <th> <tr> <tt> <u> <ul>
  • Snippets of code should be wrapped in <code> tags not <pre> tags. In fact, <pre> tags should generally be avoided. If they must be used, extreme care should be taken to ensure that their contents do not have long lines (<70 chars), in order to prevent horizontal scrolling (and possible janitor intervention).
  • Want more info? How to link or How to display code and escape characters are good places to start.
Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others musing on the Monastery: (5)
As of 2024-04-19 21:11 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found