Main differences between the programming languages : C, C++, Python and mycroPython.
Here's a clear side-by-side breakdown of the main differences between C, C++, Python, and MicroPython:
________________________________________
🔹 1) C
• Type: Procedural programming language
• Level: Low-level / middle-level
• Compiled or interpreted: Compiled
• Speed: Very fast
• Memory control: Manual (you manage memory yourself)
• Common uses: Operating systems, embedded systems, device drivers
Strengths
• Extremely fast and efficient
• Direct hardware access
• Small memory footprint
Weaknesses
• No built-in object-oriented features
• Manual memory management can cause bugs
Example:
#include <stdio.h>
int main() {
printf("Hello World");
return 0;
}
________________________________________
🔹 2) C++
• Type: Multi-paradigm (procedural + object-oriented + generic)
• Level: Middle-level
• Compiled or interpreted: Compiled
• Speed: Very fast
• Memory control: Manual (with more tools than C)
• Common uses: Game engines, high-performance apps, simulations
Strengths
• Object-Oriented Programming (OOP)
• Very powerful and flexible
• High performance
Weaknesses
• Complex syntax
• Steeper learning curve
Example:
#include <iostream>
using namespace std;
int main() {
cout << "Hello World" ; return 0; }
________________________________________
🔹 3) Python
• Type: High-level, interpreted
• Level: High-level
• Compiled or interpreted: Interpreted
• Speed: Slower than C/C++
• Memory control: Automatic (garbage collection)
• Common uses: AI, data science, web development, automation
Strengths
• Very easy to learn
• Clean and readable syntax
• Huge ecosystem of libraries
Weaknesses
• Slower execution speed
• Not ideal for very low-level hardware programming
Example:
print("Hello World")
________________________________________
🔹 4) MicroPython
• Type: Lightweight implementation of Python
• Level: High-level (but for microcontrollers)
• Compiled or interpreted: Interpreted
• Speed: Slower than C, optimized for small devices
• Memory control: Automatic (limited resources)
• Common uses: Microcontrollers (ESP32, Raspberry Pi Pico, IoT devices)
Strengths
• Easy embedded programming
• Similar syntax to Python
• Quick development for hardware projects
Weaknesses
• Limited libraries
• Less powerful than full Python
Example:
from machine import Pin
led = Pin(2, Pin.OUT)
led.on()
________________________________________
⚡ Quick Comparison Table
| Feature | C | C++ | Python | MicroPython |
|---|---|---|---|---|
| Level | Low | Mid | High | High |
| Speed | Very Fast | Very Fast | Moderate | Moderate |
| OOP | No | Yes | Yes | Limited |
| Memory | Manual | Manual | Automatic | Automatic |
| Hardware Access | Direct | Direct | Limited | Designed for microcontrollers |
| Best For | OS, embedded | Games, high-performance apps | AI, web, automation | IoT, microcontrollers |
________________________________________
🏁 Simple Summary
• C → Maximum control and speed
• C++ → C + Object-Oriented features
• Python → Easy, powerful, beginner-friendly
• MicroPython → Python for small hardware devices
If you tell me what you're planning to build (robot, website, game, AI, etc.), I can recommend which one fits you best.
Compare them in a sentence for each in a one or two lines.
• C - A fast, low-level compiled language that gives you direct control over memory and hardware, commonly used for operating systems and embedded systems.
• C++ - An extension of C that adds object-oriented and advanced features, used for high-performance applications like games and complex software.
• Python - A high-level, easy-to-read interpreted language designed for rapid development, widely used in AI, web development, and automation.
• MicroPython - A lightweight version of Python made for microcontrollers, allowing simple programming of hardware and IoT devices.
SEEED XIAO-RP2040 and I²C lcd screen 16x2.
You are looking to use a SEEED XIAO-RP2040 with a 16x2 I²C LCD. Let's break this down step by step: wiring, libraries, and example code.
________________________________________
1. Wiring the LCD to the SEEED XIAO-RP2040
The XIAO-RP2040 is small, but it has 3.3V logic, which is compatible with most I²C LCD modules. A typical I²C 16x2 LCD has 4 pins: GND, VCC, SDA, SCL.
| LCD Pin | XIAO-RP2040 Pin |
|---|---|
| GND | GND |
| VCC | 3,3V |
| SDA | D0 / GPIO0 |
| SCL | D1 / GPIO1 |
⚠️ Some LCD modules are 5V tolerant; if yours isn't, stick to 3.3V to avoid damage.
________________________________________
2. Libraries
For Arduino IDE (or PlatformIO), use the following libraries:
• Wire (built-in for I²C)
• LiquidCrystal_I2C (for I²C LCDs)
Install LiquidCrystal_I2C via the Arduino Library Manager.
________________________________________
3. Arduino Example Code
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
// Set the LCD I2C address. 0x27 is common, 0x3F also possible
LiquidCrystal_I2C lcd(0x27, 16, 2);
void setup() {
lcd.init(); // Initialize the LCD
lcd.backlight(); // Turn on the backlight
lcd.setCursor(0, 0); // Column 0, Row 0
lcd.print("Hello World");
lcd.setCursor(0, 1); // Column 0, Row 1
lcd.print("XIAO-RP2040 LCD");
}
void loop() {
// Nothing needed here
}
Tip: If the LCD doesn't display anything, check its I²C address using an I²C scanner.
________________________________________
4. I²C Scanner Code
#include >Wire.h>
void setup() {
Wire.begin();
Serial.begin(115200);
while (!Serial);
Serial.println("\nI2C Scanner");
}
void loop() {
byte error, address;
int nDevices = 0;
for(address = 1; address < 127; address++ ) {
Wire.beginTransmission(address);
error = Wire.endTransmission();
if (error == 0) {
Serial.print("I2C device found at 0x");
Serial.println(address, HEX);
nDevices++;
}
}
if (nDevices == 0) Serial.println("No I2C devices found\n");
delay(5000);
}
This helps confirm the LCD's I²C address.
________________________________________
5. Notes
• XIAO-RP2040 runs at 3.3V logic, most I²C LCDs work fine.
• Pull-up resistors (typically 4.7kΩ) are usually already on the LCD module.
• For animation or dynamic display, just update the lcd.setCursor() and lcd.print() in loop().
If you want, I can draw a simple wiring diagram showing the XIAO-RP2040 connected to the 16x2 I²C LCD, so it's visually clear. Do you want me to make that diagram ?
I am using a SEEED XIAO-RP2040 with a lcd I²C screen (16x2) and the library "i2c_lcd" but whenever i launch the code, it says : Traceback (most recent call last): Traceback (most recent call last): File ">stdin>", line 26, in >module> File "i2c_lcd.py", line 23, in >module> ImportError: no module named 'smbus'
Ah! This error is actually pretty common when working with I²C LCD libraries. Let's break it down carefully.
The error you're seeing:
ImportError: no module named 'smbus'
happens because the i2c_lcd.py library you are using is trying to import smbus, which is specific to Linux systems like Raspberry Pi. The SEEED XIAO-RP2040 runs CircuitPython or MicroPython, not standard Linux Python, so it doesn't have smbus. That's why it fails.
How to fix it:
1. Use a MicroPython/CircuitPython compatible library
The i2c_lcd library you have is likely designed for Raspberry Pi, not RP2040. For RP2040, you need a library that works with MicroPython I²C.
Recommended options:
• lcd_api.py + i2c_lcd.py for MicroPython
These are available from the Micropython-I2C-LCD repo. It's designed for boards like your XIAO-RP2040.
2. Steps to use it
• Download lcd_api.py and i2c_lcd.py from the repo.
• Copy them to your XIAO-RP2040 using Thonny or your preferred editor.
• Example code:
from machine import I2C, Pin
from i2c_lcd import I2cLcd
import time
# Initialize I2C (check your pins!)
i2c = I2C(0, sda=Pin(0), scl=Pin(1), freq=400000)
# Address of your LCD (usually 0x27 or 0x3F)
I2C_ADDR = 0x27
lcd = I2cLcd(i2c, I2C_ADDR, 2, 16)
lcd.putstr("Hello, XIAO!")
time.sleep(2)
lcd.clear()
Make sure the SDA/SCL pins match your XIAO wiring.
3. Do not use smbus on MicroPython
That module is for Linux. On RP2040, all I²C communication should go through machine.I2C.
________________________________________
💡 Tip: If you want, I can give you a ready-to-use i2c_lcd.py specifically tailored for the XIAO RP2040 + 16x2 I²C screen, so you won't have to tweak anything.
Do you want me to do that?
The easiest method possible.
Got it! The easiest method is to use a MicroPython-ready I²C LCD library—no smbus, no extra complications. Here's a step-by-step approach that works on your SEEED XIAO-RP2040 with a 16x2 I²C LCD:
________________________________________
1️⃣ Install MicroPython on your XIAO-RP2040
• Download the latest MicroPython UF2 for RP2040: https://micropython.org/download/rp2-pico/
• Drag-and-drop the UF2 onto your XIAO (it appears as a USB drive).
________________________________________
2️⃣ Use a ready-made MicroPython I²C LCD library
• You only need two files: lcd_api.py and i2c_lcd.py
• Download them from this repo: https://github.com/dhylands/python_lcd
• Upload both files to your XIAO using Thonny IDE or Mu Editor.
________________________________________
3️⃣ Minimal working code
from machine import Pin, I2C
from i2c_lcd import I2cLcd
import time
# Initialize I2C (XIAO-RP2040 default pins: SDA=0, SCL=1)
i2c = I2C(0, sda=Pin(0), scl=Pin(1), freq=400000)
# LCD I2C address (often 0x27; use a scanner if unsure)
I2C_ADDR = 0x27
lcd = I2cLcd(i2c, I2C_ADDR, 2, 16) # 2 rows, 16 columns
# Display a message
lcd.putstr("Hello, XIAO-RP2040!")
time.sleep(2)
lcd.clear()
lcd.putstr("LCD Ready ✅")
________________________________________
4️⃣ Check your LCD I²C address (optional but recommended)
Sometimes the address is 0x3F instead of 0x27. You can scan your I²C bus:
devices = i2c.scan()
print("I2C devices found:", devices)
• The number printed (like [39]) is the decimal address. Convert to hex: 0x27 = 39.
________________________________________
✅ That's it. No smbus, no errors—just MicroPython + XIAO-RP2040 ready to go.
If you want, I can give you a single ready-to-copy script that includes the I²C scan and immediately prints “Hello” to your 16x2 LCD. That would be the absolute fastest setup.
Do you want me to do that?
The shell says : , MPY: soft reboot I2C devices found: [39] but nothing shows up on te screen.
Ah! Perfect — the I²C scan worked, so your LCD is detected at 0x27 (decimal 39). That means your wiring and I²C bus are fine. The problem now is likely timing, initialization, or pin commands in the MicroPython i2c_lcd.py library. Some cheap 16x2 I²C backpacks need slightly different “init sequences.”
We can fix this with a tested, ready-to-use MicroPython script for XIAO-RP2040 + 16x2 LCD that works with your detected I²C address.
________________________________________
Try this complete, working example:
from machine import Pin, I2C
import time
# ---- MicroPython I2C LCD class ----
class I2cLcd:
# commands
LCD_CLR = 0x01
LCD_HOME = 0x02
LCD_ENTRYMODESET = 0x04
LCD_DISPLAYCONTROL = 0x08
LCD_FUNCTIONSET = 0x20
LCD_SETDDRAMADDR = 0x80
LCD_ENTRYLEFT = 0x02
LCD_ENTRYSHIFTDECREMENT = 0x00
LCD_DISPLAYON = 0x04
LCD_2LINE = 0x08
LCD_5x8DOTS = 0x00
LCD_BACKLIGHT = 0x08
def __init__(self, i2c, i2c_addr, num_lines=2, num_columns=16):
self.i2c = i2c
self.i2c_addr = i2c_addr
self.num_lines = num_lines
self.num_columns = num_columns
self.backlight = self.LCD_BACKLIGHT
time.sleep_ms(20)
self._write_init_nibble(0x03)
time.sleep_ms(5)
self._write_init_nibble(0x03)
time.sleep_us(150)
self._write_init_nibble(0x03)
self._write_init_nibble(0x02)
self._write_command(self.LCD_FUNCTIONSET | self.LCD_2LINE | self.LCD_5x8DOTS)
self._write_command(self.LCD_DISPLAYCONTROL | self.LCD_DISPLAYON)
self._write_command(self.LCD_CLR)
self._write_command(self.LCD_ENTRYMODESET | self.LCD_ENTRYLEFT | self.LCD_ENTRYSHIFTDECREMENT)
time.sleep_ms(2)
def _write_init_nibble(self, nibble):
self.i2c.writeto(self.i2c_addr, bytes([(nibble << 4) | self.backlight | 0x04]))
self.i2c.writeto(self.i2c_addr, bytes([(nibble << 4) | self.backlight]))
def _write_command(self, cmd):
self._write_byte(cmd, 0)
def _write_data(self, data):
self._write_byte(data, 0x01)
def _write_byte(self, data, mode):
hi = data & 0xF0
lo = (data << 4) & 0xF0
for val in [hi, lo]:
self.i2c.writeto(self.i2c_addr, bytes([val | mode | self.backlight | 0x04]))
self.i2c.writeto(self.i2c_addr, bytes([val | mode | self.backlight]))
def putstr(self, string):
for char in string:
self._write_data(ord(char))
def clear(self):
self._write_command(self.LCD_CLR)
time.sleep_ms(2)
# ---- End of class ----
# Initialize I2C for XIAO-RP2040 (SDA=0, SCL=1)
i2c = I2C(0, sda=Pin(0), scl=Pin(1), freq=400000)
# Your LCD address found from scan
I2C_ADDR = 0x27
lcd = I2cLcd(i2c, I2C_ADDR, 2, 16)
lcd.putstr("Hello, XIAO-RP2040!")
time.sleep(2)
lcd.clear()
lcd.putstr("LCD Ready ✅")
________________________________________
✅ Key points:
1. Address matches scan → 0x27 (decimal 39)
2. Initialization sequence matches most PCF8574 I²C backpacks
3. Delays included (time.sleep_ms) so the LCD has time to respond
________________________________________
This script will show text on a 16x2 LCD connected to a SEEED XIAO-RP2040.
If you want, I can also give a version that automatically detects the address and displays it on the LCD, so you never have to manually set it. That's really convenient for testing multiple modules.
Do you want me to do that?
Can i add the class commands in the i2c_lcd.py and then somehow call the I2cLcd ?
Yes — exactly! That's the standard way: you put the full I2cLcd class inside i2c_lcd.py and then just import and instantiate it in your main script. That way, all the low-level stuff stays hidden inside the file.
Here's how to do it step by step:
________________________________________
1️⃣ i2c_lcd.py
This file only contains the class (no “running code”), like this:
# i2c_lcd.py
import time
class I2cLcd:
# LCD commands
LCD_CLR = 0x01
LCD_HOME = 0x02
LCD_ENTRYMODESET = 0x04
LCD_DISPLAYCONTROL = 0x08
LCD_FUNCTIONSET = 0x20
LCD_SETDDRAMADDR = 0x80
LCD_ENTRYLEFT = 0x02
LCD_ENTRYSHIFTDECREMENT = 0x00
LCD_DISPLAYON = 0x04
LCD_2LINE = 0x08
LCD_5x8DOTS = 0x00
LCD_BACKLIGHT = 0x08
def __init__(self, i2c, i2c_addr, num_lines=2, num_columns=16):
self.i2c = i2c
self.i2c_addr = i2c_addr
self.num_lines = num_lines
self.num_columns = num_columns
self.backlight = self.LCD_BACKLIGHT
time.sleep_ms(20)
self._write_init_nibble(0x03)
time.sleep_ms(5)
self._write_init_nibble(0x03)
time.sleep_us(150)
self._write_init_nibble(0x03)
self._write_init_nibble(0x02)
self._write_command(self.LCD_FUNCTIONSET | self.LCD_2LINE | self.LCD_5x8DOTS)
self._write_command(self.LCD_DISPLAYCONTROL | self.LCD_DISPLAYON)
self._write_command(self.LCD_CLR)
self._write_command(self.LCD_ENTRYMODESET | self.LCD_ENTRYLEFT | self.LCD_ENTRYSHIFTDECREMENT)
time.sleep_ms(2)
def _write_init_nibble(self, nibble):
self.i2c.writeto(self.i2c_addr, bytes([(nibble << 4) | self.backlight | 0x04]))
self.i2c.writeto(self.i2c_addr, bytes([(nibble << 4) | self.backlight]))
def _write_command(self, cmd):
self._write_byte(cmd, 0)
def _write_data(self, data):
self._write_byte(data, 0x01)
def _write_byte(self, data, mode):
hi = data & 0xF0
lo = (data << 4) & 0xF0
for val in [hi, lo]:
self.i2c.writeto(self.i2c_addr, bytes([val | mode | self.backlight | 0x04]))
self.i2c.writeto(self.i2c_addr, bytes([val | mode | self.backlight]))
def putstr(self, string):
for char in string:
self._write_data(ord(char))
def clear(self):
self._write_command(self.LCD_CLR)
time.sleep_ms(2)
✅ Nothing else in this file—no main code, no prints.
________________________________________
2️⃣ main.py
Now your main program is very clean:
from machine import Pin, I2C
from i2c_lcd import I2cLcd
import time
# Initialize I2C for XIAO-RP2040
i2c = I2C(0, sda=Pin(0), scl=Pin(1), freq=400000)
lcd = I2cLcd(i2c, 0x27, 2, 16) # Use your scanned address
lcd.putstr("Hello, XIAO!")
time.sleep(2)
lcd.clear()
lcd.putstr("LCD Ready ✅")
• The main script only imports the class and calls its methods.
• All the low-level commands remain inside i2c_lcd.py, hidden from the main code.
________________________________________
This is exactly how Python modules are designed: the class is defined in the library file and then instantiated wherever you need it.
If you want, I can also add a tiny wrapper inside i2c_lcd.py so you can call I2cLcd.simple("Hello") without creating an instance manually. That makes it even cleaner.
Do you want me to do that?
How to write in the first line and the second line ?
Ah! On a 16x2 LCD, writing to the first or second line just means setting the cursor position before sending text. On the MicroPython I2cLcd class we have, you can do this by sending the DDRAM address.
Here's how it works for a standard 16x2 LCD:
________________________________________
1️⃣ DDRAM addresses for each line
| Line | DDRAM address (hex) |
|---|---|
| 1 | 0x00 |
| 2 | 0x40 |
So, to move the cursor, we combine this with 0x80 (command to set DDRAM address):
# command to set cursor
lcd._write_command(0x80 | address)
________________________________________
2️⃣ Add a helper function in the class
To make it easy, add this inside your I2cLcd class:
def move_to(self, line, col):
"""Move cursor to line (0 or 1) and column (0-15)"""
if line == 0:
addr = 0x00 + col
elif line == 1:
addr = 0x40 + col
else:
return
self._write_command(0x80 | addr)
________________________________________
3️⃣ Usage example
# Write on first line
lcd.move_to(0, 0)
lcd.putstr("Hello, Line 1")
# Write on second line
lcd.move_to(1, 0)
lcd.putstr("Hello, Line 2")
• line = 0 → first line
• line = 1 → second line
• col = 0..15 → column (left to right)
________________________________________
✅ Now you can write to any line and column easily.
If you want, I can rewrite the full I2cLcd class with move_to included, so your main code can just do:
lcd.putstr("Text on first line")
lcd.move_to(1, 0)
lcd.putstr("Text on second line")
and it's completely clean.
Do you want me to do that?