Pi 5 Stratum-1 NTP Server from ESP32 GNSS PPS

Use the kernel PPS path. Do not write a Python or C GPIO polling loop for timing. User-space timing will add scheduling latency; the kernel PPS driver timestamps the edge much earlier and exposes it as /dev/pps0.

Wiring:

ESP32 GPIO27(PPS) ----------------->  Pi 5 GPIO18 / phy pin 12
ESP32 GND         ----------------->  Pi 5 GND    / phy pin 14

Time Source Design

PPS alone is not a complete clock. It tells the Pi exactly where the second boundary is, but not which UTC second it is.

Chrony therefore needs:

  • PPS on /dev/pps0 for the precise second edge.
  • A normal time source, such as the ESP32 GNSS NTP server, for UTC date and second numbering.

Flow:

M10S GNSS
   |
   | GNSS time + PPS
   v
ESP32
   |
   | GPIO27 PPS
   v
Raspberry Pi 5 GPIO18
   |
   | kernel PPS timestamp
   v
/dev/pps0
   |
   v
chronyd
   |
   +-- PPS: precise second boundary
   +-- ESP32 NTP: UTC second/date
   |
   v
Pi 5 serves LAN NTP as stratum 1

Enable PPS on Raspberry Pi 5

Edit the Pi boot config:

sudo nano /boot/firmware/config.txt

Add this line:

dtoverlay=pps-rp1,pin=18,pull-down,schmitt-trigger

Why this overlay:

  • pps-rp1 is the Pi 5/RP1-specific PPS overlay.
  • pin=18 selects GPIO18, physical pin 12.
  • pull-down=keeps the input from floating when PPS is disconnected.
  • schmitt-trigger can help clean up marginal/noisy edges on a wire.

Do not add assert-falling-edge for the normal GNSS PPS case. The default is rising-edge assert, which is what you usually want from a GNSS 1PPS output.

Reboot:

After reboot, check that the PPS device exists:

ls -l /dev/pps*

Install Chrony and PPS Tools:

apt update
apt install chrony pps-tools

Test the PPS input before configuring Chrony:

ppstest /dev/pps0

Expected output should increment once per second:

root@lp-arm-5:~# ppstest /dev/pps0
trying PPS source "/dev/pps0"
found PPS source "/dev/pps0"
ok, found 1 source(s), now start fetching data...
source 0 - assert 1788624719.999999432, sequence: 5376 - clear  0.000000000, sequence: 0
source 0 - assert 1788624721.000000259, sequence: 5377 - clear  0.000000000, sequence: 0
source 0 - assert 1788624721.999999836, sequence: 5378 - clear  0.000000000, sequence: 0

Configure Chrony

Assume the ESP32 GNSS NTP server is reachable at:

192.168.10.90

Edit Chrony config:

sudo nano /etc/chrony/chrony.conf

Use this minimal configuration as the core of the file. Adjust the server IP and allow network for your LAN.

# Coarse UTC source.
# This gives chronyd the actual UTC second/date.
server 192.168.10.90 iburst minpoll 3 maxpoll 3 prefer

# Precise PPS source.
# ESP32 GPIO27 -> Pi 5 GPIO18 -> kernel PPS -> /dev/pps0.
refclock PPS /dev/pps0 refid PPS poll 0 prefer

# Clock discipline.
makestep 0.1 3
rtcsync

# Allow LAN clients to query this Pi as an NTP server.
# Change this to match your network.
allow 192.168.0.0/16

# Optional logging.
log tracking measurements statistics
logdir /var/log/chrony

Restart Chrony:

sudo systemctl restart chrony

Verify SynchronizationP

chronyc sources -v

Healthy output eventually looks like this:

root@lp-arm-5:~# chronyc sources -v
MS Name/IP address         Stratum Poll Reach LastRx Last sample               
===============================================================================
#* PPS                           0   0   377     1   -133ns[ -155ns] +/-   18ns
^- 192.168.10.90                 1   3   377     7  -2363us[-2362us] +/- 5521us
  • 2 Local PPS compare
