yeti logo icon
Close Icon
contact us
Yeti postage stamp
We'll reply within 24 hours.
Thank you! Your message has been received!
A yeti hand giving a thumb's up
Oops! Something went wrong while submitting the form.

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

April 7, 2017

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.



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

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


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!


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 variablesint 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


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 AdapterBus 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 -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.


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 asyncioimport serial_asyncioimport websockets# Try this port. If I get nothing printing out, try '/dev/ttyAMA0'SERIAL_PORT = '/dev/ttyACM0' BAUDRATE = 9600class 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()

EDIT: I've added my commands for doing installing all the things I said we'd need below!

pip install websockets pyserial-asyncio

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!

You Might also like...

colorful swirlsAn Introduction to Neural Networks

Join James McNamara in this insightful talk as he navigates the intricate world of neural networks, deep learning, and artificial intelligence. From the evolution of architectures like CNNs and RNNs to groundbreaking techniques like word embeddings and transformers, discover the transformative impact of AI in image recognition, natural language processing, and even coding assistance.

A keyboardThe Symbolicon: My Journey to an Ineffective 10-key Keyboard

Join developer Jonny in exploring the Symbolicon, a unique 10-key custom keyboard inspired by the Braille alphabet. Delve into the conceptualization, ideas, and the hands-on process of building this unique keyboard!

Cross-Domain Product Analytics with PostHog

Insightful product analytics can provide a treasure trove of valuable user information. Unfortunately, there are also a large number of roadblocks to obtaining accurate user data. In this article we explore PostHog and how it can help you improve your cross-domain user analytics.

Browse all Blog Articles

Ready for your new product adventure?

Let's Get Started