Support for Raspberry Pi Pico W with Wireless
efectn opened this issue ยท 42 comments
Pico W has been released today with included wirless chip. It would be great if tinygo supports it. It uses CYW43439 as wireless chip.
https://www.raspberrypi.com/news/raspberry-pi-pico-w-your-6-iot-platform/
Hmm?
Maybe we need PIO support first?
https://github.com/raspberrypi/pico-sdk/tree/master/src/rp2_common/cyw43_driver
I've got my hands on one. Using the Pico target in tinygo does the basics (as you'd expect):
- UART is functional (GPIO 0 / GPIO 1)
- Blinky is not functional (because on-board LED is via the wireless chip and GPIO25 is not an LED)
Wireless (obviously) not tested.
To properly support will need at least:
- A new target (
picow
?) - Figure what to do about on-board LED (if anything), given it's not a pico GPIO
- Add support for the wireless chipset
If this is worked on, and requires someone to test, I'm more than happy to commit some time to test on a Pico W.
If this is worked on, and requires someone to test, I'm more than happy to commit some time to test on a Pico W.
Ready to participate in testing as well
I've got a couple of Pico W here as well. Ready to help testing
Is someone already working on this?
Guys anyone working on this? I think the supply chain issues at least for Pico W seems to be resolved as they are available everywhere now.
Does the introduction of the Pico W warrant a separate page from the current Pico page (https://tinygo.org/docs/reference/microcontrollers/pico/)?
Seems that still the Wifi module of raspberry pi pico is not supported.
Any plans to implement this?
is it plan to be supported this year ?
Like @sago35 said, PIO would go a long way to having wifi as envisioned by the Raspberry Pi engineers since the CY43's SPI data in and out are shared by pin 24 of the pico, so peripheral SPI is not a possibility. I'm out of my element when it comes to PIO, very excited about it (bought a logic analyzer recently with 500MHz sampling for PIO debugging in future).
The other option is using software SPI, which I will try to test out the pico W once I get back from vacations and settled in. That may take while to get working, if at all possible- that depends on how far the software spi can go and what the cy43 interface can tolerate.
I've documented helpful info in a repo for anyone interested in maybe helping out https://github.com/soypat/cyw43439. I've bought 10 of these Pico W's with hopes that wifi will work on it in the near future, for some definition of "near".
Little update. Work on the aforementioned repo is coming along nicely. I've managed to communicate with the CYW43439 and read from its general purpose registers. I have but one blocker: The gSPI to communicate via the backplane interface. I have replicated the backplane communications the best I can to my knowledge. The I/O of my implementation and the pico-sdk looks identical when looking at the verbose log of gSPI communications but I keep getting data-unavailable status on backplane writes. I must have a off-by-one error somewhere.
To solve this as fast and painless as possible I have fast tracked the delivery of a new logic analyzer I bought a couple months ago. The one I'm using now is a Logic Saleae 8 which has 100MHz sampling frequency which is too low to debug the pico-sdk's implementation of the gSPI (they use PIO). The new logic analyzer arrives 6th of March according to DHL. As soon as I get it and my buddy @trippas at work solders some test leads to the pico-w's<->CYW43439 SPI bus I'll be ready to tackle this problem again.
If we're being optimistic I might have it by this months end. But who knows really. I'd really like to have it early march for some things I want to test at work...
We should also get the Netdev2 #3452 PR ready by then which needs some more work
Supporting Pico W should also lead a long way to supporting Pimoroni Badger 2040 W, which is basically a Badger 2040 with a Pico W and a few other tweaks.
Seeing I gave a few bold statements in my last comment I think a follow up is deserving.
The difficulty of porting the wifi driver was more than I had imagined. Not because the act of porting is hard but because small mistakes can cost a lot of time. Most of the time I have spent developing can be traced to several !=
that should have been a ==
in code, or a conversion of a integer that should be LittleEndian, not BigEndian.
That said, today I've reached a major milestone. I have succesfully initialized the on-board wifi chip and controlled the GPIOs on it- which means now users are able to turn the Pico W's LED on and off via TinyGo code.1 This also validates most of the IO of the wifi chip.
What remains is implementing the wifi logic. This means porting over lots of functions which make use of the already ported IO. I'll be taking a short break from developing this for now. If anyone is interested in helping out in the effort please reach out at the Gophers Slack .2
Footnotes
-
Link to the blinky example. Note: The initialization duration is around 20 seconds. โฉ
-
https://invite.slack.golangbridge.org/, find me as Patricio Whittingslow or on the #tinygo-dev channel. โฉ
To solve this as fast and painless as possible I have fast tracked the delivery of a new logic analyzer I bought a couple months ago. The one I'm using now is a Logic Saleae 8 which has 100MHz sampling frequency which is too low to debug the pico-sdk's implementation of the gSPI (they use PIO).
Probably useless advice by now, but the way I usually solve this is by lowering the clock speed. Usually there is a single place where the clock speed is controlled. I would assume there is something similar for PIO.
(I have a Logic 4 which has a sample rate of 25MHz, so can read up to 8MHz or so).
I did take a look around the code but it was not immediately apparent where i could tinker with the baud. Ended up using it as an excuse to get myself my very own logic analyzer ๐
Dropped a bounty for the implementation of Wifi with the pico, was much more than what I could take on. Apparently also needs a lwip
like library which I'm unfamiliar with implementations in Go.
Dropped a bounty for the implementation of Wifi with the pico, was much more than what I could take on. Apparently also needs a
lwip
like library which I'm unfamiliar with implementations in Go.
@soypat What do you have working currently and where can that be found? It might make it easier to scope out what is missing. I think it's unlikely that we will have an implementation of lwip
in go, but maybe we can wrap it. I'm not sure if tinygo is wrapping other C libraries currently?
Edit: Seems like it's doable making the initial wrappers: https://github.com/xlab/c-for-go
@cmol It's all in the cyw43439 repo. Issue has more information here: soypat/cyw43439#1.
lwip stand-in can be brought to Go via one of the following:
- Generating wrappers that bind C code to Go, though this may prove hard if lwip expects external functions to be linked in
- Porting lwip via a tool like ccgo.
- Porting lwip manually.
- Writing a Go ip/tcp handler from scratch. This can prove hard. I tried a while back and got thus far: ether-swtch. Note: Does not work too well, when it does.
I am not sure which way would be the best. I'm far from being a Cgo power user. I usually port stuff manually when it's small enough. lwip
is 88k lines long... So judging from this data, I think rewriting lwip is out of the question.
But- could it be rewritten from scratch? 88k sounds like a lot but keep in mind we can avoid a lot of complexity in TCP if we stick to the basics. My ether-swtch
implementation was once working for the ENC28J60 which let me host http pages on an arduino uno back in the day and thats only 750 lines of code (excluding tests). What's more, there's a rust implementation of TCP in just under 750 lines of code (what a coincidence!), and there's a video series howing how the programmer did it.
So lwip might seem scary- but I think the TCP/ethernet marshaller could prove to be the easiest part of this whole shenanigan since it could be kept as simple as possible and it would be really easy to test (no need for hardware; test on your PC/laptop).
@soypat I think my biggest worry with a rewrite is supporting a full IPv4 and IPv6 stack including all the DHCP / SLAAC stuff. I'm not sure how lwip
works though, so the idea of generating wrappers might sound easier than it is.
I'll do some reading on lwip
to see if it's possible to wrap it (my guess is that the python implementation does that).
Honestly, Ethernet, IPv6 and UDP would likely be the simple start since we would not need connections or handshakes or anything, but wrapping a well know and maintained lib is likely better in the long run.
LwIP
is a bit of PITA, but it supports three different levels of abstraction - the lowest being a threadless callback model.
I'd also suggest looking at what Earle Philhower did for the Arduino adaptation. He built a shim on top of the CYW43 library:
Honestly, Ethernet, IPv6 and UDP would likely be the simple start since we would not need connections or handshakes or anything, but wrapping a well know and maintained lib is likely better in the long run.
Wrapping lwip would be very nice. That said, as far as the bounty goes I'd want focus hard on having a solid ethernet interface on the CYW43439, even if it means incomplete TCP support. This would be the first step to having good TCP support- if the ethernet layer is shaky then the TCP can't possibly perform well.
I guess we could organize a bounty to get TCP/UDP/IP up to speed after the current one ends.
@soypat happy to contribute testing or cgo translation if needed - otherwise Iโm cheering your work from the sidelines
As I did the whole worst-case-of-everything-run the PicoW != Pico
just somewhat freaked me out.
Running NixOS and neovim rendered things not working at all:
tinygo build --target=pico
producinghello.elf
outputtinygo flash --target=pico
was unable to shift it over the Pico on NixOS due to whatevertinygo flash --work --target=pico && sudo cp <WORK/main.uf2> ~/usb
did the trick, but no blinking LED
As @kenbell stated correctly the LED is not GP25
but WL_GPIO0
on the embedded Infineon 43439 wireless chip. In fact I wasn't able to find GP25
on the pinout documentation, thus it may be used for the wireless chip: See https://datasheets.raspberrypi.com/picow/PicoW-A4-Pinout.pdf
So rewriting the Blinky to GP22 with an external LED and tinygo flash --work --target=pico && sudo cp <WORK/main.uf2> ~/usb
finally made the LED blink.
For the flashing I'll bring the setup to GopherConEU and we can figure it out.
For the Blinky I'd love to see the --target=picow
support with machine.LED := machine.WL_GPIO0
for beginners, so even if things sum up they won't get upset for the non-functional sample.
@marcofeltmann See the cyw43439 library for a blinky on the pico w. We're working on this with @scottfeldman, still needs some more work before it is ready to integrate with tinygo.
@soypat howโs the TCP + ethernet work going? Need coding help? Is the bounty resolved?
Hey @Notargets! I've put a pause on the TCP+Ethernet work (dgrams) for now until the ethernet interface is up on the CYW43439 which would greatly help me debug it since working with linux TUN/TAP did not work too well for me. If you want to pick it back up feel free to do so, just be aware as soon as cyw43439 driver is finished I plan on going back to work on it again. Think it'd be great to have lots of people working on it since there is separation of concerns between different protocol implementations, we can work in parallel on TCP/IP and ARP without stepping on our toes. So far dgrams consists of decoding/encoding of tcp/ip packets and a non-functional TCP/IP stack.
One thing to note- to facilitate development we will be moving dgrams into its own subpackage withing cyw43439 so we don't have to manage go versions between two separate repos, which I feel would complicate things. If you are willing to help out with TCP/IP, DHCP or any other interesting internet protocol feel free to file an issue under cyw43439/issues so we can plan the development out!
Time for a little update about your favorite microcontroller's wifi chip Go driver!
Switching reference - Bye C, hello Rust
Thanks to some of @scottfeldman 's key insight we've switched References. Initially we were taking inspiration from Damien's cyw43-driver + Pico-SDK mix. This began taking a toll after hitting a certain project size as the source code was hard to follow at times.
It's been barely more than a week since I heard Scott mention the Rust CYW43439 implementation at (embassy-rs) as being "really clean". After taking a look myself it did not take me long to convince myself to switch over to using the rust implementation as the reference- and I can't stress enough how much more easier and pleasant rust is to read than C. Even was able to find a bug in the rust implementation.
You can now find the latest rewrite under the cyrw directory in the repo (stands for CY ReWrite)
Rusty blink
One can run a blinky program using the new version. There's a test program that displays this functionality in action.
Wifi
Since we are switching references we are leaving a lot of progress on the original driver. We had even got the C driver to connect to a wifi network a couple weeks ago!
That said, it's been barely a week with the rust rewrite and we've already made it farther than 2 months in with the original driver. I think we're making the right choice. The new implementation is much easier to follow and has a readable reference, which in itself might not sound like much but I feel there is huge value in it. The rust version is active and is still receiving updates. I've talked with the lead maintainer and the guy is super helpful, even with the most basic of questions. I've asked about everything from basics on generic type instantiation on rust structs all the way to the internals of embassy-rs, and always gotten a quick, helpful response over at the matrix.to channel.
I think there's room to work hand in hand with the rust community to make the best of the Pico W hardware!
Hello... C? Again?!
So... we might not be completely done with C. The rust implementation is great and all but it makes heavy use of asynchronous APIs for Ioctl calls. This can be hard to follow at times since the driver initialization stops being linear. The Rust reference also sometimes takes shortcuts (rarely) and leaves room for questions... in that case we can always recur to the source- the wifi-host-driver.
This is the mother of all drivers- the rust implementation uses it as reference. If I'm not mistaken, even Damien's cyw43-driver is based off the WHD. I've occasionally fallen back to reading the WHD when I'm unsure on how the rust version is doing things.
Vacations / Help wanted
I'm taking a 2 week vacation starting 26 of August and coming back 10 of September. That means that development will slow down by some- it also means things will be more stable.
Dear reader, if you stand to benefit greatly from this driver like myself or anyone who writes embedded Go code and loves cheap, flexible, general purpose compute microcontrollers- consider lending a hand! |
---|
Things that help:
- Reading through the code and comparing with the reference for any differences. This is without a doubt the most helpful task one can do:
- Example: here's
bus.go
- if one opens this file one can find links to the reference implementations. Most Go functions have similar, if not identical names to their rust counterpart (yes, even snake_case identifiers!)
- Example: here's
- Porting code! Not all functions have been written. There's some low hanging fruits, like AP functions such as
start_ap
.- If you plan on contributing code please join the Gophers Slack and talk to me or Scott Feldman. Alternatively file an issue at the cyw43439 repo.
Closing remarks
This last month has been hard- but switching from reading C to reading rust has given me a second wind of sorts. From what I can tell we are very near from being able to send and receive ethernet packets with the rust rewrite.
- By looking at the data on the bus there seems to be differences between what we read over the wlan interface. This could be due to non-reproducibility... that said we are not reading Async packets correctly, so there's likely a bug somewhere there. This could be happening anywhere between
wlan_read
andcheck_status
... but that's anyone's guess
I think the bug above may very well be the last "hard" bug left (if it really is the last bug behind ioctl calls). The rest of the rust driver is pretty straightforward and simple, relying on the ioctl calls as the backbone of all communications.
Time for a little update-
It's alive
After a very succesful bughunt led by @scottfeldman, we now have a fully functional Ethernet interface working on the Pico W #14-- both as a wifi client and with access point functionality (#18) (again, thanks @scottfeldman).
We have implemented our own lightweight IP stack with working DHCP over UDP support (17) and have a working TCP socket (#19) implementation which still needs stateful logic.
What does this mean?
It is not done. This still cannot be used for solving problems as the APIs are still experimental and subject to change. What can be done is testing! @cmol has been lending a hand by running the DHCP client example (info below) and actually found that the same program that does not crash for us crashes for him. It'd be interesting to see if others can start testing with their own Tinygo+PicoW setup to see if there are any latent bugs.
Steps going forward
I'm quite busy the coming weeks preparing for Gophercon 2023 where I'll be giving a talk so it might be a couple weeks before I can resume work.
@scottfeldman will be working on adding more functionality to the Pico W, so that would include like Wifi Scan and any remaining AP/STA functionality we are missing.
Click to see more on the DHCP example
More on the DHCP example
NOTE: You have to add a secret.go
file based on the template file in the directory with your wifi credentials for the program to work!
Flash the DHCP example by running
tinygo flash -monitor -target pico -stack-size=8kb -size short ./examples/dhcp/
This will attach your terminal to the pico W and you'll see a lot of logs printed out. Look for the one that says:
========
DHCP done, your IP: 192.168.1.145
========
After that the Pico W will turn on it's LED every time it receives a packet. You can try running ping
linux/windows utility from terminal to turn on the LED:
$ ping 192.168.1.145
PING 192.168.1.145 (192.168.1.145) 56(84) bytes of data.
From 192.168.1.147 icmp_seq=1 Destination Host Unreachable
From 192.168.1.147 icmp_seq=2 Destination Host Unreachable
From 192.168.1.147 icmp_seq=3 Destination Host Unreachable
From 192.168.1.147 icmp_seq=5 Destination Host Unreachable
From 192.168.1.147 icmp_seq=6 Destination Host Unreachable
^C
--- 192.168.1.145 ping statistics ---
7 packets transmitted, 0 received, +5 errors, 100% packet loss, time 6143ms
pipe 4
The pico W will not respond to ping packets, but will flash it's LED.
This is incredible progress thank you so much @soypat and @scottfeldman ๐
First off: Amazing work! This is a dream come true.
I've done the least I can do and tried the DHCP sample.
Works for me! ๐ฅณ
Logs & Details
~/w/cyw43439 (main)> tinygo version
tinygo version 0.29.0 linux/amd64 (using go version go1.20.7 and LLVM version 16.0.6)
~/w/cyw43439 (main)> tinygo flash -monitor -target pico -stack-size=8kb -size short ./examples/dhcp/
code data bss | flash ram
368508 2832 6440 | 371340 9272
Connected to /dev/ttyACM0. Press Ctrl-C to exit.
starting program
INFO Init:start slog.Level=DEBUG
DEBUG read back bus ctl got=177
DEBUG current bus ctl00010030writing:000300b1 got:feedbead
DEBUG flashing firmware chip_id=43439 fwlen=230321
DEBUG bp_write addr=0 last16=442030312d39353536366436612d30f0
DEBUG bp_write:done status=no status
DEBUG flashing nvram
DEBUG bp_write addr=523540 last16=006274635f6d6f64653d31000000636f
DEBUG bp_write:done status=no status
DEBUG core up
CY43 hndarm_armr addr: 0x18003000, cr4_idx: 0
CY43 000000.001
CY43 RTE (SDIO-CDC) 7.95.62 (b03806e CY) on BCM43439 r5 @ 37.4/81.6/81.6MHz
CY43 000000.003 sdpcmdcdc0: Broadcom SDPCMD CDC driver
CY43 000000.008 reclaim section 0: Returned 46156 bytes to the heap
CY43 000000.012 wlc_bmac_info_init: host_enab 1
CY43 000000.037 wl0: wlc_ampdu_tx_set: AGG Mode = MAC+Ucode txmaxpkts 64 txmaxpkts_agg 0
CY43 000000.048 wl0: wlc_channels_commit: no valid channel for "#n" nbands 1 bandlocked 0
CY43 000000.053 wl0: Broadcom BCM43439 802.11 Wireless Controller 7.95.62 (b03806e CY)
CY43 000000.055 TCAM: 256 used: 77 exceed:0
CY43 000000.058 reclaim section 1: Returned 86104 bytes to the heap
DEBUG base init done
DEBUG initControl clm_len=4752
DEBUG sendIoctl kind=2 cmd=SET_VAR len=1044
DEBUG rx len=256 hdr=asyncev
ERROR rxEvent err=BDC header invalid length plen=244 bdc=&{Flags:179 Priority:68 Flags2:104 DataOffset:192} event=&{EthHeader:{Destination:[0 0 0 0 0 0] Source:[0 0 0 0 0 0] SizeOrEtherType:0} EventHeader:{Subtype:0 Length:0 Version:0 OUI:[0 0 0] UserSubtype:0} Message:{Version:0 Flags:0 EventType:SET_SSID Status:0 Reason:0 AuthType:0 DataLen:0 Addr:[0 0 0 0 0 0] IFName:[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] IFIdx:0 BSSCfgIdx:0}}
ERROR rx err=BDC header invalid length
DEBUG tryPoll:ignore_spurious err=BDC header invalid length
DEBUG rx len=256 hdr=asyncev
ERROR rxEvent err=BDC header invalid length plen=244 bdc=&{Flags:179 Priority:68 Flags2:104 DataOffset:192} event=&{EthHeader:{Destination:[0 0 0 0 0 0] Source:[0 0 0 0 0 0] SizeOrEtherType:0} EventHeader:{Subtype:0 Length:0 Version:0 OUI:[0 0 0] UserSubtype:0} Message:{Version:0 Flags:0 EventType:SET_SSID Status:0 Reason:0 AuthType:0 DataLen:0 Addr:[0 0 0 0 0 0] IFName:[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] IFIdx:0 BSSCfgIdx:0}}
ERROR rx err=BDC header invalid length
DEBUG tryPoll:ignore_spurious err=BDC header invalid length
DEBUG rx len=1072 hdr=ctl
DEBUG sendIoctl kind=2 cmd=SET_VAR len=1044
DEBUG rx len=1072 hdr=ctl
DEBUG sendIoctl kind=2 cmd=SET_VAR len=1044
DEBUG rx len=1072 hdr=ctl
DEBUG sendIoctl kind=2 cmd=SET_VAR len=1044
DEBUG rx len=1072 hdr=ctl
DEBUG sendIoctl kind=2 cmd=SET_VAR len=676
DEBUG rx len=704 hdr=ctl
DEBUG clmload:done
DEBUG sendIoctl kind=0 cmd=GET_VAR len=15
DEBUG rx len=259 hdr=ctl
DEBUG sendIoctl kind=2 cmd=SET_VAR len=15
DEBUG rx len=259 hdr=ctl
DEBUG sendIoctl kind=2 cmd=SET_VAR len=10
DEBUG rx len=258 hdr=ctl
DEBUG sendIoctl kind=0 cmd=GET_VAR len=14
DEBUG rx len=258 hdr=ctl
DEBUG MAC mac=28:cd:c1:06:ce:27
DEBUG sendIoctl kind=2 cmd=SET_VAR len=20
DEBUG rx len=256 hdr=ctl
DEBUG sendIoctl kind=2 cmd=SET_ANTDIV len=4
DEBUG rx len=256 hdr=ctl
DEBUG sendIoctl kind=2 cmd=SET_VAR len=15
DEBUG rx len=259 hdr=ctl
DEBUG sendIoctl kind=2 cmd=SET_VAR len=19
DEBUG rx len=259 hdr=ctl
DEBUG sendIoctl kind=2 cmd=SET_VAR len=15
DEBUG rx len=259 hdr=ctl
DEBUG sendIoctl kind=2 cmd=SET_VAR len=46
DEBUG rx len=258 hdr=ctl
DEBUG sendIoctl kind=2 cmd=UP len=0
DEBUG rx len=256 hdr=ctl
CY43 000001.495 wl0: wl_open
DEBUG sendIoctl kind=2 cmd=SET_GMODE len=4
DEBUG rx len=256 hdr=ctl
DEBUG sendIoctl kind=2 cmd=SET_BAND len=4
DEBUG rx len=256 hdr=ctl
DEBUG set_power_management mode=PowerSave
DEBUG sendIoctl kind=2 cmd=SET_VAR len=18
DEBUG rx len=258 hdr=ctl
DEBUG sendIoctl kind=2 cmd=SET_VAR len=15
DEBUG rx len=259 hdr=ctl
DEBUG sendIoctl kind=2 cmd=SET_VAR len=16
DEBUG rx len=256 hdr=ctl
DEBUG sendIoctl kind=2 cmd=SET_VAR len=17
DEBUG rx len=257 hdr=ctl
DEBUG sendIoctl kind=2 cmd=SET_PM len=4
DEBUG rx len=256 hdr=ctl
INFO joinWpa2 ssid=Haakon len(pass)=12
DEBUG sendIoctl kind=2 cmd=SET_VAR len=19
DEBUG rx len=259 hdr=ctl
DEBUG sendIoctl kind=2 cmd=SET_WSEC len=4
DEBUG rx len=256 hdr=ctl
DEBUG sendIoctl kind=2 cmd=SET_VAR len=23
DEBUG rx len=259 hdr=ctl
DEBUG sendIoctl kind=2 cmd=SET_VAR len=31
DEBUG rx len=259 hdr=ctl
DEBUG sendIoctl kind=2 cmd=SET_VAR len=27
DEBUG rx len=259 hdr=ctl
DEBUG sendIoctl kind=2 cmd=SET_WSEC_PMK len=68
DEBUG rx len=256 hdr=ctl
DEBUG sendIoctl kind=2 cmd=SET_INFRA len=4
DEBUG rx len=256 hdr=ctl
DEBUG sendIoctl kind=2 cmd=SET_AUTH len=4
DEBUG rx len=256 hdr=ctl
DEBUG sendIoctl kind=2 cmd=SET_WPA_AUTH len=4
DEBUG rx len=256 hdr=ctl
DEBUG sendIoctl kind=2 cmd=SET_SSID len=36
DEBUG rx len=256 hdr=ctl
DEBUG rx len=258 hdr=asyncev
DEBUG rx len=260 hdr=asyncev
INFO rxEvent:success event=AUTH
DEBUG rx len=259 hdr=asyncev
DEBUG rx len=261 hdr=asyncev
DEBUG rx len=258 hdr=asyncev
DEBUG rx len=260 hdr=asyncev
DEBUG rx len=260 hdr=asyncev
DEBUG rx len=258 hdr=asyncev
INFO rxEvent:success event=SET_SSID
MAC: 28:cd:c1:06:ce:27
CY43 000005.124 wl0: link up (wl0)
Trying DoDHCP
INFO HandleEth dstlen=1500
DEBUG tx len=590
DEBUG rx len=258 hdr=data
DEBUG Stack.RecvEth:start plen=60
DEBUG rx len=258 hdr=data
DEBUG Stack.RecvEth:start plen=60
DEBUG rx len=258 hdr=data
DEBUG Stack.RecvEth:start plen=60
DEBUG rx len=258 hdr=data
DEBUG Stack.RecvEth:start plen=60
DEBUG rx len=364 hdr=data
DEBUG Stack.RecvEth:start plen=342
INFO UDP packet stored plen=300
INFO HandleEth dstlen=1500
DEBUG tx len=590
DEBUG rx len=365 hdr=data
DEBUG Stack.RecvEth:start plen=343
INFO UDP packet stored plen=301
DHCP did not complete, state=2
DEBUG rx len=260 hdr=data
DEBUG Stack.RecvEth:start plen=86
DEBUG rx len=258 hdr=data
DEBUG Stack.RecvEth:start plen=60
DEBUG rx len=258 hdr=data
DEBUG Stack.RecvEth:start plen=60
DEBUG rx len=258 hdr=data
DEBUG Stack.RecvEth:start plen=60
Trying DoDHCP
INFO HandleEth dstlen=1500
DEBUG tx len=590
DEBUG rx len=364 hdr=data
DEBUG Stack.RecvEth:start plen=342
INFO UDP packet stored plen=300
INFO HandleEth dstlen=1500
DEBUG tx len=590
DEBUG rx len=365 hdr=data
DEBUG Stack.RecvEth:start plen=343
INFO UDP packet stored plen=301
INFO HandleEth dstlen=1500
========
DHCP done, your IP: 192.168.16.143
========
finished init OK
INFO GPIOSet wlGPIO=0 value=true
DEBUG sendIoctl kind=2 cmd=SET_VAR len=16
DEBUG rx len=256 hdr=ctl
INFO GPIOSet wlGPIO=0 value=false
DEBUG sendIoctl kind=2 cmd=SET_VAR len=16
DEBUG rx len=256 hdr=ctl
DEBUG rx len=265 hdr=data
DEBUG Stack.RecvEth:start plen=243
DEBUG rx len=258 hdr=data
DEBUG Stack.RecvEth:start plen=92
DEBUG rx len=258 hdr=data
DEBUG Stack.RecvEth:start plen=92
DEBUG rx len=258 hdr=data
DEBUG Stack.RecvEth:start plen=92
INFO GPIOSet wlGPIO=0 value=true
DEBUG sendIoctl kind=2 cmd=SET_VAR len=16
DEBUG rx len=256 hdr=ctl
INFO GPIOSet wlGPIO=0 value=false
DEBUG sendIoctl kind=2 cmd=SET_VAR len=16
DEBUG rx len=256 hdr=ctl
DEBUG rx len=258 hdr=data
DEBUG Stack.RecvEth:start plen=60
INFO GPIOSet wlGPIO=0 value=true
DEBUG sendIoctl kind=2 cmd=SET_VAR len=16
DEBUG rx len=256 hdr=ctl
INFO GPIOSet wlGPIO=0 value=false
DEBUG sendIoctl kind=2 cmd=SET_VAR len=16
DEBUG rx len=256 hdr=ctl
DEBUG rx len=258 hdr=data
DEBUG Stack.RecvEth:start plen=60
INFO GPIOSet wlGPIO=0 value=true
DEBUG sendIoctl kind=2 cmd=SET_VAR len=16
DEBUG rx len=256 hdr=ctl
INFO GPIOSet wlGPIO=0 value=false
DEBUG sendIoctl kind=2 cmd=SET_VAR len=16
DEBUG rx len=256 hdr=ctl
DEBUG rx len=258 hdr=data
DEBUG Stack.RecvEth:start plen=60
INFO GPIOSet wlGPIO=0 value=true
DEBUG sendIoctl kind=2 cmd=SET_VAR len=16
DEBUG rx len=256 hdr=ctl
INFO GPIOSet wlGPIO=0 value=false
DEBUG sendIoctl kind=2 cmd=SET_VAR len=16
DEBUG rx len=256 hdr=ctl
DEBUG rx len=258 hdr=data
DEBUG Stack.RecvEth:start plen=60
INFO GPIOSet wlGPIO=0 value=true
DEBUG sendIoctl kind=2 cmd=SET_VAR len=16
DEBUG rx len=256 hdr=ctl
INFO GPIOSet wlGPIO=0 value=false
DEBUG sendIoctl kind=2 cmd=SET_VAR len=16
DEBUG rx len=256 hdr=ctl
DEBUG rx len=258 hdr=data
DEBUG Stack.RecvEth:start plen=60
INFO GPIOSet wlGPIO=0 value=true
DEBUG sendIoctl kind=2 cmd=SET_VAR len=16
DEBUG rx len=256 hdr=ctl
INFO GPIOSet wlGPIO=0 value=false
DEBUG sendIoctl kind=2 cmd=SET_VAR len=16
DEBUG rx len=256 hdr=ctl
DEBUG rx len=258 hdr=data
DEBUG Stack.RecvEth:start plen=60
INFO GPIOSet wlGPIO=0 value=true
DEBUG sendIoctl kind=2 cmd=SET_VAR len=16
DEBUG rx len=256 hdr=ctl
INFO GPIOSet wlGPIO=0 value=false
DEBUG sendIoctl kind=2 cmd=SET_VAR len=16
DEBUG rx len=256 hdr=ctl
DEBUG rx len=258 hdr=data
DEBUG Stack.RecvEth:start plen=60
INFO GPIOSet wlGPIO=0 value=true
DEBUG sendIoctl kind=2 cmd=SET_VAR len=16
DEBUG rx len=256 hdr=ctl
INFO GPIOSet wlGPIO=0 value=false
DEBUG sendIoctl kind=2 cmd=SET_VAR len=16
DEBUG rx len=256 hdr=ctl
DEBUG rx len=258 hdr=data
DEBUG Stack.RecvEth:start plen=60
INFO GPIOSet wlGPIO=0 value=true
DEBUG sendIoctl kind=2 cmd=SET_VAR len=16
DEBUG rx len=256 hdr=ctl
INFO GPIOSet wlGPIO=0 value=false
DEBUG sendIoctl kind=2 cmd=SET_VAR len=16
DEBUG rx len=256 hdr=ctl
DEBUG rx len=258 hdr=data
DEBUG Stack.RecvEth:start plen=60
INFO GPIOSet wlGPIO=0 value=true
DEBUG sendIoctl kind=2 cmd=SET_VAR len=16
DEBUG rx len=256 hdr=ctl
INFO GPIOSet wlGPIO=0 value=false
DEBUG sendIoctl kind=2 cmd=SET_VAR len=16
DEBUG rx len=256 hdr=ctl
DEBUG rx len=260 hdr=data
DEBUG Stack.RecvEth:start plen=86
INFO GPIOSet wlGPIO=0 value=true
DEBUG sendIoctl kind=2 cmd=SET_VAR len=16
DEBUG rx len=256 hdr=ctl
INFO GPIOSet wlGPIO=0 value=false
DEBUG sendIoctl kind=2 cmd=SET_VAR len=16
DEBUG rx len=256 hdr=ctl
Go @soypat! Nice job on the DEBUG output, love it. slog rocks.
TCP is working with reconnection! soypat/cyw43439#24 (comment)
To run clone the repo and edit the examples/tcpserver/secrets.go.template
file:
- Remove the
.template
part of the extension so it's a normal Go file - Add the SSID of your network and the password for it.
git clone git@github.com:soypat/cyw43439.git
cd cyw43439
# Edit the `secrets.go.template` file betwixt these steps!
tinygo flash -target=pico -opt=1 -stack-size=8kb -size=short -monitor ./examples/tcpserver/
Please, please PLEASE load all issues related to the wifi chip or the TCP stack in the cyw43439 issue tracker or the seqs issue tracker!
I'm leaving a program to help y'all test the TCP stack:
TCP Client (click to show)
// Modify IP below to match the one assigned by DHCP!
package main
import (
"fmt"
"net"
"net/netip"
"time"
)
func main() {
const server = "192.168.1.120:1234" // Edit this to match the result of the DHCP Request. Leave the :1234 there though!
raddr := netip.MustParseAddrPort(server)
conn, err := net.DialTCP("tcp", nil, net.TCPAddrFromAddrPort(raddr))
if err != nil {
panic(err)
}
// wait a second for SYN/ACK stuff.
time.Sleep(time.Second)
dd := make([]byte, 1024)
go func() {
for {
time.Sleep(time.Second)
n, err := conn.Read(dd)
if err != nil {
fmt.Println("rerr", err.Error())
}
if n > 0 {
fmt.Printf("read %q\n", string(dd[:n]))
}
}
}()
for {
_, err = conn.Write([]byte("hello"))
if err != nil {
fmt.Println("werr", err.Error())
}
time.Sleep(time.Second)
}
}
Gave it a quick try, echo works! Amazing stuff!
Logs
...
start listening on: 192.168.16.143:1234
time=1970-01-01T00:00:18.877Z level=DEBUG msg=ARP:recv op=2
time=1970-01-01T00:00:18.878Z level=DEBUG msg=ARP:send isReply=true
time=1970-01-01T00:00:36.064Z level=DEBUG msg=ARP:recv op=2
time=1970-01-01T00:00:36.065Z level=DEBUG msg=ARP:send isReply=true
time=1970-01-01T00:00:36.115Z level=DEBUG msg=TCP:recv opt=20 ipopt=0 payload=0
time=1970-01-01T00:00:36.116Z level=INFO msg=TCP:rx-statechange port=1234 old=SynSent new=SynRcvd rxflags=[SYN]
time=1970-01-01T00:00:36.117Z level=DEBUG msg=TCP:send plen=54
time=1970-01-01T00:00:36.170Z level=DEBUG msg=TCP:recv opt=0 ipopt=0 payload=0
time=1970-01-01T00:00:36.170Z level=INFO msg=TCP:rx-statechange port=1234 old=SynRcvd new=Established rxflags=[ACK]
time=1970-01-01T00:00:42.452Z level=DEBUG msg=TCP:recv opt=0 ipopt=0 payload=3
time=1970-01-01T00:00:42.453Z level=DEBUG msg=TCP:send plen=54
time=1970-01-01T00:00:42.455Z level=DEBUG msg=TCP:send plen=57
time=1970-01-01T00:00:42.507Z level=DEBUG msg=TCP:recv opt=0 ipopt=0 payload=0
time=1970-01-01T00:00:42.508Z level=DEBUG msg=TCP:send plen=54
time=1970-01-01T00:00:48.486Z level=DEBUG msg=TCP:recv opt=0 ipopt=0 payload=13
time=1970-01-01T00:00:48.487Z level=DEBUG msg=TCP:send plen=54
time=1970-01-01T00:00:48.488Z level=DEBUG msg=TCP:send plen=67
time=1970-01-01T00:00:48.541Z level=DEBUG msg=TCP:recv opt=0 ipopt=0 payload=0
time=1970-01-01T00:00:48.542Z level=DEBUG msg=TCP:send plen=54
Tested the HTTP example on my Pico W and it's working nicely. This is great stuff @soypat !
Bluetooth is still not being worked on at the time being.
It it safe to say that the most straightforward option for implementing bluetooth on the pico would be to do something similar as what has been done with the nordic semi bluetooth chipsets in tinygo/bluetooth
where we just wrap an existing certified bluetooth stack's C headers? If I recall right, the pi foundation indicated that they negotiated a license deal with the bluetooth stack they use for the pico W so that pi pico owners (business or individual) don't need to purchase a separate license for commercial use, so I don't see much of a disadvantage of wrapping that certified stack.
I might be working on a project in the near-ish future on the pico W where I'd like to get tinygo with bluetooth running on the board, so if that does happen, I'd be happy to contribute back any work if I do continue down that route.
@Kytech The first step would be to implement the low level unexported functions in cyw43439 for bluetooth control. We can take inspiration from cyw43 repository to implement these since I don't think there's a Rust version yet ๐ . I'm not familiar enough with the tinygo/bluetooth internals, but I guess we could then choose to wrap the API with it?
@soypat makes sense. I am thinking we'd wrap the low-level implementation with the high-level APIs exposed by TinyGo, based on what I've seen from the tinygo/bluetooth repo's codebase. I'll have to see if there's any progress on a rust version by the time I am looking into this. Thanks for the info!
Why is the Wifi not added to this repo as native support. Reading above comments - the wifi does work (example - https://github.com/soypat/cyw43439/blob/main/examples/mqtt/mqtt.go) but it's still not part of TinyGo?
Although it works wifi is still a work in progress. There's a lot ot figure out when dealing with a link layer device with Go, unlike other existing devices which work at the transport layer. It turns out it is surprisingly hard to make it work with the standard library API.