refclock PPS /dev/pps0 refid PPS0 poll 0
refclock PPS /dev/pps1 refid PPS1 poll 0

Verify NTP Serving from Another Machine:

From another Linux machine on the LAN:

ntpdate -q <PI5_IP>

Example:

ntpdate -q 192.168.0.183

Why There Is No Custom Timing Code?

The fast and low-jitter path is:

ESP32 PPS
   |
   v
Pi 5 RP1 GPIO interrupt
   |
   v
Linux PPS kernel timestamp
   |
   v
/dev/pps0
   |
   v
chronyd

A custom C or Python program would run after the kernel schedules it, which adds avoidable latency and jitter. For a stratum-1 NTP server, use the kernel PPS driver plus Chrony.

References

sdc41 with bmp280 issue – wrong ppm value – zero ppm

  • Today got to know why my sdc41 sensor was showing bad data earlier it used to show 500 in day and 600-800ppm in night when door was closed. #sdc41 #esp32
    • It was paired with bmp280 and it was using pressure to Compensate the co2. but bmp 280 was showing bad value randomly about -149 hpa
    • As it was using BMP280 value it was started showing double and after few days it started showing 0 ppm
    • Now i ran simple code to just get value of sdc41 it was showing 23000 which more than current limit of sdc41 which is 5000
    • Ran frc 420 to self calibrate the sensor with current value to 420 reference
  • Ran auto calibration , everything looks good now.
#include <Arduino.h>
#include <Wire.h>
#include <SparkFun_SCD4x_Arduino_Library.h>

// NodeMCU/ESP8266 I2C pins: D2 = SDA, D1 = SCL
static const uint8_t SDA_PIN = D2;
static const uint8_t SCL_PIN = D1;

static const uint16_t MAX_VALID_CO2_PPM = 5000;
static const uint16_t DEFAULT_FRC_REFERENCE_PPM = 420; // Typical fresh-air reference
static const uint32_t FRC_STABILIZE_MS = 180000;       // >= 3 minutes per datasheet
static const float DEFAULT_AMBIENT_PRESSURE_HPA = 1006.0f;

SCD4x scd4x(SCD4x_SENSOR_SCD41);

bool applyAmbientPressureCompensation(float pressureHpa, bool persist = true) {
  if (pressureHpa < 700.0f || pressureHpa > 1200.0f) {
    Serial.println("ERROR: pressure out of expected range (700..1200 hPa).");
    return false;
  }

  const float pressurePa = pressureHpa * 100.0f;

  scd4x.stopPeriodicMeasurement();
  delay(500);

  if (!scd4x.setAmbientPressure(pressurePa)) {
    Serial.println("ERROR: Failed to set ambient pressure compensation.");
    scd4x.startPeriodicMeasurement();
    return false;
  }

  if (persist && !scd4x.persistSettings()) {
    Serial.println("WARNING: Ambient pressure set but persistSettings failed.");
  }

  if (!scd4x.startPeriodicMeasurement()) {
    Serial.println("ERROR: Failed to restart periodic measurement after pressure update.");
    return false;
  }

  Serial.print("Ambient pressure compensation set to ");
  Serial.print(pressureHpa, 1);
  Serial.println(" hPa");
  return true;
}

bool configureAutoSelfCalibration(bool enabled) {
  scd4x.stopPeriodicMeasurement();
  delay(500);

  if (!scd4x.setAutomaticSelfCalibrationEnabled(enabled)) {
    Serial.println("ERROR: Failed to set ASC state.");
    return false;
  }

  if (!scd4x.persistSettings()) {
    Serial.println("WARNING: ASC updated but persistSettings failed.");
  }

  if (!scd4x.startPeriodicMeasurement()) {
    Serial.println("ERROR: Failed to restart periodic measurement after ASC config.");
    return false;
  }

  Serial.print("ASC is now ");
  Serial.println(enabled ? "ENABLED" : "DISABLED");
  return true;
}

