Skip to content
Snippets Groups Projects
Commit 7dedc8b9 authored by Teo Serra's avatar Teo Serra
Browse files

week14 add content

parent e7f74cb0
Branches
No related tags found
No related merge requests found
Pipeline #454251 passed
+++
title = 'Week 14 : Networking and Communications'
fa = "network-wired"
faVariant = "solid"
toc = true
weight = 14
+++
### Summary
This week, I took inspiration from previous weeks and continued my exploration
of I2C by trying to communicate with several slaves on the same communication
line. The end result is a communication with one input, an infrared temperature
sensor, and one output, an OLED screen.
<div class="row">
<div class="col-md-6 align-self-center" markdown="1" >
<img src = "./img/week14/Result.jpg" alt = "WireNoPullup"
class="mx-auto d-block">
</div>
<div class="col-md-6 align-self-center" markdown="1" >
<center>
<video controls muted loop>
<source src="./vid/week14/Result_ChangeofTemp.mp4" type=video/mp4>
</video>
</center>
</div>
</div>
***
### Assignments
#### Group Assignment
* Send a message between two projects.
#### Individual Assignments
* Design, build, and connect wired or wireless node(s) with network
or bus addresses and local input &/or outputdevice(s).
***
## Communication Between Two Projects
This part was done in group and is accessible on the [group page](https://fabacademy.org/2024/labs/ulb/group-assignments/week14/).
## Wired Network With Input & Output
### I2C Network
When connecting multiple slave devices on an I2C bus, adopting a networked
approach can streamline communication effectively. Each slave device needs a
unique address on the bus to ensure individual identification and communication
by the master device. In scenarios where multiple master devices share the same
I2C bus, coordination is crucial to avoid conflicts. Bus arbitration mechanisms
facilitate this coordination, allowing only one master to communicate at a time.
Organizing slave devices into a network topology enables the master to interact
with them individually, maintaining their unique addresses. In setups requiring
multiple master devices, a multi-master configuration allows independent access
to the bus while ensuring coordination to prevent collisions. An illustration
of this communication is shown here.
<img src = "./img/week14/I2C_bus.jpg" alt = "i2cbus"
class="mx-auto d-block" width=80%>
Clock synchronization is essential to maintain proper timing during data
transfer, with clock stretching accommodating any delays by slave devices.
Including error detection and handling mechanisms enhances reliability,
addressing issues like bus collisions or data corruption.
Thorough testing and validation of the networked I2C communication setup
confirm its functionality and address any issues. This comprehensive approach
ensures efficient data exchange and coordination among interconnected devices
in embedded systems or IoT projects.
### Pull-Up Resistor
Integrating pull-up resistors is essential for reliable communication in setups
with multiple slave devices on an I2C bus. These resistors ensure that the bus lines
return to a logic high state when not actively driven by a device, preventing bus
contention and ensuring effective communication. The value of the pull-up resistors
depends on factors like bus capacitance and desired signal rise time, typically
ranging from 2.2 kΩ to 10 kΩ. By incorporating pull-up resistors, you enhance the
overall reliability and performance of your embedded systems or IoT projects.
### Master & Slaves
The elements that will be on my network are :
* Master #1: A Raspberry Pi Pico with MicroPython firmware.
* Slave #1: SSD1306 OLED display.
* Slave #2: A MLX90614 temperature sensor.
<div class="row">
<div class="col-md-4 align-self-center" markdown="1" >
<img src = "./img/week14/RaspPico.jpg" alt = "RP2Pico"
class="mx-auto d-block">
<center>
<i>Master #1</i>
</center>
</div>
<div class="col-md-4 align-self-center" markdown="1" >
<img src = "./img/week14/Oled_ssd1306.jpg" alt = "SSD1306"
class="mx-auto d-block" >
<center>
<i>Slave #1</i>
</center>
</div>
<div class="col-md-4 align-self-center" markdown="1" >
<img src = "./img/week14/Sensor_mlx90614.jpg" alt = "MLX90614"
class="mx-auto d-block" >
<center>
<i>Slave #2</i>
</center>
</div>
</div>
### Wiring
As described above, wiring is fairly straightforward, since all elements
must be connected in parallel to the SDA and SCL lines. Pull-up resistors
are also required.
It should be noted that I forgot to add the pull-up resistors at the start
of my tests, and then fixed the problem, although this didn't solve the problems
I encountered and which will be presented later.
The wiring is shown here.
<div class="row">
<div class="col-md-6 align-self-center" markdown="1" >
<img src = "./img/week14/Wiring_WithoutPullup.jpg" alt = "WireNoPullup"
class="mx-auto d-block">
<center>
<i>Wiring without Pull-up Resistor</i>
</center>
</div>
<div class="col-md-6 align-self-center" markdown="1" >
<img src = "./img/week14/Wiring_WithPullup.jpg" alt = "WirePullup"
class="mx-auto d-block" >
<center>
<i>Wiring with Pull-up Resistor</i>
</center>
</div>
</div>
I used the Pins GP8 and GP9 of the raspberry Pi Pico for i2c communication.
A diagram of the raspberry's pins can be seen below.
<img src = "./img/week14/RaspPico_Scheme.jpg" alt = "RaspPico_Scheme"
class="mx-auto d-block" width=80%>
### Network Testing
To test the network, after plugging it in, I tested the two escalves separately,
and then tried to make them work together.
#### Testing the OLED
To test the screen, I took an old code from week 12, which displayed the
analog measurement of a temperature sensor (measured this time on a different pin).
The code is available on the [Week 12 page](https://fabacademy.org/2024/labs/ulb/students/teo-serra/weeklyassignments/week12/).
Don't forget to add the ssd1306 library to the raspberry pico's internal memory.
It worked perfectly!
#### Testing the Temperature Sensor
To test the temperature sensor, I used the test code in the [mlx90614 library](https://github.com/mcauser/micropython-mlx90614).
I also need to add the mlx90614 library to the raspberry pico's internal memory.
```python
import mlx90614
from machine import I2C, Pin
i2c = I2C(scl=Pin(5), sda=Pin(4))
sensor = mlx90614.MLX90614(i2c)
print(sensor.read_ambient_temp())
print(sensor.read_object_temp())
if sensor.dual_zone:
print(sensor.object2_temp)
```
##### Troubleshooting the Sensor
The sensor does not initialize correctly and returns an error when creating the
"sensor" object. After some research, it seems that this is due to the Raspberry
Pico's default communication frequency, which is too high.
So I add the freq argument to the I2C and set it to 100 kHz as indicated in the
[datasheet](https://www.melexis.com/-/media/files/documents/datasheets/mlx90614-datasheet-melexis.pdf)
(maximum frequency).
```python
import mlx90614
from machine import I2C, Pin
i2c = I2C(scl=Pin(5), sda=Pin(4), freq=100_000)
sensor = mlx90614.MLX90614(i2c)
print(sensor.read_ambient_temp())
print(sensor.read_object_temp())
if sensor.dual_zone:
print(sensor.object2_temp)
```
Now it's working!
#### Testing the Two Slaves Together
To test both I need to write new code. In theory, the idea is quite similar
to what's been done before, i.e. create an i2c object with the machine library
and assign it to the two objects, oled and sensor, created with the ssd1306
and mlx90614 libraries.
A bit of formatting and I get this code, which, spoiler, won't work...
```python
from machine import Pin, I2C
import ssd1306
import time
import mlx90614
# Initialize I2C
i2c = SoftI2C(scl=Pin(9), sda=Pin(8), freq=100_000)
# Initialize MLX90614
mlx = mlx90614.MLX90614(i2c)
# OLED Display setup
oled_width = 128
oled_height = 64
oled = ssd1306.SSD1306_I2C(oled_width, oled_height, i2c)
# Main loop
while True:
ambient_temp = mlx.read_ambient_temp() # Ambient temperature
object_temp = mlx.read_object_temp() # Temperature of the object in front of the sensor
oled.fill(0) # Clear the display
oled.text('Ambient Temp:', 0, 0)
oled.text(f'{ambient_temp:.2f} C', 0, 10)
oled.text('Object Temp:', 0, 20)
oled.text(f'{object_temp:.2f} C', 0, 30)
oled.show()
time.sleep(1)
```
#### Troubleshooting the Network
This time, it's the ssd1306 library that's having trouble, generating a
"time_out" error when initializing the oled object. After a bit of research,
I realize that I forgot to add the pull-up resistors, what a shame. As a reminder,
they prevent floating pins on my SDA and SCL channels. However, this didn't solve
the problem...
However, the two elements appear to be clearly identified and distinct when
"i2c.scan()" is run. In fact, this function returns the addresses of the two
elements on the i2c line: [60, 90], in decimal.
So for me it must be a bug in the library when initializing the oled, it
must be trying to write something that doesn't exist.
I decided to work around the problem, as suggested on a forum, by
using "Software I2C".
#### Software I2C
Software I2C, also known as bit-banging I2C, is a flexible method of implementing
the I2C protocol using software routines instead of dedicated hardware. It directly
controls the SDA and SCL lines of an I2C bus through GPIO pins, offering versatility
across various microcontrollers and hardware platforms. While it provides flexibility,
it may not match the performance of hardware-based solutions, and developers should
consider factors like timing accuracy and resource utilization when choosing to use
software I2C in their projects.
### Final Solution & Result
So I solved my problem using the SoftI2C library from machine, the code is
shown below.
Pull-up resistors are not required.
```python
from machine import Pin, SoftI2C
import ssd1306
import time
import mlx90614
# Initialize I2C
i2c = SoftI2C(scl=Pin(9), sda=Pin(8), freq=100_000)
# Initialize MLX90614
mlx = mlx90614.MLX90614(i2c)
# OLED Display setup
oled_width = 128
oled_height = 64
oled = ssd1306.SSD1306_I2C(oled_width, oled_height, i2c)
# Main loop
while True:
ambient_temp = mlx.read_ambient_temp() # Ambient temperature
object_temp = mlx.read_object_temp() # Temperature of the object in front of the sensor
oled.fill(0) # Clear the display
oled.text('Ambient Temp:', 0, 0)
oled.text(f'{ambient_temp:.2f} C', 0, 10)
oled.text('Object Temp:', 0, 20)
oled.text(f'{object_temp:.2f} C', 0, 30)
oled.show()
time.sleep(1)
```
The operation of the oled and the sensor on the same i2c line can be seen below.
<div class="row">
<div class="col-md-6 align-self-center" markdown="1" >
<img src = "./img/week14/Result.jpg" alt = "WireNoPullup"
class="mx-auto d-block">
</div>
<div class="col-md-6 align-self-center" markdown="1" >
<center>
<video controls muted loop>
<source src="./vid/week14/Result_ChangeofTemp.mp4" type=video/mp4>
</video>
</center>
</div>
</div>
static/img/week14/I2C_bus.jpg

25.5 KiB

static/img/week14/Oled_ssd1306.jpg

6.92 KiB

static/img/week14/RaspPico.jpg

50.4 KiB

static/img/week14/RaspPico_Scheme.jpg

48 KiB

static/img/week14/Result.jpg

400 KiB

static/img/week14/Sensor_mlx90614.jpg

39.7 KiB

static/img/week14/Wiring_WithPullup.jpg

343 KiB

static/img/week14/Wiring_WithoutPullup.jpg

281 KiB

static/img/week14/i2c_error.jpg

27.9 KiB

File added
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment