Connecting a Raspberry Pi 3 B+ to a Laptop Without Wi-Fi

Generated with Claude Opus 5 (Claude Code), September 2026. Verify commands against your own hardware and campus policy before relying on them.

Why USB gadget mode can’t work

This is relevant because some high ranking pages will suggest using USB gadget mode.

The Pi 3 B+ is one of the models that physically cannot do gadget mode. Its micro-USB port is wired to the power input only, and the SoC’s OTG-capable USB port is hard-wired to the LAN7515 hub/Ethernet chip, which strips OTG and leaves the board host-only. The dwc2/g_ether steps in the Raspberry Pi USB gadget mode article apply to the Zero, Zero 2 W, Pi 4, Pi 5, Pi 400, and CM4 — boards with a data-capable USB-C or micro-USB port. No amount of config.txt editing will make a 3 B+ enumerate as a device.

So the answer isn’t “USB gadget mode,” it’s “something else over the USB port you already have.”

Recommended: direct Ethernet through a USB Ethernet adapter

The Lab 1 kit already issues exactly the hardware for this — the Trendnet USB 2.0 Ethernet adapter (or similar device such as Realtek USB GbE) and an ethernet patch cable. From the laptop’s point of view this is a USB connection; the laptop has no built-in NIC, so the adapter is the port. The Pi 3 B+’s Ethernet is Auto-MDIX, so a straight patch cable works — no crossover needed, no switch, no Wi-Fi.

Which adapter, on which machine. Two show up in this course and they are not interchangeable for these purposes:

Adapter Used by Notes
Realtek USB GbE instructor demos RTL8153 class. In-box drivers on both Windows 11 and macOS, Apple Silicon included. This is the one to use from a MacBook.
Trendnet USB 2.0 10/100 students ASIX AX88772 class. Fine on Windows, which carries an in-box driver. Do not assume it works on macOS — that generation is not covered in-box on Apple Silicon and wants an ASIX DriverKit package.

Since the student labs run on Windows laptops with the Trendnet, and the Mac path is an instructor demo with the Realtek, that split is normally invisible. It only matters if someone tries to run the Mac instructions with a student’s Trendnet adapter, which is worth knowing before standing in front of a class.

On an Apple Silicon MacBook there are no USB-A ports either way, so a USB-A dongle needs a USB-C adapter or hub in front of it; a native USB-C Gigabit adapter is tidier. Confirm the adapter enumerated, and learn its BSD name (en5, en6, …) and its exact port name:

networksetup -listallhardwareports

If the adapter never appears there, its chipset wants a third-party kext — and on Apple Silicon that means lowering Startup Security, which is not worth doing for a demo. Borrow the Realtek instead.

Windows: Internet Connection Sharing — convenient, but often blocked

ICS gives the Pi a real DHCP lease and internet through the laptop’s Wi-Fi (handy for apt during a demo):

  1. Plug in the USB Ethernet adapter, cable it straight to the Pi, power the Pi.
  2. Run ncpa.cpl → right-click Wi-FiPropertiesSharing tab → check Allow other network users to connect… and pick the USB Ethernet adapter below it.
  3. Windows assigns the adapter 192.168.137.1/24 and runs a DHCP server on it. The Pi picks up 192.168.137.x in about 30 s.
  4. Find and connect: arp -a -N 192.168.137.1 to see the lease, or just ssh pi@<your-hostname>.local — Windows 11 resolves mDNS natively and the Imager set that hostname.

Bonus for the labs: since the VM is already bridged to the USB Ethernet adapter (Lab 1, Figure 3), the VM lands on the same 192.168.137.0/24 and can reach the Pi for scp/gdbserver without changing anything.

Caveat: “An error occurred while Internet Connection Sharing was being enabled. Access is denied.”

On an MSOE-managed laptop this step usually fails with that message, even from an elevated ncpa.cpl. It is not an ACL problem, so elevating does not help. The cause is a domain Group Policy:

HKLM\SOFTWARE\Policies\Microsoft\Windows\Network Connections
    NC_ShowSharedAccessUI = 0

That is Prohibit use of Internet Connection Sharing on your DNS domain network. The Sharing tab still renders, but the enable call is refused.

To confirm this is what you are hitting (the other ICS prerequisites — SharedAccess, MpsSvc, BFE, Netman — should all be running):

Get-Service SharedAccess,MpsSvc,BFE,Netman | Select-Object Name,Status,StartType
Get-ItemProperty 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\Network Connections'

Overriding the policy locally

From an administrative PowerShell prompt:

Set-ItemProperty 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\Network Connections' -Name NC_ShowSharedAccessUI -Value 1
Restart-Service SharedAccess

Then reopen ncpa.cpl and enable sharing again. To put it back:

Set-ItemProperty 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\Network Connections' `
  -Name NC_ShowSharedAccessUI -Value 0

This is a temporary override. The value is pushed by domain Group Policy and will be rewritten at the next policy refresh — background refresh runs roughly every 90 minutes, and always at reboot or on gpupdate /force. This can reset at any time, and it appears MSOE laptops do not allow this setting.