bool runForcedRecalibration(uint16_t referencePpm) {
  if (referencePpm < 350 || referencePpm > 2000) {
    Serial.println("ERROR: Reference ppm out of expected FRC range (350..2000).");
    return false;
  }

  Serial.println();
  Serial.println("Starting Forced Recalibration (FRC)...");
  Serial.println("Keep sensor in stable, known CO2 environment.");
  Serial.print("Reference CO2 (ppm): ");
  Serial.println(referencePpm);

  scd4x.stopPeriodicMeasurement();
  delay(500);
  if (!scd4x.startPeriodicMeasurement()) {
    Serial.println("ERROR: Could not start periodic measurement for FRC preconditioning.");
    return false;
  }

  // Datasheet requirement: run in normal mode for at least 3 minutes before FRC.
  const uint32_t startMs = millis();
  while (millis() - startMs < FRC_STABILIZE_MS) {
    const uint32_t remaining = (FRC_STABILIZE_MS - (millis() - startMs)) / 1000;
    Serial.print("FRC preconditioning... ");
    Serial.print(remaining);
    Serial.println(" s remaining");

    if (scd4x.readMeasurement()) {
      Serial.print("  sample CO2(ppm): ");
      Serial.println(scd4x.getCO2());
    }

    delay(5000);
  }

  scd4x.stopPeriodicMeasurement();
  delay(500);

  float correction = 0.0f;
  if (!scd4x.performForcedRecalibration(referencePpm, &correction)) {
    Serial.println("ERROR: FRC failed.");
    scd4x.startPeriodicMeasurement();
    return false;
  }

  Serial.print("FRC correction applied (ppm): ");
  Serial.println(correction, 1);

  if (!scd4x.persistSettings()) {
    Serial.println("WARNING: FRC succeeded but persistSettings failed.");
  }

  if (!scd4x.reInit()) {
    Serial.println("WARNING: reInit failed after FRC.");
  }

  if (!scd4x.startPeriodicMeasurement()) {
    Serial.println("ERROR: Could not restart periodic measurement after FRC.");
    return false;
  }

  Serial.println("FRC complete. Sensor restarted in periodic mode.");
  return true;
}

void handleSerialCommands() {
  if (!Serial.available()) {
    return;
  }

  String cmd = Serial.readStringUntil('\n');
  cmd.trim();
  cmd.toLowerCase();

  if (cmd == "help") {
    Serial.println("Commands:");
    Serial.println("  help         -> show commands");
    Serial.println("  asc on       -> enable automatic self-calibration");
    Serial.println("  asc off      -> disable automatic self-calibration");
    Serial.println("  frc          -> run forced recalibration with 420 ppm reference");
    Serial.println("  frc <ppm>    -> run forced recalibration with custom reference");
    Serial.println("  pressure     -> set pressure compensation to 1006 hPa");
    return;
  }

  if (cmd == "asc on") {
    configureAutoSelfCalibration(true);
    return;
  }

  if (cmd == "asc off") {
    configureAutoSelfCalibration(false);
    return;
  }

  if (cmd == "frc") {
    runForcedRecalibration(DEFAULT_FRC_REFERENCE_PPM);
    return;
  }

  if (cmd.startsWith("frc ")) {
    int ppm = cmd.substring(4).toInt();
    if (ppm <= 0) {
      Serial.println("ERROR: Invalid ppm. Example: frc 420");
      return;
    }
    runForcedRecalibration((uint16_t)ppm);
    return;
  }

  if (cmd == "pressure") {
    applyAmbientPressureCompensation(DEFAULT_AMBIENT_PRESSURE_HPA, true);
    return;
  }

  Serial.print("Unknown command: ");
  Serial.println(cmd);
  Serial.println("Type 'help' for available commands.");
}

