california app design company

Setting Up a Light Sensor with an Arduino and a Raspberry Pi

April 07, 2017

Motivation:

About three to six times a day, the sun decides to make life in our trendy, skylit San Francisco office ever so slightly painful for at least one of my colleagues, forcing them to get up and use a long metal rod to slide the fashionable skylight drapes shut. In my never-ending quest to make something in the IoT (Internet of Things) sphere that would remedy this perennial obstacle to productivity, I began with the data collection side of things. Sensors. I bought a fairly cheap Arduino-for-babies kit, grabbed the closest Raspberry Pi I could reach, and got started.

Materials:

Goal

The goal for this blog post is to showcase the following:

  • Produce an Arduino that writes to Serial the current ambient light level.
  • Connect an Arduino to a Raspberry Pi via USB port
  • Read the light levels on the Raspberry Pi that the Arduino is writing

High Level Approach

simple diagram of light sensor client setup

The idea is that I'd just use an Arduino and a cheap photocell resistor/light sensor to handle collecting the signals to send via USB to a Raspberry Pi, which will be responsible for taking those signals and jettisoning them out into the internet in some way.

Setting up the Arduino + Sensor

Hardware

Setting up the hardware side of things was relatively straightforward with the Arduino Uno. Here is a page that describes the setup more succinctly than I ever could. They use a photocell, which is pretty similar in every way to the photo transistor light sensor I'm using, except potentially cheaper!

WYSIWYG Hardware

Essentially, you have a 5V circuit with the light sensor and 10kΩ resistor arranged serially. There is an alternate line branching off after these components that is piped to Analog pin 0. You can change the range of current values piped to this pin by changing the 10kΩ resistor with other resistors at different resistances. Higher resistor values mean more current gets routed to the pin instead, while lower resistor values mean more charge goes to ground. This project uses the 10kΩ resistor so the current registered by pin 0 ranges between 0-1000. I tested out a 1kΩ resistor for funsies and saw the range plummet to 0-100. Yay science!

Software

The development lifecycle of an Arduino is such that you simply install the Arduino IDE or use the new online code Editor, type in your code, plug the Arduino into your computer via USB, and then press a button on the IDE/Editor that flashes your code onto the Arduino. In the words of the Barefoot Contessa, how easy is that?

Programming on the Arduino is a joy in that the boilerplate has been kept to a minimum. You have the setup function, which handles one-time setup of things. And you have the more interesting loop function, which as you might imagine runs the code inside of it on an infinite loop. Assuming that I've hooked everything up correctly, this is the code that I'd flash to the Arduino:

// Initialize the variables
int photoRPin = 0;
int lightLevel;

void setup() {
  // One-time setup. 9600 is a value that is called the "baud rate"
  Serial.begin(9600);
}

void loop() {
  // Collect a data point
  lightLevel=analogRead(photoRPin);

  // Send the adjusted Light level result to Serial port
  Serial.println(lightLevel);

  // Slow down the transmission to keep the call-stack down. 500ms!
  delay(500);
}

If you're using the IDE, once the code is flashed to the Arduino, you can check the serial port for the output by navigating through Tools > Serial Monitor. If you see a beautiful stream of numbers, you're where you need to be on the Arduino front!

Setting up the Pi

Hardware

What's great about the hardware setup is the only hardware that needs to be plugged into the Raspberry Pi is the USB attached to the Arduino. To verify that the Arduino is plugged into the Pi sufficiently, run lsusb. You should see something like this:

Bus 001 Device 004: ID 2341:0043 Arduino SA Uno R3 (CDC ACM)
Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp. SMSC9512/9514 Fast Ethernet Adapter
Bus 001 Device 002: ID 0424:9514 Standard Microsystems Corp.
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

You'll want to know what Serial Port path to point the Raspberry Pi to read from, since that is where the Arduino will be writing data from the light sensor. With the Arduino plugged in, pip install pyserial and use the list_ports tool by entering python -m serial.tools.list_ports -v in the terminal. You'll get something that looks like the snippet below:

/dev/ttyACM0
    desc: ttyACM0
    hwid: USB VID:PID=2341:0043 SER=854393031333514140C1 LOCATION=1-1.2

/dev/ttyAMA0
    desc: ttyAMA0
    hwid: 3f201000.uart

In this case, where there are multiple ports and you don't know which one to use, I'd probably just keep track of both and use one or the other, depending on what ends up working.

Software

The software on the Raspberry Pi is only slightly more complicated. I'm using Python 3.5 on this Pi, and I want to be able to read from the serial port in an asynchronous way, so that I greedily gather as much data as possible without the read process itself blocking anything. To accomplish that I've pip-installed both pyserial and pyserial-asyncio and used the pyserial-asyncio short introduction as a starting point for the python code. I also pip-installed websockets, and used the client to asynchronously send incoming data to some websocket server, but that bit is commented out since this post isn't including the server-side. Alternatively, one could send this data via HTTP/HTTPS to a web server the old-fashioned way with the aiohttp library!

import asyncio
import serial_asyncio
import websockets

SERIAL_PORT = '/dev/ttyACM0' # Try this port. If I get nothing printing out, try '/dev/ttyAMA0'
BAUDRATE = 9600

class SerialConnection(asyncio.Protocol):

    data_buffer = ''

    # Example of co-routine that sends data to a server
    async def send_data(self, data):
        async with websockets.connect(os.getenv('PUBLISH_SOCKET_LINK')) as ws:
            dataline = data
            if dataline != '':
                await ws.send(dataline)

    # Built-in protocol method provided by pyserial-asyncio
    def data_received(self, data):
        # Gather the data in a chunk until it's ready to send
        self.data_buffer += data.decode('utf-8')
if '\r\n' in self.data_buffer: data_to_send = self.data_buffer.strip('\r\n')
print(data_to_send)

# Reset the data_buffer! self.data_buffer = '' ''' At this point, you can make an HTTP Request with this data or use websockets! ''' # asyncio.get_event_loop().create_task(self.send_data(data_to_send)) def record(path = None): # Boiler-plate for getting the pyserial-asyncio goodness to work loop = asyncio.get_event_loop() serial_connection = serial_asyncio.create_serial_connection(loop, SerialConnection, SERIAL_PORT, baudrate=BAUDRATE) try: loop.run_until_complete(serial_connection) loop.run_forever() finally: loop.close() if __name__ == '__main__': record()

Next Steps

So if everything is successful, you should have a Python program running on the Pi that 1.) reads ambient light levels coming from an Arduino and 2.) prints those light levels. The next steps will involve setting up a server of some sort to handle the signals this system is putting out, as well as setting up other clients to read from that server. Happy building!

is a Software Engineer at Yeti. When he isn't baking bread, or messing with Raspberry Pis, he's dreaming about a future in which humanity is governed by benevolent robotic dictators. He went to school for pre-med and linguistics at the University of Florida and is hoping one day to make his parents proud by working on something tangentially related to either subject.

Enter your email to subscribe to our newsletter and get even more delicious content delivered straight to your inbox. No spam, we pinky swear.

Newsletter
blog comments powered by Disqus
Setting Up a Light Sensor with an Arduino and a Raspberry Pi https://s3-us-west-1.amazonaws.com/yeti-site-media/uploads/blog/.thumbnails/product-service.jpg/product-service-360x0.jpg
Yeti (415) 766-4198 https://s3-us-west-1.amazonaws.com/yeti-site-static/img/yeti-head-blue.png