nerves-project / nerves_system_rpi0

Base Nerves system configuration for the Raspberry Pi Zero and Zero W
Apache License 2.0
100 stars 50 forks source link

Bluetooth support for Raspberry Pi Zero W #224

Closed angrycandy closed 3 years ago

angrycandy commented 3 years ago

Environment

Elixir 1.12.0 (compiled with Erlang/OTP 24)

* Nerves environment: (`mix nerves.env --info`)

==> nerves ==> firmware |nerves_bootstrap| Environment Package List

Pkg: nerves_toolchain_ctng Vsn: 1.8.3 Type: toolchain_platform BuildRunner: {nil, []}

Pkg: nerves_toolchain_armv6_nerves_linux_gnueabihf Vsn: 1.4.2 Type: toolchain BuildRunner: {Nerves.Artifact.BuildRunners.Local, []}

Pkg: nerves_system_br Vsn: 1.16.0 Type: system_platform BuildRunner: {nil, []}

Pkg: nerves_system_rpi0 Vsn: 1.16.0 Type: system BuildRunner: {Nerves.Artifact.BuildRunners.Local, []}

|nerves_bootstrap| Loadpaths Start

Nerves environment MIX_TARGET: rpi0 MIX_ENV: dev

|nerves_bootstrap| Environment Variable List target: rpi0 toolchain: /home/bfw/.nerves/artifacts/nerves_toolchain_armv6_nerves_linux_gnueabihf-linux_x86_64-1.4.2 system: /home/bfw/.nerves/artifacts/nerves_system_rpi0-portable-1.16.0 app: .

|nerves_bootstrap| Loadpaths End


* Additional information about your host, target hardware or environment that
  may help

NA

### Current behavior

There is no UART for Bluetooth.

iex(1)> cmd "ls /dev/ttyA*" /dev/ttyAMA0 0 iex(2)>


### Expected behavior

https://hexdocs.pm/blue_heron_transport_uart/readme.html

> BlueHeron supports UART-based Bluetooth modules. Currently, this ONLY includes the Cypress Semiconductor BCM43438. This part is on the Raspberry Pi Zero W and the Raspberry Pi 3 B. It is NOT on the 3 B+.

https://hexdocs.pm/nerves_system_rpi0/readme.html#linux-kernel-configuration-notes
> In Networking support, disable Amateur Radio support, CAN bus subsystem, IrDA subsystem, **Bluetooth**, WiMAX, Plan 9, and NFC. (TBD - this may be too harsh, **please open issues** if you're using any of these and it's the only reason for you to create a custom system.)
fhunleth commented 3 years ago

Try "/dev/ttyS0".

I don't use BLE, but I've been told that people have gotten it working. Some people modify the config.txt to swap ttyAMA0 and ttyS0 for higher speeds.

angrycandy commented 3 years ago

"/dev/ttyS0" worked for me. Logger.add_backend(BlueHeron.HCIDump.Logger) shows:

No. Time    Source  Destination Protocol    Length  Info
1   0.000000    host    controller  HCI_CMD 4   Sent Reset
2   0.000000    controller  host    HCI_EVT 7   Rcvd Command Complete (Reset)
3   0.000000    host    controller  HCI_CMD 4   Sent Read Local Version Information
4   0.000000    controller  host    HCI_EVT 15  Rcvd Command Complete (Read Local Version Information)
5   0.000000    host    controller  HCI_CMD 4   Sent Read Local Name
6   0.000000    controller  host    HCI_EVT 255 Rcvd Command Complete (Read Local Name)

I don't understand swap ttyAMA0 and ttyS0 for higher speeds. I'm using ttyS0 at 115_200. Is ttyAMA0 faster?

fhunleth commented 3 years ago

I'm glad it's working! Let's not change anything. :)

Here's the story on the UART. There are two UARTs on the Raspberry Pis, ttyAMA0 and ttyS0. On most hardware UART connections are fixed, but the Raspberry Pi is special and you can switch whether ttyAMA0 is connected to the UART pins or Bluetooth. ttyS0 is connected to the other one. This is done with the config.txt. Unfortunately, the UARTs aren't the same so there are tradeoffs. ttyAMA0 is the better one. Here's more info: https://www.raspberrypi.org/documentation/computers/configuration.html#pl011-and-mini-uart.