void setup() {
  Serial.begin(115200);
  delay(200);

  Serial.println();
  Serial.println("SCD41 minimal CO2 test starting...");

  Wire.begin(SDA_PIN, SCL_PIN);
  Wire.setClock(100000);

  if (!scd4x.begin(true, true, false)) {
    Serial.println("ERROR: SCD41 init failed. Check wiring, power, and I2C address.");
    while (true) {
      delay(1000);
    }
  }

  // Ensure periodic mode starts from a clean state.
  scd4x.stopPeriodicMeasurement();
  delay(500);

  if (!scd4x.startPeriodicMeasurement()) {
    Serial.println("ERROR: Could not start low-power periodic measurement.");
    while (true) {
      delay(1000);
    }
  }

  // Recommended for SCD41 long-term stability in real deployments.
  configureAutoSelfCalibration(true);

  // Overwrite any previously saved bad pressure setting (e.g. from a faulty upstream sensor)
  // and persist the correct local pressure compensation.
  applyAmbientPressureCompensation(DEFAULT_AMBIENT_PRESSURE_HPA, true);

  Serial.println("SCD41 initialized. First reading can take ~30s.");
  Serial.println("Type 'help' in Serial Monitor for calibration commands.");
}

void loop() {
  handleSerialCommands();

  if (!scd4x.readMeasurement()) {
    Serial.println("Measurement not ready yet...");
    delay(5000);
    return;
  }

  const uint16_t co2 = scd4x.getCO2();
  const float temperature = scd4x.getTemperature();
  const float humidity = scd4x.getHumidity();

  Serial.print("CO2(ppm): ");
  Serial.print(co2);
  Serial.print("  Temp(C): ");
  Serial.print(temperature, 2);
  Serial.print("  RH(%): ");
  Serial.println(humidity, 2);

  if (co2 == 0) {
    Serial.println("WARNING: CO2 is 0 ppm (invalid on SCD41). Sensor may still be stabilizing or faulty.");
  } else if (co2 > MAX_VALID_CO2_PPM) {
    Serial.println("WARNING: CO2 is above SCD41 range (5000 ppm). Reading likely invalid; run 'frc 420' in fresh air.");
  }

  delay(5000);
}
FRC preconditioning... 24 s remaining
  sample CO2(ppm): 23076
FRC preconditioning... 19 s remaining
  sample CO2(ppm): 23076
FRC preconditioning... 14 s remaining
  sample CO2(ppm): 23077
FRC preconditioning... 9 s remaining
  sample CO2(ppm): 23075
FRC preconditioning... 4 s remaining
  sample CO2(ppm): 23078
FRC correction applied (ppm): -22518.0
FRC complete. Sensor restarted in periodic mode.
Measurement not ready yet...
CO2(ppm): 471  Temp(C): 30.83  RH(%): 73.06
CO2(ppm): 472  Temp(C): 30.39  RH(%): 74.56
CO2(ppm): 488  Temp(C): 30.14  RH(%): 75.75
CO2(ppm): 486  Temp(C): 29.92  RH(%): 76.85
CO2(ppm): 469  Temp(C): 29.68  RH(%): 77.88

raspberry pi useful commands

  • See voltage of all pin – can be used in TRNG
root@lp-arm-5:~# vcgencmd pmic_read_adc
 3V7_WL_SW_A current(0)=0.09271335A
   3V3_SYS_A current(1)=0.07026696A
   1V8_SYS_A current(2)=0.12589500A
  DDR_VDD2_A current(3)=0.00292779A
  DDR_VDDQ_A current(4)=0.00000000A
   1V1_SYS_A current(5)=0.20299340A
    0V8_SW_A current(6)=0.35523850A
  VDD_CORE_A current(7)=0.68761000A
   3V3_DAC_A current(17)=0.00000000A
   3V3_ADC_A current(18)=0.00006105A
   0V8_AON_A current(16)=0.00323565A
      HDMI_A current(22)=0.01172160A
 3V7_WL_SW_V volt(8)=3.69977600V
   3V3_SYS_V volt(9)=3.30632200V
   1V8_SYS_V volt(10)=1.79975400V
  DDR_VDD2_V volt(11)=1.10732500V
  DDR_VDDQ_V volt(12)=0.60402870V
   1V1_SYS_V volt(13)=1.10256300V
    0V8_SW_V volt(14)=0.79926660V
  VDD_CORE_V volt(15)=0.72097620V
   3V3_DAC_V volt(20)=3.30768900V
   3V3_ADC_V volt(21)=3.30585700V
   0V8_AON_V volt(19)=0.79736190V
      HDMI_V volt(23)=5.02902000V
     EXT5V_V volt(24)=5.02098000V
      BATT_V volt(25)=0.00000000V