Windows: static IPs — more dependable

No ICS, no policy exception, no DHCP, no mDNS. This is the one to rely on in front of a class.

Attach the USB ethernet adapter with cable and a second USB cable from the laptop to the micro-USB connector on the Pi. Let the operating system boot.

On the laptop, in an administrative PowerShell prompt (substitute the adapter’s own name — check Get-NetAdapter; it shows up as something like Ethernet 7 for a Realtek USB GbE dongle):

Set-NetConnectionProfile -InterfaceAlias "Ethernet 7" -NetworkCategory Private
New-NetIPAddress -InterfaceAlias "Ethernet 7" -IPAddress 192.168.50.1 -PrefixLength 24

Setting the profile to Private is not just tidiness: a fresh USB adapter lands in Public, where the firewall drops inbound mDNS, and that is what breaks <hostname>.local resolution.

On the Pi — do this while it is still on the classroom network, so you do not lock yourself out:

sudo nmcli con mod "Wired connection 1" +ipv4.addresses 192.168.50.2/24
sudo nmcli con up "Wired connection 1"

Using +ipv4.addresses instead of ipv4.method manual keeps DHCP working and adds the fixed address, so the Pi answers at 192.168.50.2 on any network — including a bare cable to the laptop — while still behaving normally on classroom Ethernet. Nothing to reconfigure between the two. If the Pi is not reachable to set this up, use the serial console below.

To undo the laptop side:

Remove-NetIPAddress -InterfaceAlias "Ethernet 7" -IPAddress 192.168.50.1 -Confirm:$false
Set-NetIPInterface -InterfaceAlias "Ethernet 7" -Dhcp Enabled

macOS: Internet Sharing — the ICS equivalent, and it actually works

This assumes you are using a Mac (with administrative status) that is not managed by MSOE.

  1. Plug in the USB Ethernet adapter, cable it straight to the Pi, power the Pi.
  2. System Settings → General → Sharing → Internet Sharing (click the ⓘ to configure before switching it on).
  3. Share your connection from: Wi-Fi. To computers using: check the USB Ethernet adapter (the port name from networksetup -listallhardwareports).
  4. Toggle Internet Sharing on and confirm the prompt.

macOS puts the adapter on 192.168.2.1/24 and runs bootpd as a DHCP server on it, so the Pi picks up a 192.168.2.x lease in about 30 seconds. Note the subnet differs from the Windows ICS 192.168.137.x — if you have written 192.168.137.2 on a sticky note, it does not apply here.

Finding the Pi is genuinely easier than on Windows, because Bonjour is native rather than bolted on:

ssh pi@<your-hostname>.local        # hostname you set in the Imager
dns-sd -B _ssh._tcp                 # or browse for it
arp -an | grep 192.168.2            # or read the lease table

The macOS gotcha: Local Network permission

Recent macOS gates LAN access per application. If ssh to a .local name or a 192.168.2.x address hangs and then times out — with no error that explains itself — the terminal app has not been granted access. Go to System Settings → Privacy & Security → Local Network and enable Terminal (or iTerm, or VS Code if you are sshing from its integrated terminal). The first attempt normally raises the prompt itself, but it is easy to dismiss by reflex and then spend twenty minutes blaming the cable.

macOS: static IPs

Same reasoning as the Windows static setup — no DHCP, no mDNS, nothing to fail in front of a class. Use the exact port name from networksetup -listallhardwareports, quoted:

sudo networksetup -setmanual "USB 10/100/1000 LAN" 192.168.50.1 255.255.255.0

Leave the router/gateway field empty; there is nothing to route to. The GUI equivalent is System Settings → Network → [the adapter] → Details… → TCP/IP → Configure IPv4: Manually.

To undo:

sudo networksetup -setdhcp "USB 10/100/1000 LAN"

The Pi side is identical to the Windows case — the nmcli command above is what it is regardless of what is on the other end of the cable.

Either platform: a static link gives the Pi no internet

Over a static point-to-point link the Pi cannot reach the outside world, so apt will not work during a demo. (macOS Internet Sharing does not have this problem — it NATs for you.) New-NetNat -Name PiNat -InternalIPInterfaceAddressPrefix 192.168.50.0/24 is the WinNAT equivalent and is not covered by the ICS policy — but Windows permits only one NAT instance and the Hyper-V Default Switch already holds one, so it may refuse. Simplest is to do package installs on classroom Ethernet beforehand.

Guaranteed fallback: USB-to-serial console

Worth having in the bag regardless — it works with no networking at all, which is what you want when the demo is about the network being broken.

Windows: finding the COM port

Plug the cable into the laptop first. Windows 11 ships inbox drivers for FTDI and CP210x parts, so the adapter should enumerate on its own; a CH340 clone may need the vendor driver.