Unless you run into performance issues with whatever you're doing with Bluetooth, I'd stick with ttyS0.

angrycandy commented 2 years ago

I'm adding more information here to support #227 using An example scanner to collect Manufacturer Specific Data.

Raspberry Pi Zero W results:

iex(1)> {:ok, pid} = BlueHeronScan.start_link(:uart, %{device: "ttyS0"})
{:ok, #PID<0.10860.0>}
iex(2)> state = :sys.get_state(pid)
%{
  ctx: #BlueHeron.Context<0.10861.0>,
  devices: %{
    4753574963174 => %{
      272 => <<64, 10, 1, 0>>,
      :name => "Bose AE2 SoundLink",
      :time => ~U[2021-09-27 14:48:25.778174Z]
    },
    48660401950223 => %{
      784 => <<64, 16, 2, 48>>,
      :name => "LE-Bose Revolve SoundLink",
      :time => ~U[2021-09-27 14:48:25.658670Z]
    },
    110946934216995 => %{
      117 => <<66, 4, 1, 128, 102, 100, 231, 216, 154, 89, 35, 102, 231, 216,
        154, 89, 34, 1, 62, 0, 0, 0, 0, 0>>,
      :time => ~U[2021-09-27 14:48:25.873323Z]
    },
    181149778439893 => %{
      1 => <<1, 1, 4, 28, 196, 90>>,
      :name => "GVH5102_EED5",
      :time => ~U[2021-09-27 14:48:26.032518Z]
    },
    181149781445015 => %{
      name: "ihoment_H6182_C997",
      time: ~U[2021-09-27 14:48:26.059225Z]
    },
    246390811914386 => %{
      60552 => <<0, 97, 10, 12, 22, 100, 2>>,
      :name => "Govee_H5074_F092",
      :time => ~U[2021-09-27 14:48:24.429195Z]
    }
  },
  ignore_cids: [6, 76],
  working: true
}
iex(3)> BlueHeronScan.ignore_cids(pid, MapSet.new([6, 76, 117, 784]))
{:ok, #MapSet<[6, 76, 117, 784]>}
iex(4)> BlueHeronScan.clear_devices(pid)
:ok
iex(5)> state = :sys.get_state(pid)
%{
  ctx: #BlueHeron.Context<0.10861.0>,
  devices: %{
    4753574963174 => %{
      272 => <<64, 10, 1, 0>>,
      :name => "Bose AE2 SoundLink",
      :time => ~U[2021-09-27 14:48:46.192324Z]
    },
    181149778439893 => %{
      1 => <<1, 1, 4, 28, 196, 90>>,
      :name => "GVH5102_EED5",
      :time => ~U[2021-09-27 14:48:46.287562Z]
    },
    181149781445015 => %{
      name: "ihoment_H6182_C997",
      time: ~U[2021-09-27 14:48:47.139443Z]
    },
    246390811914386 => %{
      60552 => <<0, 94, 10, 11, 22, 100, 2>>,
      :name => "Govee_H5074_F092",
      :time => ~U[2021-09-27 14:48:45.477457Z]
    }
  },
  ignore_cids: #MapSet<[6, 76, 117, 784]>,
  working: true
}
iex(6)> BleAdMfgData.print(state.devices)
[
  ["26.5˚C 56.4% RH 100%🔋", "Govee_H5074_F092"],
  ["27.0˚C 50.8% RH 90%🔋", "GVH5102_EED5"]
]
iex(7)> BlueHeronScan.disable(pid)
:ok
iex(8)> BlueHeronScan.clear_devices(pid)
:ok
iex(9)> state = :sys.get_state(pid)
%{
  ctx: #BlueHeron.Context<0.10861.0>,
  devices: %{},
  ignore_cids: #MapSet<[6, 76, 117, 784]>,
  working: true
}
iex(10)> BlueHeronScan.enable(pid)
:ok
iex(11)>