ESP 8266 node auto reboot – ESPHome

  • logs
[10:24:37.762][E][api:128]: No clients; rebooting
[10:24:37.806][I][app:264]: Forcing a reboot
[10:24:38.079][W][wifi_esp8266:512]: Disconnected ssid='JioFiber-b44M3' bssid=34:D8:56:21:62:FA reason='Association Leave'\xff\xeaU\xfa[I][logger:032]: Log initialized
[10:24:38.151][E][esp8266:171]: *** CRASH DETECTED ON PREVIOUS BOOT ***
[10:24:38.231][E][esp8266:186]:   Reason: Hardware WDT - Level1Int (exccause=4)
[10:24:38.279][E][esp8266:191]:   PC: 0x40106487
WARNING Decoded 0x40106487: system_get_time
[10:24:38.343][C][safe_mode:189]: Unsuccessful boot attempts: 0
[10:24:38.394][I][app:060]: Running through setup()

This means the ESPHome API decided to reboot because no API client (typically Home Assistant or the ESPHome dashboard) was connected for the configured timeout.

By default, if you’re using the api: component, ESPHome can reboot after a period without an API connection.

  • Remove esphome api or set to 0
api:
  reboot_timeout: 0s

raspberry pi boot long time – no boot / reboot issue fixed

I have raspberry pi 4 with raspbian os lite installed and the OS is directly installed on SSD which is connected with USB3.0 with connector USB-to-SATA. Whenever the power outage happens the pi some time boots and sometime it doesn’t and it also takes lots of time to boot 10min+

  • First I checked with below command what services are taking longer time and disabled docker and kubelet rpi-eeprom-update
root@lp-arm-3:~# systemd-analyze blame
1min 36.522s docker.service
     51.427s containerd.service
     50.609s rpi-eeprom-update.service
     50.590s user@1000.service
     50.221s ModemManager.service
     49.702s NetworkManager-wait-online.service
     38.108s apt-daily-upgrade.service
     32.277s dpkg-db-backup.service
      2.558s dev-sda2.device
      1.281s raspi-config.service
       670ms man-db.service
       613ms NetworkManager.service
       583ms keyboard-setup.service
       536ms systemd-logind.service
       512ms systemd-udev-trigger.service
       429ms systemd-journald.service


root@lp-arm-3:~# systemd-analyze
Startup finished in 36.419s (kernel) + 2min 33.375s (userspace) = 3min 9.794s 
multi-user.target reached after 2min 33.318s in userspace.



root@lp-arm-3:~# systemd-analyze critical-chain
The time when unit became active or started is printed after the "@" character.
The time the unit took to start is printed after the "+" character.

multi-user.target @2min 33.318s
└─kubelet.service @1min 6.646s
  └─network-online.target @55.038s
    └─NetworkManager-wait-online.service @5.315s +49.702s
      └─NetworkManager.service @4.687s +613ms
        └─dbus.service @4.552s +97ms
          └─basic.target @4.509s
            └─sockets.target @4.509s
              └─docker.socket @4.485s +23ms
                └─sysinit.target @4.468s
                  └─systemd-timesyncd.service @4.091s +375ms
                    └─systemd-tmpfiles-setup.service @3.805s +208ms
                      └─local-fs.target @3.777s
                        └─run-user-1000.mount @5.576s
                          └─local-fs-pre.target @1.650s
                            └─keyboard-setup.service @1.065s +583ms
                              └─systemd-journald.socket @1.019s
                                └─-.mount @946ms
                                  └─-.slice @946ms
  • This did not fix issue.
  • Main issue was USB SATA driver speed.
  • I enabled permanent logs for boot
journalctl --list-boots
journalctl -b -1

show only error