Device Manager hides the Ports node entirely when the class is empty. If you do not see Ports (COM & LPT) at all, that is not a fault to troubleshoot — it means no serial device is attached, which is the normal state of a laptop with no USB-to-TTL cable plugged in. For the same reason the PowerShell commands below return nothing rather than an error. Both are working; there is simply nothing to list.

With the cable attached, either open Device Manager (devmgmt.msc) and expand Ports (COM & LPT), or ask PowerShell:

Get-PnpDevice -Class Ports | Select-Object Status,FriendlyName

You are looking for something like USB Serial Port (COM5) or Silicon Labs CP210x USB to UART Bridge (COM5). Note the number. It is a property of the USB port you plugged into, not of the cable, so it will change if you move to a different port — worth plugging into the same port every time and writing the number on the cable.

A quick way to tell “no cable” from “bad driver” without opening Device Manager:

Get-PnpDevice -PresentOnly | Where-Object { $_.Status -ne 'OK' }

A cable that is plugged in but lacking a driver shows up here (and under Other devices with a yellow triangle in Device Manager). An empty result with no COM ports means nothing is attached.

Windows: PuTTY settings

PuTTY is a separate download (choco install putty, or putty.org). Windows has no built-in serial terminal, so you do need it or an equivalent. On the opening Session screen:

Setting Value
Connection type Serial (this swaps the Host Name box for Serial line/Speed)
Serial line COM5 — whatever number you found above
Speed 115200

Then open the Connection → Serial category in the left-hand tree and confirm the rest:

Setting Value
Speed (baud) 115200
Data bits 8
Stop bits 1
Parity None
Flow control None

Flow control is the one that actually bites. PuTTY defaults to XON/XOFF, and the three-wire hookup above has no RTS/CTS lines at all — leave either hardware or software flow control on and the session will look connected but hang, or swallow keystrokes after the first screenful of output. Set it to None explicitly.

Two optional settings that make the session much nicer to live in:

Go back to Session, type a name such as pi-serial under Saved Sessions, and click Save. Reconnecting later is then a double-click, which is what you want mid-demo. Remember to re-save if the COM number changes.

Click Open.

macOS: finding the device node and connecting with screen

There is no PuTTY here and none is needed — screen is preinstalled and its defaults are already 8N1 with no flow control, so the entire settings table above collapses into one command.

Enabling the UART from macOS. Put the SD card in a reader; the FAT bootfs partition automounts:

echo 'enable_uart=1' >> /Volumes/bootfs/config.txt
diskutil eject /Volumes/bootfs

Eject properly rather than pulling the card — a half-flushed config.txt is a confusing way to lose an afternoon. macOS also scatters ._* AppleDouble files and a .Spotlight-V100 directory onto the FAT volume; these are harmless to the Pi’s boot, but dot_clean /Volumes/bootfs tidies them if they bother you.

Drivers. FTDI cables work with no install at all. For CP210x, check first and install Silicon Labs' DriverKit package only if nothing shows up — do not install a kext preemptively on Apple Silicon. PL2303 remains the one to avoid.

Finding the node. List the callout devices before and after plugging the cable in:

ls /dev/cu.*

You are looking for a new entry such as /dev/cu.usbserial-A50285BI or /dev/cu.SLAB_USBtoUART.

Use cu.*, never tty.*. Both nodes exist for the same cable, and this is the single most common way to lose time on macOS: opening /dev/tty.usbserial-… blocks waiting for carrier detect, which a three-wire hookup never asserts, so the command just sits there looking like dead hardware. The cu. (“callout”) node does not wait.

Connect:

screen /dev/cu.usbserial-A50285BI 115200

Exiting screen is the other thing worth knowing before you start rather than after: Ctrl-A then K, then y to confirm. Note that Ctrl-A is also “beginning of line” in the remote shell — to send a literal one through to the Pi, press Ctrl-A then A.

If you would rather not fight screen’s key bindings, brew install picocom and then picocom -b 115200 /dev/cu.usbserial-A50285BI; it exits with Ctrl-A Ctrl-X and survives the cable being unplugged and replugged more gracefully.

What you should see

Closing the PuTTY window, or killing screen, does not log you out — the getty session stays open on the Pi. Type exit first, or the next person to connect lands in your shell.

Troubleshooting

Symptom Likely cause
Nothing at all, even on boot enable_uart=1 missing from config.txt, or TX/RX not swapped between cable and Pi
Garbage or random high-bit characters Wrong speed — confirm 115200, not 9600
Text appears but typing does nothing Cable TX not landing on pin 10, or flow control left on
Session freezes after a screen of output Flow control — set it to None
Unable to open connection to COM5 Another program holds the port, or the cable was unplugged and re-enumerated on a new number
macOS: blank window, unresponsive, no boot log Opened /dev/tty.* instead of /dev/cu.*
macOS: Resource busy An earlier screen still holds the port — screen -ls, then screen -X -S <id> quit
macOS: ssh to .local hangs then times out Terminal lacks Local Network permission (Privacy & Security → Local Network)

This gets a console but not scp or gdbserver, so it complements the Ethernet route rather than replacing it.

Sources