journalctl -b -1 -p err
Feb 07 20:40:53 lp-arm-3.home kernel: sd 0:0:0:0: [sda] tag#9 uas_eh_abort_handler 0 uas-tag 15 inflight: CMD OUT Feb 07 20:40:53 lp-arm-3.home kernel: sd 0:0:0:0: [sda] tag#9 CDB: opcode=0x2a 2a 00 01 50 21 78 00 00 08 00 Feb 07 20:40:53 lp-arm-3.home kernel: sd 0:0:0:0: [sda] tag#4 uas_eh_abort_handler 0 uas-tag 6 inflight: CMD Feb 07 20:40:53 lp-arm-3.home kernel: sd 0:0:0:0: [sda] tag#4 CDB: opcode=0x35 35 00 00 00 00 00 00 00 00 00 Feb 07 20:40:53 lp-arm-3.home kernel: scsi host0: uas_eh_device_reset_handler start Feb 07 20:40:53 lp-arm-3.home kernel: usb 2-2: reset SuperSpeed USB device number 2 using xhci_hcd Feb 07 20:40:53 lp-arm-3.home kernel: scsi host0: uas_eh_device_reset_handler success Feb 07 20:40:53 lp-arm-3.home kernel: sd 0:0:0:0: [sda] tag#8 UNKNOWN(0x2003) Result: hostbyte=0x07 driverbyte=DRIVER_OK cmd_age=61s Feb 07 20:40:53 lp-arm-3.home kernel: sd 0:0:0:0: [sda] tag#8 CDB: opcode=0x28 28 00 00 5e dc f8 00 00 10 00 Feb 07 20:40:53 lp-arm-3.home kernel: I/O error, dev sda, sector 6216952 op 0x0:(READ) flags 0x80700 phys_seg 2 prio class 2 Feb 07 20:40:53 lp-arm-3.home kernel: sd 0:0:0:0: [sda] tag#2 UNKNOWN(0x2003) Result: hostbyte=0x07 driverbyte=DRIVER_OK cmd_age=61s Feb 07 20:40:53 lp-arm-3.home kernel: sd 0:0:0:0: [sda] tag#2 CDB: opcode=0x28 28 00 02 88 f5 e0 00 00 30 00 Feb 07 20:40:53 lp-arm-3.home kernel: I/O error, dev sda, sector 42530272 op 0x0:(READ) flags 0x80700 phys_seg 6 prio class 2 Feb 07 20:40:53 lp-arm-3.home kernel: sd 0:0:0:0: [sda] tag#1 UNKNOWN(0x2003) Result: hostbyte=0x07 driverbyte=DRIVER_OK cmd_age=61s Feb 07 20:40:53 lp-arm-3.home kernel: sd 0:0:0:0: [sda] tag#1 CDB: opcode=0x28 28 00 00 5e cb c8 00 01 00 00 Feb 07 20:40:53 lp-arm-3.home kernel: I/O error, dev sda, sector 6212552 op 0x0:(READ) flags 0x80700 phys_seg 32 prio class 2 Feb 07 20:40:53 lp-arm-3.home kernel: sd 0:0:0:0: [sda] tag#3 UNKNOWN(0x2003) Result: hostbyte=0x07 driverbyte=DRIVER_OK cmd_age=61s Feb 07 20:40:53 lp-arm-3.home kernel: sd 0:0:0:0: [sda] tag#3 CDB: opcode=0x28 28 00 02 84 ee 68 00 00 50 00 Feb 07 20:40:53 lp-arm-3.home kernel: I/O error, dev sda, sector 42266216 op 0x0:(READ) flags 0x80700 phys_seg 10 prio class 2 Feb 07 20:40:53 lp-arm-3.home kernel: sd 0:0:0:0: [sda] tag#6 UNKNOWN(0x2003) Result: hostbyte=0x07 driverbyte=DRIVER_OK cmd_age=55s Feb 07 20:40:53 lp-arm-3.home kernel: sd 0:0:0:0: [sda] tag#6 CDB: opcode=0x28 28 00 02 80 6e 38 00 00 90 00 Feb 07 20:40:53 lp-arm-3.home kernel: I/O error, dev sda, sector 41971256 op 0x0:(READ) flags 0x80700 phys_seg 18 prio class 2 Feb 07 20:40:53 lp-arm-3.home kernel: sd 0:0:0:0: [sda] tag#7 UNKNOWN(0x2003) Result: hostbyte=0x07 driverbyte=DRIVER_OK cmd_age=49s Feb 07 20:40:53 lp-arm-3.home kernel: sd 0:0:0:0: [sda] tag#7 CDB: opcode=0x28 28 00 02 80 3c d0 00 00 58 00 Feb 07 20:40:53 lp-arm-3.home kernel: I/O error, dev sda, sector 41958608 op 0x0:(READ) flags 0x80700 phys_seg 11 prio class 2
  • The kernel is resetting the USB
  • Disabled UAS for SSD – fixed the issue
lsusb

#get the USB ID eg 7825:a2a4

vi /boot/firmware/cmdline.txt

append this line at the end

usb-storage.quirks=7825:a2a4:u

reboot

root@lp-arm-3:~# lsusb -t
/:  Bus 02.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/4p, 5000M
    |__ Port 2: Dev 2, If 0, Class=Mass Storage, Driver=usb-storage, 5000M
/:  Bus 01.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/1p, 480M
    |__ Port 1: Dev 2, If 0, Class=Hub, Driver=hub/4p, 480M

usb-storage (BOT):

sends one command at a time
waits for completion
has simpler recovery
tolerates retries and delays

BME680 sensor permanent fix for 0x77 and 0x76 on rasp pi 5

  • After reboot the
root@pi5:~# i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:                         -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
70: -- -- -- -- -- -- 76 -- 
  • After removing the power pin and reattaching
root@pi5:~# i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:                         -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- 77 
  • ValueError: No I2C device at address: 0x77
  • Solutions:
  • Some BME680 boards default to 0x76 so set it as default.
  • Update the code to try both 0x76 and 0x77

Voltage Sensor – with pico w 2

  • R1 – 7.5K
    R2 – 30K
from machine import ADC
from time import sleep

# Use ADC0 (GPIO26)
adc = ADC(26)

# Constants
VREF = 3.3              # Pico ADC reference voltage
ADC_RESOLUTION = 65535  # For read_u16() scale
DIVIDER_RATIO = 5       # Because 7.5k / (30k + 7.5k) = 0.2 → inverse is 5

def read_input_voltage():
    raw = adc.read_u16()
    v_out = (raw / ADC_RESOLUTION) * VREF
    v_in = v_out * DIVIDER_RATIO
    return round(v_in, 3)

# Main loop
while True:
    voltage = read_input_voltage()
    print("Measured Voltage:", voltage, "V")
    sleep(1)

Since the Pico ADC max is 3.3V and you’re dividing by 5: Vin max=3.3V×5=16.5VV = 3.3V \times 5 = 16.5V

Vin max​=3.3V×5=16.5V

So you can safely measure up to about 16.5 volts.

Pico 2 w – HC-SR04 ultrasonic distance sensor

Pins:

from machine import Pin, time_pulse_us
from time import sleep

# Define GPIO pins
TRIG = Pin(3, Pin.OUT)
ECHO = Pin(2, Pin.IN)

def get_distance():
    # Ensure trigger is low
    TRIG.low()
    sleep(0.002)  # Let sensor settle
    
    # Send a 10µs pulse to trigger
    TRIG.high()
    sleep(0.00001)
    TRIG.low()

    # Measure time for echo
    try:
        duration = time_pulse_us(ECHO, 1, 30000)  # 30ms timeout
    except OSError as ex:
        print("Pulse timed out")
        return None

    # Distance calculation: time (us) × speed of sound (cm/us) / 2
    distance_cm = (duration * 0.0343) / 2
    return round(distance_cm, 2)

# Main loop
while True:
    dist = get_distance()
    if dist:
        print("Distance:", dist, "cm")
    else:
        print("No distance measured.")
    sleep(1)

Pi Pico 2 W – UF2 flash – install

  • While connect to your PC first press the BOOTSEL cutton on the pico 2 w and then connect the USB
  • It will be connected as new drive(FS mode). you can view in “files”