Compare commits

..

173 Commits

Author SHA1 Message Date
soypat 85a28fb937 delay addition of extra error types until verifiably needed 2024-02-24 23:43:12 -03:00
Patricio Whittingslow 9b74ecbc36 Delete nets/example_test.go 2024-01-10 09:11:23 -03:00
Patricio Whittingslow 1690c2c2d8 Delete nets/nets.go
nets/nets.go was replaced by netif/netif.go
2024-01-10 09:11:09 -03:00
soypat cdc76f3ea7 modify netif.Resolver to use net.DefaultResolver method set 2024-01-07 15:42:43 -03:00
soypat 68ccd70c56 rename file 2024-01-07 15:28:48 -03:00
soypat f657269a66 rename package: nets -> netif 2024-01-07 15:26:55 -03:00
soypat 6913eaf2b9 add examples 2023-12-20 10:26:06 -03:00
soypat d4705e501d rethink netdev name again 2023-12-19 23:12:46 -03:00
soypat 728bdbba5b apply some of @scottfeldman's suggestions 2023-12-19 23:07:20 -03:00
soypat b4f43b4d21 remove old comment 2023-12-18 16:02:55 -08:00
soypat 570e3d2b71 change EthLinkPoller name 2023-12-18 16:01:59 -08:00
soypat 7e45551ff5 add more methods to Link interface 2023-12-18 16:01:17 -08:00
soypat 60cdb7fd50 add EthLinkPoller; 2023-12-18 15:56:35 -08:00
soypat 731362a0cf nets: new package with netdev redesign ideas 2023-12-18 15:32:49 -08:00
Simon Dismo 1a96fc4547 Add more line speeds to mcp2515.go (#626)
mcp2515: adding the possibility to configure lower line speeds on the CAN bus.
2023-12-11 20:40:36 +01:00
Scott Feldman 3fabdc5c96 smoketest: add stack-size param for net tests. 2023-12-06 20:09:39 +01:00
deadprogram a8928d137e modules: add natiu package for tests
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-12-06 20:09:39 +01:00
Scott Feldman 709fc77960 fix examples/net/socket to work with netip API 2023-12-06 20:09:39 +01:00
Scott Feldman d6ae0b0d8b store Bind laddr in netip.AddrPort 2023-12-06 20:09:39 +01:00
Scott Feldman 7a7235f83b move netdev interface to use net/netip for IP addr/ports 2023-12-06 20:09:39 +01:00
Scott Feldman 3089bf8b1b net: updates/cleanup/docs for net, netdev, and netlink 2023-12-06 20:09:39 +01:00
Scott Feldman 196d1dd58d add tcpip stack framework
Add basis for TCP/IP stack.  The stack implements Netdever interface for
the top-end, and calls into a Netlinker interface on the bottom-end.
Each TCP/IP stack instance represents a L3/L4 endpoint with an IP
address.  The Netlinker is bound when creating the stack.  E.g.:

	spi, cs, wlreg, irq := cyw43439.PicoWSpi(0)
	cyw43 := cyw43439.NewDevice(spi, cs, wlreg, irq, irq)
	stack := tcpip.NewStack(cyw43)
	netdev.UseNetdev(stack)

Here, the cyw43439 driver is the Netlinker for the stack.  The new stack
is a Netdever, so we tell the "net" package to use the stack as the
netdev.  The stack manages L3/L4 socket connections, ultimately calling
into the Netlinker to send/recv L2 Ethernet pkts.
2023-12-06 20:09:39 +01:00
Scott Feldman 1fd8a768ae netlink: add L2 bridge/bond/vlan intermediate drivers
This adds three new L2 (intermediate) drivers for bridge/bond/vlan.
Theses drivers are incomplete but illustrate how to stack L2 drivers.
Here are some examples of how these driver would stack with the cy243439
driver:

  Bridge: bridge two cyw43 devices together, connecting the two LANs:

	cyw43_0 := cyw43439.NewDevice(...)
	cyw43_1 := cyw43439.NewDevice(...)

	bridge := NewBridge([]netlink.Netlinker{cyw43_0, cyw43_1})

	stack := tcpip.NewStack(bridge)
	netdev.UseNetdev(stack)

  Bond: bond two cyw43 devices together, creating one logical device.
  The first physical device is primary, the second is backup (fail-over)
  device:

	cyw43_0 := cyw43439.NewDevice(...)  // primary
	cyw43_1 := cyw43439.NewDevice(...)  // secondary (backup)

	bond := NewBond([]netlink.Netlinker{cyw43_0, cyw43_1})

	stack := tcpip.NewStack(bond)
	netdev.UseNetdev(stack)

   Vlan: add tagged VLAN ID=100 to cyw43:

	cyw43 := cyw43439.NewDevice(...)

	vlan100 := NewVlan(100, cyw43)

	stack := tcpip.NewStack(vlan100)
	netdev.UseNetdev(stack)
2023-12-06 20:09:39 +01:00
Scott Feldman 8238f96319 move netdev and netlink into their own packages and out of drivers
This moves the netdev/netlink namespace out of drivers and into their
own packages.  Also, defines netdev and netlink as L3/L4 and L2 OSI
layers, respectively.  Move some L3 functionality from netlink to
netdev (GetIPAddr).

For netlink, add ConnectParams for NetConnect to pass in L2 connection
parameters (ssid, pass, auth_type, etc).  Also adds connection mode
(STA, AP, etc).

For netlink, add SendEth and RecvEthFunc funcs to handle L2 send/recv of
Ethernet pkts.
2023-12-06 20:09:39 +01:00
Scott Feldman e7d51d3c73 Add new MQTT client example using natiu-mqtt
Add new MQTT client example using natiu-mqtt
2023-12-06 20:09:39 +01:00
Scott Feldman 4bf631a7e3 s/ConnectTimeo/ConnectTimeout, s/WatchdogTimeo/WatchdogTimeout/ 2023-12-06 20:09:39 +01:00
Scott Feldman 3c4df6e811 wifinina: add 'unknown failure' reason code for AP connect 2023-12-06 20:09:39 +01:00
Scott Feldman 255c639ad4 wifinina: add ResetIsHigh cfg switch for MKR 1010 (copied from #561) 2023-12-06 20:09:39 +01:00
Scott Feldman 276feecc20 Add network device driver model, netdev 2023-12-06 20:09:39 +01:00
deadprogram 00b1b4fbfb smoketest: allow stack-size flag as it is needed for net examples
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-12-06 12:52:56 +01:00
SoftTacos 3b183ad6ed added support for SK6812 to WS2812 device (#610)
sk6812: added support for SK6812 to WS2812 device
2023-12-04 11:17:20 +01:00
Thomas Richner 68da68c8c8 seesaw: remove delay from Read(...) 2023-12-03 21:39:00 -05:00
Thomas Richner f7b80ef95a seesaw: added example and smoketest 2023-12-03 21:39:00 -05:00
Thomas Richner 933c9e127d seesaw: device support and tests 2023-12-03 21:39:00 -05:00
Ayke van Laethem 92050d90da sgp30: add the now-obsolete SGP30 air quality sensor
Tested with a rp2040.

TODO: add the ability to set the absolute humidity for more accurate
sensor details. The formula for that is rather complex, so I've left
this as a future addition.
2023-12-01 07:44:49 +01:00
Ayke van Laethem 93cbba5d8b pixel: implement RGB555 pixel format as used by the GameBoy Advance 2023-11-24 18:13:04 +01:00
Ayke van Laethem 6cf07aff4c pyportal_boing: update to use DrawBitmap
Replace DrawRGBBitmap8 with DrawBitmap, following the change in the
previous commit.

This improves performance from 86fps to 100fps! I didn't investigate
why, but I suspect it's because it now needs only a single store instead
of two to update a pixel.
2023-11-23 17:27:00 +01:00
Ayke van Laethem af33129f41 ili9341: st7735: st7789: add DrawBitmap method
This adds a new DrawBitmap method, which is meant to replace
DrawRGBBitmap8.
2023-11-23 17:27:00 +01:00
Ayke van Laethem 100aadf585 st7789: make the display generic over RGB565 and RGB444
Same as for st7735 in the previous commit.

In addition, this avoids allocating a big chunk of memory on _every_
draw operation (even SetPixel) and instead reuses it across draw
operations. This makes the driver a whole lot more efficient.
2023-11-23 17:27:00 +01:00
Ayke van Laethem 4a9667ffef st7735: make the display generic over RGB565 and RGB444
Using RGB444 instead of RGB565 can speed up graphics operations by up to
25%, especially on slow screens. But for full support, all parts of the
driver need to be aware of the color format.

It's possible to do this using a regular configuration variable, but
it's unlikely to be very efficient. Hence the usage of generics.
2023-11-23 17:27:00 +01:00
Ayke van Laethem f96a70915e pixel: add package for efficiently working with raw pixel buffers
This has been optimized for working with SPI displays like the ST7789.
By working directly in the native color format of the display, graphics
operations can be much, _much_ faster.

Also, this makes it easier to use a different color format like RGB444
simply by changing the generic type.
2023-11-23 17:27:00 +01:00
Ayke van Laethem 3e64e754a2 epd2in13: use better black/white approximation
The previoius behavior was that entirely black pixels were treated as
white, and anything else as black. That's at least counter-intuitive.
This patch changes the behavior to actually look at the color values and
use a cutoff around medium gray: darker colors are treated as black, and
lighter colors are treated as white.

This is a backwards incompatible change, but I think this behavior makes
a lot more sense.
2023-11-09 10:07:19 +01:00
Ayke van Laethem 2c2da5c7bb epd2in13: add Sleep method like other displays
For details, see: https://github.com/tinygo-org/drivers/pull/548
2023-11-07 10:39:14 +01:00
Ayke van Laethem 14994a3f31 epd2in13: unify rotation configuration with other displays
This commit updates rotation behavior to match other displays.
For more information, see: https://github.com/tinygo-org/drivers/pull/550

This changes the signature of `SetRotation` but I don't think any
existing code will be affected by this change.
2023-11-07 10:39:14 +01:00
deadprogram 47dfeb9e94 examples/lora/lorawan: modify atcmd and basic demo to support choosing
any one of the supported regions at compile time by using ldflags.

Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-10-26 08:32:04 +02:00
deadprogram f20d1759d3 examples/lora/lorawan: add missing functions for simulated interface
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-10-26 08:32:04 +02:00
deadprogram 6b79a1b386 lora/lorawan: refactor shared functionality for ChannelUS, etc into embedded type
named channel, do the same for RegionSettings, and then change RegionSettings to
just Settings to avoid the redundant naming.

Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-10-26 08:32:04 +02:00
Emilio Garcia 7dd1067cc1 LoRa WAN add setter functions 2023-10-26 08:32:04 +02:00
Emilio Garcia 4edb771c5b fix lora us915 logic to step into 500 kHz range 2023-10-26 08:32:04 +02:00
Mirac Kara e95cfe66c1 LoRa WAN US915 Support
Adds a driver for us 915 protocol to the lora WAN drivers.
2023-10-26 08:32:04 +02:00
Thomas Richner 4edb58c0c5 fix-tests: fix broken testrunner 2023-10-25 09:57:40 +02:00
Thomas Richner f384e2db48 fix-tests: migrated legacy I2C 2023-10-25 09:57:40 +02:00
sago35 7c96387845 sh1106: fix I2C interface and add smoketest 2023-10-14 11:43:06 +02:00
Christian Ege 182d4c6ebb sh1106: fixed the description of the device struct
It looks like there was an copy paste left over. This is corrected to be
concise.

Signed-off-by: Christian Ege <ch@ege.io>
2023-10-09 08:59:44 +02:00
Thomas Richner dc2de4f9ed adafruit4650: support for Adafruite 4650 feather OLED 2023-10-06 09:03:32 +02:00
Christian Ege 36a12dd7a2 at24cx: fixed the description of the device struct
It looks like there was an copy paste left over. This is corrected to be
concise.

Signed-off-by: Christian Ege <ch@ege.io>
2023-10-06 08:44:12 +02:00
Christian Ege 00992756eb ds3231: fix the description in the example
It looks like there was a copy paste leftover. This got corrected to be
concise.

Signed-off-by: Christian Ege <ch@ege.io>
2023-10-06 08:44:12 +02:00
Ayke van Laethem 715c33d4b0 all: prepare for CGo changes in TinyGo
For details, see: https://github.com/tinygo-org/tinygo/pull/3927
This change just means we need to be more careful to use the right type,
now that types like C.uint32_t don't match to the Go equivalent (like
uint32).
2023-10-05 10:01:25 +02:00
deadprogram fd21e6ac9b all: update for drivers release 0.26.0
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-09-21 09:43:46 +02:00
Thomas Richner 485ed702c8 pcf8523: bump minimal go version to 1.18 2023-09-20 19:14:17 +02:00
Thomas Richner cfa50fd3c2 pcf8523: RTC driver 2023-09-20 19:14:17 +02:00
Thomas 5888bb2ded Sensirion Sht4x Support (#597)
sht4x: implemented driver
2023-09-19 08:31:43 +02:00
fchiesadoc a9b36f8bd4 Mpu9150 (#596)
mpu9150: add mpu9150 driver
2023-08-27 11:38:33 +02:00
deadprogram 1e4545828f build: use latest tag of tinygo-dev container for running tests
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-08-12 10:08:28 +02:00
dependabot[bot] cfa5103969 build(deps): bump golang.org/x/net
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.0.0-20210614182718-04defd469f4e to 0.7.0.
- [Release notes](https://github.com/golang/net/releases)
- [Commits](https://github.com/golang/net/commits/v0.7.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-11 14:39:57 +02:00
Patricio Whittingslow a45227590e add Sandbox Electronics NDIR CO2 sensor driver (2) (#580)
add sandbox electronics NDIR CO2 sensor
2023-08-11 11:58:28 +02:00
Kenneth Bell 4f789fb556 ssd1306: improve bus error handling 2023-08-11 11:40:45 +02:00
Kenneth Bell 996f1b047f fix uses of legacy i2c WriteRegister calls 2023-08-08 11:16:50 +02:00
Ayke van Laethem eef03917ab bma42x: add new BMA421/BMA425 driver
I wrote this for the PineTime, and all available sensors (accelerometer,
step counter, temperature sensor) do work.

This commit also includes two "configuration files", that actually
appear to be firmware files to run on the accelerometer for special
features like step counting.
I'm not sure where they originally came for, and I don't know the
copyright status of them. However, Bosch has open-sourced the BMA423
driver which includes a similar binary blob and the InfiniTime and
Wasp-OS projects have been shipping these blobs without issues, so I
think it's reasonably safe to include these binary blobs directly in the
source.
2023-08-08 10:36:23 +02:00
soypat 31538f1b2f fix requested changes by @aykevl 2023-07-02 18:30:11 +02:00
soypat 64029612e0 add correct Tx implementation for mock I2C interfaces 2023-07-02 18:30:11 +02:00
soypat e6f82fad2e i2c iface refactor: Resolve 559 2023-07-02 18:30:11 +02:00
Ayke van Laethem e20c6d05f8 st7789: fix scrolling when rotated by 180°
This fixes https://github.com/tinygo-org/drivers/issues/573.
It doesn't handle 90° or 270°, I guess it needs a fix for 270° but I
haven't tested that so didn't include it in the patch.
2023-06-20 17:49:47 +02:00
Ayke van Laethem 9c29529cbb st7789: fix incorrect Rotation configuration
The rotation as configured using st7789.Config was rotated 180°: 0° was
configured as 180°, 90° was configured as 270°, etc. Presumably with the
original test display, the ribbon cable was seen as the top of the
screen while if you look at product photos it is usually at the bottom.
Example:
https://www.buydisplay.com/wide-angle-1-3-inch-240x240-color-ips-tft-display-st7789-controller
Only Adafruit seems to sell these displays upside down:
https://www.adafruit.com/product/3787

This patch fixes this mistake. It should be noted that this is backwards
incompatible: all code that uses a st7789 will have to be modified to
use the correct rotation instead of the previous incorrect rotation.

If this is too big of a change, we could just keep things as-is and
pretend that all displays are upside down.
2023-06-20 10:44:49 +02:00
Ayke van Laethem f6d399ec08 ili9341: st7789: fix SetScrollArea
The existing code was broken in a few ways:

  - It didn't use the correct operator precedence for the VSCRDEF VSA
    variable: it needed some extra parentheses to be correct.
  - It used the configured height instead of the actual display height
    for calculating VSA, which is incorrect. TFA+VSA+BFA must always be
    exactly 320, even if a lower value is configured.
  - If a lower than 320 pixel height is configured, the bottomFixedArea
    parameter applied to the whole 320 pixel screen height. Because this
    seems counter intuitive (and relies on properties of any given
    screen), I've changed it to work from the actual visible bottom of
    the screen (which may be smaller than 320 pixels).
    TODO: this doesn't take RowOffset into account, while it probably
    should.

I haven't fixed the st7735 implementation, because I didn't have example
code on hand that would easily work on a st7735 screen. This is left as
a TODO for the future.
2023-06-20 10:27:08 +02:00
deadprogram 5e0191655b Prepare for 0.25.0 release
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-06-11 12:07:35 +02:00
Daniel Esteban 95f0ca8c3e initial support for ttp229 (BSF) 2023-05-21 00:32:05 +02:00
Ayke van Laethem 42e4d29157 st7789: allow changing the color format using COLMOD
This is only really useful for applications that use DrawRGBBitmap8. The
RGB444 format has fewer colors, but can be up to 25% faster than the
default RGB565.

I have tested this change on the Gopher Badge and the PineTime. Both can
be substantially faster once the code is modified to output RGB444
instead of RGB565.
2023-05-20 16:07:10 +02:00
ivoszz f627fd162d mpu6886: add smoke test 2023-05-20 15:49:13 +02:00
ivoszz f3f2d65878 mpu6886: initial implementation 2023-05-20 15:49:13 +02:00
Ayke van Laethem deca190ba2 ili9341: add EnableTEOutput to be able to sync drawing with VSYNC
Many displays don't have the TE pin exposed. But those that do have the
pin (for example, the PyPortal) can use it to synchronize writing a new
image to the display. When implemented correctly, tearing can be avoided
entirely.

This commit also changes the LCD refresh direction to either
top-to-bottom or left-to-right depending on the rotation. Previously it
might refresh from right-to-left or bottom-to-top. This has little
impact on code that doesn't use the TE line, but code that does now only
needs to worry about two cases (top-to-bottom and left-to-right) instead
of four.
(Unfortunately, it appears that the hardware doesn't support changing
the major LCD refresh order so code that wants to do tear-free rendering
still needs to care about these two cases).
2023-05-20 15:22:14 +02:00
Ayke van Laethem 184dff93b1 ili9341: fix Size() for mirrored rotation 2023-05-20 15:22:14 +02:00
deadprogram 331a60ce2d sdcard: remove tinyfs example and replace with link to tinyfs repo in docs
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-05-20 09:38:16 +02:00
Ron Evans de2742f10c Revert "(#501) make IP.String() method return something sensible"
This reverts commit b1c9612158.
2023-05-19 15:11:22 +02:00
deadprogram 422051d822 wifinina: add ResetIsHigh to control the behavior of the RESET pin for boards like the Arduino MKR 1010
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-05-18 21:25:33 +02:00
deadprogram 66abe9c28b examples/wifinina: improve connectToAP() and other needed minor corrections
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-05-18 21:25:33 +02:00
deadprogram 5f25a4f6be wifinina: small timing adjustments in Configure() to better ensure device reset
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-05-18 21:25:33 +02:00
deadprogram 9955e09466 wifinina: only add generated strings when using wifidebug tag
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-05-14 23:16:40 +02:00
deadprogram bc9177726d wifinina: add generated strings, improved debugging system and messages
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-05-14 23:16:40 +02:00
Ayke van Laethem aac959eb7b i2csoft: use cycle counting for delays
This should make the software I2C driver more portable, and potentially
usable on chips that aren't yet supported. More importantly, it avoids
some guesswork in the various chip-specific timing code.
2023-05-14 19:35:29 +02:00
Ayke van Laethem 9fdf0d657d delay: add new package for cycle-accurate delays
This is implemented in inline assembly using machine.CPUFrequency() to
know how long a single CPU cycle takes. As long as it is called with a
constant duration, it should be fully inlined and all values can be
const-propagated resulting in very tight inline assembly.

For example, when I convert i2csoft to use this delay function, the
entire delay function compiles to something like this:

    8784:   movs    r6, #100
    8786:   mov     r0, r6
    8788:   nop
    878a:   nop
    878c:   nop
    878e:   nop
    8790:   nop
    8792:   subs    r0, #1
    8794:   bne     0x8788

That means that all the math to calculate the number of cycles is
entirely optimized away (in this case, to 100 loops).

I ran the example on a few boards to see how well it works:

| board                 | 100ms wait | CPU core
|-----------------------|------------|------
| microbit              | 121.6ms    | Cortex-M0 so it has 12% overhead
| circuitplay-express   | 100.1ms    | Cortex-M0+ so it is cycle accurate
| pico                  | 100.2ms    | Cortex-M0+
| pyportal              | 100.3ms    | Cortex-M4
| circuitplay-bluefruit | 125.8ms    | Cortex-M4
| esp8266               | 125.1ms    |

This shows that there is some loop overhead because of conservative
estimates, but note that even though there may be a 25% overhead, the
actual overhead per `delay.Sleep()` call is very small. It should be
good enough for software I2C at least, and can potentially be improved
in the future.
2023-05-14 19:35:29 +02:00
Ayke van Laethem b0c9f259dc st7789: update saved rotation in SetRotation 2023-05-14 15:54:51 +02:00
Ayke van Laethem 53404b3f4b ci: run smoke tests in parallel
This speeds up the smoke tests by over 5x on my laptop, but keeps two
features that I find very important:

 1. A stable output, which is used by tools like sizediff.
 2. An easy to change text file with all the smoke tests.

This means that parsing is a little bit harder, but with the help of
packages like shlex and flag this is actually not very difficult.
2023-05-07 12:30:44 +02:00
Patricio Whittingslow 8ddfa6ac8f add Sensor interface and Measurement type 2023-05-01 21:13:46 +02:00
deadprogram c0ab3596fa docs: remove full list of devices from README, better to keep it on the tinygo.org site
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-04-30 01:11:18 +02:00
deadprogram 9de76bc145 docs: update LICENSE year
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-04-30 01:11:18 +02:00
deadprogram ae04d5b0bb build: switch to ghcr.io for docker container
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-04-30 01:09:18 +02:00
Ayke van Laethem 3c01b7e222 st7789: support the chip select pin
It is needed to control the chip select pin when the st7789 display is
wired together with some other SPI device on the same bus, for example
if it shares the bus with SPI flash.

This required some refactoring of the code to correctly set the CS pin
everywhere. Notably, this removes the public Command, Data, Tx, and Rx
methods which poke into private details of the st7789 driver and are
therefore best hidden in my opinion.
2023-04-30 00:56:47 +02:00
Ayke van Laethem c689c11838 ili9341: st7735: st7789: unify rotation support
* Unify the Rotation type, so that SetRotation can be used in
    interfaces.
  * Add Rotation() method to these displays, so that the current
    rotation can be read.
  * Change SetRotation() so that it can return an error (if the given
    rotation isn't supported).
2023-04-27 19:00:51 +02:00
Ayke van Laethem 59d66d1e73 Makefile: add XTENSA=0 flag to skip Xtensa tests
This can be useful, to quickly run the tests without compile errors
because of missing Xtensa support.
2023-04-27 18:38:44 +02:00
Ayke van Laethem 1eb7072162 Makefile: remove AVR=0 flag
Now that we don't rely on external tools anymore, this isn't necessary
anymore.
2023-04-27 18:38:44 +02:00
Daniel Esteban 5cc21329a6 first implementation of 1-wire protocol (#505)
onewire: initial implementation for protocol and ds18b20 device
2023-04-22 17:55:37 +02:00
Ayke van Laethem d3022e7d6e all: add sleep mode to various SPI displays
The API looks like this and is based on the PCA9685 PWM chip:

    Sleep(sleepEnabled bool) error

It will set the LCD panel to a low power state (not displaying any
image) but memory contents will be preserved.
2023-04-17 08:23:54 +02:00
Ayke van Laethem 5c06b82b27 st7789: avoid heap allocations after the driver is created
Heap allocations can result in significant and unpredictable slowdowns.
For example, they can take over 10ms on the Gopher Badge, which results
in visible tearing (when tearing is otherwise avoided).
2023-04-14 08:34:51 +02:00
Pablo Canseco bf53cb2fd4 [gps] make the date available in addition to the time (#532)
gps: also parse the date from an RMC sentence. Add coverage to unit test so it also asserts on time and date.
2023-04-13 09:52:57 +02:00
Ayke van Laethem 1fd38a8a68 st7789: make it possible to configure gamma
Some displays need other values than the default to look right.
Therefore, add a configuration option to set the gamma table for a
particular LCD panel.
2023-04-09 07:43:29 +02:00
Daniel Esteban 55e100c4fe added DrawRGBBitmap8 (same as ili9341 & st7735) 2023-03-30 00:19:36 +02:00
Ayke van Laethem cba17ef125 st7735: add DrawRGBBitmap8 method to draw raw RGB565 buffers
This is needed for high performance graphics.
Using this API, and using partial updates to the screen (no full screen
refreshes), I've been able to get well over 60fps of updates.
2023-03-28 19:52:27 +02:00
Neil Davis c59b5a909b Add support for ams AS560x on-axis magnetic rotary position sensors 2023-03-21 23:27:48 +01:00
Brian Park b0a971573b ds3231: Document incorrect leap year 2100
The current code interprets the 'century' flag as the year 2100. However the
DS3231 hardware does not incorporate this flag in its leap year calculation, so
will incorrectly consider the year 2100 as a leap year and increment from
2100-02-28 to 2100-02-29 instead of the correct 2100-03-01.

The 'century' bit is not useful for anything as far as I can tell. But instead
of removing the code that uses the 'century' bit, I thought it would be less
intrusive to just document the current behavior.
2023-03-20 22:59:57 +01:00
Olivier Fauchon 94017b5437 sx126x/sx27x: Reduce spi buffer size, add missing select when using channels 2023-03-20 22:38:33 +01:00
Olivier Fauchon 56208a28d5 sx127x/sx126x: Remove heap alloc in interrupt, add non blocking channel send/receive, and other cleanups 2023-03-20 22:38:33 +01:00
Brian Park 0adc7e78aa ds3231: Fix negative temperature conversion
I also verified my interpretation of the DS3231 datasheet and my implementation
of this formula by placing a DS3231 chip in the freezer section of my
refrigerator for an hour or two, then reading the temperature. I got -16C,
which is close enough to the -18C that the freezer was set to.
2023-03-03 09:43:48 +01:00
deadprogram 820298bfe3 all: prepare for release 0.24.0
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-02-12 22:30:48 +01:00
deadprogram 46cbacbe03 docs: add sx127x to list of supported devices
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-02-12 21:33:36 +01:00
deadprogram 9d4847fc6d sx12xx: add README files for some basic explanation
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-02-12 21:33:36 +01:00
deadprogram 4de0afc224 build: work around for CVE-2022-24765
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-02-12 08:39:28 +01:00
deadprogram 21fa184b76 build: update to actions/checkout@v3
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-02-12 08:39:28 +01:00
Olivier Fauchon fae4307190 sx127x: Fix bug in SetTxPower, SetPreambleLength 2023-02-01 09:08:43 +01:00
Olivier Fauchon 7f61bda24c lorawan: Update AU915 RegionSet modulations 2023-02-01 08:53:21 +01:00
deadprogram e613ab1eb3 sx127x: use select for timeout
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-01-31 23:54:43 +01:00
Olivier Fauchon d57d9b6c74 sx127x: fix typos, provide corrections 2023-01-31 23:54:43 +01:00
Olivier Fauchon 07ba9ec0fd fix bandwidth,tx power in lora/sx126x/lora_continuous example 2023-01-31 23:54:20 +01:00
Olivier Fauchon fa0cf6ec7f lorawan cleanup and optimizations:
- Remove all default loraconf default initialisation in lorawan examples (Region Settings takes care of this now)
- Remove retries while receiving JoinAccept and increase LoraRX to 10s (no need to wait more than 10s)
- Add constants for RegionSetting default frequencies
- Standardize all lora default configurations in lora examples
- Add new AT+LW=NET,(ON|OFF) command in atcmd example
2023-01-31 23:54:13 +01:00
deadprogram 4e687b29c3 mpu6050: add functions to configure clock, and scaling for accelerometer and gyroscope
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-01-30 10:07:38 +01:00
Olivier Fauchon d283abdb3a lorawan: Fix error in uplink crc 2023-01-29 23:56:19 +01:00
Olivier Fauchon 02d808a6fb lorawan/sx12xx : Code cleanup 2023-01-29 23:56:19 +01:00
Olivier Fauchon d473b9e1dc lorawan/basic-demo: add debug logs at compile time with ldflags 2023-01-29 23:56:19 +01:00
Olivier Fauchon 7cc32dffed lorawan/basic-demo : fix in lorawan debug informations 2023-01-29 23:56:19 +01:00
Olivier Fauchon 58cb44f3f6 sx126x driver: fix in SetBandwidth function 2023-01-29 23:56:19 +01:00
Olivier Fauchon 78cc1afe46 lorawan: Basic implementation of Lorawan Regional Settings and EU868/AU915 regions 2023-01-29 23:56:19 +01:00
deadprogram e6cde8f7ae bmp180: add ReadAltitude() function copied from BMP280 driver
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-01-27 21:42:08 +01:00
deadprogram 89f113c0ca examples: lora/lorawan add needed build tags for lgt92/featherwing
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-01-27 00:49:38 +01:00
deadprogram 3b8a808a52 examples: lora/lorawan pin mapping for lgt92 and Adafruit Featherwing
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-01-27 00:49:38 +01:00
deadprogram 7506a895a2 examples: update lora/lorawan common setup to use sx127x RadioControl
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-01-27 00:49:38 +01:00
deadprogram 4a950a4474 sx127x: add RadioController interface to match sx126x
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-01-27 00:49:38 +01:00
deadprogram f2637a87d2 gps: improve parsing and add tests to verify
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-01-17 07:48:22 +01:00
deadprogram ecb343b3b3 gps: improve error handling
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-01-17 07:48:22 +01:00
deadprogram 0b62d4fb4a gps: add support for GLL sentence type, add original sentence to gps errors
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-01-17 07:48:22 +01:00
deadprogram 5b461ec2d1 gps: improve error handling
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-01-17 07:48:22 +01:00
Vincent Janelle b1c9612158 (#501) make IP.String() method return something sensible 2023-01-16 13:43:59 +01:00
deadprogram 0b6e1d0e78 lorawan: increment devnonce don't just create a new one each join attempt
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-01-16 09:33:13 +01:00
deadprogram 49e7a6d6c8 sx126x: actually set the frequency when calling SetFrequency()
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-01-16 09:33:13 +01:00
deadprogram be644b36fe lorawan: simplify GetRand16() function
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-01-16 09:33:13 +01:00
deadprogram 409723e496 lorawan: set devNonce to new random value on each attempt to join
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-01-16 09:33:13 +01:00
deadprogram 8aa3530c8b lorawan: increase timeout values for tx/rx
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-01-16 09:33:13 +01:00
deadprogram 390854ee45 examples: set pin mappings for sx126x external board
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-01-16 09:33:13 +01:00
deadprogram ce5dd65dad sx126x: pre-define all errors to avoid heap allocations
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-01-16 09:33:13 +01:00
deadprogram 73e9d5bfec sx126x: move RadioController into separate file for clarity
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-01-16 09:33:13 +01:00
deadprogram 462d2b3315 sx126x: correct RX/TX pin mapping for TheThingsIndustries GNSE board
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-01-16 09:33:13 +01:00
deadprogram 9e70ecbe64 sx126x: there is no dio0 just dio1
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-01-16 09:33:13 +01:00
deadprogram d6d06396b4 sx126x: refactor to RadioController interface to more easily handle non-STM32WL boards and remove duplicated code
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-01-16 09:33:13 +01:00
deadprogram 80b2a4a569 lora/lorawan: set functions for keys
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-01-16 09:33:13 +01:00
deadprogram 556aa9e6d3 examples: lorawan atcmd outputs needed info after Join to reuse, aka 'OTAA provisioning'
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-01-16 09:33:13 +01:00
deadprogram 4bc8ecdbdd lora/lorawan: completed functions needed for Join OTAA process
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-01-16 09:33:13 +01:00
sago35 999f42dc03 net/http: add PostForm() 2023-01-11 21:29:12 +01:00
Olivier Fauchon a1b47cd778 lorawan: Add test in Makefile 2023-01-11 07:48:30 +01:00
Olivier Fauchon 889536f7f0 Lorawan uplink support and new lorawan 'basic-demo' example 2023-01-11 07:48:30 +01:00
deadprogram 301e6bc35b examples: improvements to README for LoRaWAN atcmd example
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-01-10 22:45:01 +01:00
deadprogram 49b390f7bc examples: lorawan atcmd able to join
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-01-10 22:45:01 +01:00
deadprogram a62fc01d81 lora/lorawan: implement minimal Join() function, and supporting functions
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-01-10 22:45:01 +01:00
deadprogram 36dfdf568e examples: move atcmd into lorawan
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-01-10 22:45:01 +01:00
deadprogram 7b5a3970ca lorawan: add initial LoRaWAN stack support
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-01-10 22:45:01 +01:00
deadprogram 3433364586 examples: example with LoRa AT command set implementation
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-01-10 22:45:01 +01:00
deadprogram 338be928c9 sx126x, sx127x: remove extra Lora in functions to avoid stuttering
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-01-09 07:36:07 +01:00
deadprogram 98943393a5 sx127x: correct header type implicit/explcit as they were reversed
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-01-09 07:36:07 +01:00
deadprogram 6b817faed3 sx126x: add Reset() and needed pin
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-01-09 07:36:07 +01:00
deadprogram 9b14ee3ec5 sx127x: lora rx/tx example working, now assumes pybadge+featherwing
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-01-09 07:36:07 +01:00
deadprogram 6d51370512 lora: created shared RadioEvent
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-01-09 07:36:07 +01:00
deadprogram 2005d7d483 lora: move shared config for sx126x/sx127x to single package
Signed-off-by: deadprogram <ron@hybridgroup.com>
2023-01-09 07:36:07 +01:00
Olivier Fauchon f15eec822f sx127x: Ajout du support d'interruptions RXTimeout (DIO1) 2023-01-09 07:36:07 +01:00
Olivier Fauchon dc4f0c974a sx127x: Major cleanup/rewrite, and tests pass with lorawan stack tests 2023-01-09 07:36:07 +01:00
Olivier Fauchon 28ddb2681d sx127x: Driver for Semtech sx127x radio modules
This first version of the driver has been tested with BB-FRM9x module
2023-01-09 07:36:07 +01:00
307 changed files with 15593 additions and 17992 deletions
+5 -2
View File
@@ -11,10 +11,13 @@ on:
jobs:
build:
runs-on: ubuntu-latest
container: tinygo/tinygo-dev
container: ghcr.io/tinygo-org/tinygo-dev:latest
steps:
- name: Work around CVE-2022-24765
# We're not on a multi-user machine, so this is safe.
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: TinyGo version check
run: tinygo version
- name: Enforce Go Formatted Code
+193
View File
@@ -1,3 +1,196 @@
0.26.0
---
- **core**
- i2c iface refactor: Resolve #559
- fix uses of legacy i2c WriteRegister calls
- add correct Tx implementation for mock I2C interfaces
- bump golang.org/x/net version
- **new devices**
- **bma42x**
- add new BMA421/BMA425 driver
- **ndir**
- add Sandbox Electronics NDIR CO2 sensor driver (#580)
- **mpu9150**
- implement driver for Mpu9150 (#596)
- **sht4x**
- implement driver for sht4x (#597)
- **pcf8523**
- implement driver for pcf8523 (#599)
- **enhancements**
- **ssd1306**
- improve bus error handling
- **bugfixes**
- **st7789**
- fix scrolling when rotated by 180°
- **st7789**
- fix incorrect Rotation configuration
- fix SetScrollArea
- **ili9341**
- fix SetScrollArea
- **build**
- use latest tag of tinygo-dev container for running tests
0.25.0
---
- **core**
- add Sensor interface and Measurement type
- **delay**
- add new package for cycle-accurate delays
- **new devices**
- **AS560x**
- Add support for ams AS560x on-axis magnetic rotary position sensors
- **onewire**
- first implementation of 1-wire protocol (#505)
- **mpu6886**
- initial implementation
- **ttp229**
- initial support for ttp229 (BSF)
- **enhancements**
- **gps**
- make the date available in addition to the time (#532)
- **i2csoft**
- use cycle counting for delays
- **ili9341**
- add EnableTEOutput to be able to sync drawing with VSYNC
- add sleep mode
- unify rotation support
- **st7735**
- add DrawRGBBitmap8 method to draw raw RGB565 buffers
- add sleep mode
- unify rotation support
- **st7789**
- added DrawRGBBitmap8 (same as ili9341 & st7735)
- allow changing the color format using COLMOD
- make it possible to configure gamma
- support the chip select pin
- update saved rotation in SetRotation
- add sleep mode
- unify rotation support
- **sx126x/sx127x**
- Reduce spi buffer size, add missing select when using channels
- Remove heap alloc in interrupt, add non blocking channel send/receive, and other cleanups
- **wifinina**
- add generated strings, improved debugging system and messages
- add ResetIsHigh to control the behavior of the RESET pin for boards like the Arduino MKR 1010
- only add generated strings when using wifidebug tag
- **bugfixes**
- **ds3231**
- Document incorrect leap year 2100
- Fix negative temperature conversion
- **ili9341**
- fix Size() for mirrored rotation
- **st7789**
- avoid heap allocations after the driver is created
- **net**
- Revert "(#501) make IP.String() method return something sensible"
- **wifinina**
- small timing adjustments in Configure() to better ensure device reset
- **examples**
- **sdcard**
- remove tinyfs example and replace with link to tinyfs repo in docs
- **wifinina**
- improve connectToAP() and other needed minor corrections
- **build**
- switch to ghcr.io for docker container
- run smoke tests in parallel
- **Makefile**
- add XTENSA=0 flag to skip Xtensa tests
- remove AVR=0 flag
- **docs**
- remove full list of devices from README, better to keep it on the tinygo.org site
- update LICENSE year
0.24.0
---
- **new devices**
- **lora**
- created shared RadioEvent
- move shared config for sx126x/sx127x to single package
- **lorawan**
- add initial LoRaWAN stack support
- Basic implementation of Lorawan Regional Settings and EU868/AU915 regions
- **qmi8658c**
- Add support for the QMI8658C sensor (#467)
- **sh1106**
- add support for SH1106 display driver
- **sx127x**
- Driver for Semtech sx127x radio modules
- **enhancements**
- **bme280**
- improve config support
- add ReadAltitude() function copied from BMP280 driver
- **buzzer**
- make all note durations float64
- no tone during rest
- **dht22**
- update DHT22 receive to use runtime/interrupt
- **gps**
- add support for GLL sentence type, add original sentence to gps errors
- improve error handling
- improve parsing and add tests to verify
- **microbitmatrix**
- add link to schema for microbit V2
- add smoke test for microbitmatrix with microbit-v2
- add support for brightness of led pixels
- harmonize v1 and v2 implementation
- move Size() to version agnostic part
- **mpu6050**
- add functions to configure clock, and scaling for accelerometer and gyroscope
- **net/http**
- add PostForm()
- **sx126x**
- add Reset() and needed pin
- move RadioController into separate file for clarity
- pre-define all errors to avoid heap allocations
- refactor to RadioController interface to more easily handle non-STM32WL boards and remove duplicated code
- **vl53l1x**
- Add getter for the effective SPAD count
- **wifinina**
- add support for http server (#480)
- **bugfixes**
- **lsm303agr**
- fix I2C address auto increment for multi data read
- **net**
- (#501) make IP.String() method return something sensible
- **mpu6050**
- return I2C error when configuring fails
- **sx126x**
- fix in SetBandwidth function
- actually set the frequency when calling SetFrequency()
- correct RX/TX pin mapping for TheThingsIndustries GNSE board
- **examples**
- **LoRaWAN**
- example with LoRaWAN AT command set implementation
- basic example
- update all remaining examples for refactored API
- **sx126x**
- fix bandwidth,tx power in lora//lora_continuous example
- **sx127x**
- rx/tx example
- **build**
- remove older format build tags
- update to actions/checkout@v3
- work around for CVE-2022-24765
0.23.0
---
- **new devices**
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2018-2022 The TinyGo Authors. All rights reserved.
Copyright (c) 2018-2023 The TinyGo Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
+3 -240
View File
@@ -7,248 +7,11 @@ FMT_PATHS = ./
fmt-check:
@unformatted=$$(gofmt -l $(FMT_PATHS)); [ -z "$$unformatted" ] && exit 0; echo "Unformatted:"; for fn in $$unformatted; do echo " $$fn"; done; exit 1
XTENSA ?= 1
smoke-test:
@mkdir -p build
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/adt7410/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/adxl345/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=pybadge ./examples/amg88xx
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/apa102/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=nano-33-ble ./examples/apds9960/proximity/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/apa102/itsybitsy-m0/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/at24cx/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/bh1750/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/blinkm/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/bmi160/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/bmp180/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/bmp280/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=trinket-m0 ./examples/bmp388/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=bluepill ./examples/ds1307/sram/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=bluepill ./examples/ds1307/time/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/ds3231/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/easystepper/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/espat/espconsole/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/espat/esphub/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/espat/espstation/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/flash/console/spi
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/flash/console/qspi
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/gc9a01/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=feather-m0 ./examples/gps/i2c/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=feather-m0 ./examples/gps/uart/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/hcsr04/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/hd44780/customchar/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/hd44780/text/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/hd44780i2c/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=nano-33-ble ./examples/hts221/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/hub75/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/ili9341/basic
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=xiao ./examples/ili9341/basic
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/ili9341/pyportal_boing
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/ili9341/scroll
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=xiao ./examples/ili9341/scroll
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/ili9341/slideshow
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/lis3dh/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=nano-33-ble ./examples/lps22hb/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/lsm303agr/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/lsm6ds3/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mag3110/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mcp23017/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mcp23017-multiple/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mcp3008/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mcp2515/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/microbitmatrix/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=microbit-v2 ./examples/microbitmatrix/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mma8653/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mpu6050/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=p1am-100 ./examples/p1am/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=pico ./examples/pca9685/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/pcd8544/setbuffer/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/pcd8544/setpixel/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=arduino ./examples/servo
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=pybadge ./examples/shifter/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/sht3x/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/shtc3/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/ssd1306/i2c_128x32/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/ssd1306/spi_128x64/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/ssd1331/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/st7735/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/st7789/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/thermistor/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=circuitplay-bluefruit ./examples/tone
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/tm1637/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/touch/resistive/fourwire/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/touch/resistive/pyportal_touchpaint/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/vl53l1x/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/vl6180x/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/waveshare-epd/epd2in13/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/waveshare-epd/epd2in13x/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/waveshare-epd/epd4in2/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/wifinina/ntpclient/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/wifinina/udpstation/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/wifinina/tcpclient/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/wifinina/webclient/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/ws2812
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.bin -target=m5stamp-c3 ./examples/ws2812
@md5sum ./build/test.bin
tinygo build -size short -o ./build/test.hex -target=feather-nrf52840 ./examples/is31fl3731/main.go
@md5sum ./build/test.hex
ifneq ($(AVR), 0)
tinygo build -size short -o ./build/test.hex -target=arduino ./examples/ws2812
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=digispark ./examples/ws2812
@md5sum ./build/test.hex
endif
tinygo build -size short -o ./build/test.hex -target=trinket-m0 ./examples/bme280/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/microphone/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/buzzer/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=trinket-m0 ./examples/veml6070/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/l293x/simple/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/l293x/speed/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/l9110x/simple/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/l9110x/speed/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=nucleo-f103rb ./examples/shiftregister/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=hifive1b ./examples/ssd1351/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/lis2mdl/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/max72xx/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=feather-m0 ./examples/dht/main.go
@md5sum ./build/test.hex
# tinygo build -size short -o ./build/test.hex -target=arduino ./examples/keypad4x4/main.go
# @md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=xiao ./examples/pcf8563/alarm/
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=xiao ./examples/pcf8563/clkout/
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=xiao ./examples/pcf8563/time/
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=xiao ./examples/pcf8563/timer/
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=pico ./examples/qmi8658c/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=feather-m0 ./examples/ina260/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=nucleo-l432kc ./examples/aht20/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=feather-m4 ./examples/sdcard/console/
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=feather-m4 ./examples/sdcard/tinyfs/
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=wioterminal ./examples/rtl8720dn/webclient/
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=wioterminal ./examples/rtl8720dn/webserver/
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=wioterminal ./examples/rtl8720dn/mqttsub/
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=feather-m4 ./examples/i2csoft/adt7410/
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.elf -target=wioterminal ./examples/axp192/m5stack-core2-blinky/
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.uf2 -target=pico ./examples/xpt2046/main.go
@md5sum ./build/test.uf2
tinygo build -size short -o ./build/test.elf -target=m5stack-core2 ./examples/ft6336/basic/
@md5sum ./build/test.elf
tinygo build -size short -o ./build/test.elf -target=m5stack-core2 ./examples/ft6336/touchpaint/
@md5sum ./build/test.elf
tinygo build -size short -o ./build/test.hex -target=nucleo-wl55jc ./examples/sx126x/lora_rxtx/
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.uf2 -target=pico ./examples/ssd1289/main.go
@md5sum ./build/test.uf2
tinygo build -size short -o ./build/test.hex -target=pico ./examples/irremote/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=badger2040 ./examples/uc8151/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.uf2 -target=pico ./examples/scd4x/main.go
@md5sum ./build/test.uf2
tinygo build -size short -o ./build/test.uf2 -target=circuitplay-express ./examples/makeybutton/main.go
@md5sum ./build/test.uf2
@go run ./smoketest.go -xtensa=$(XTENSA) smoketest.sh
# rwildcard is a recursive version of $(wildcard)
# https://blog.jgc.org/2011/07/gnu-make-recursive-wildcard-function.html
+233
View File
@@ -0,0 +1,233 @@
### Table of Contents
- ["net" Package](#net-package)
- [Using "net" Package](#using-net-package)
- [Using "net/http" Package](#using-nethttp-package)
- [Using "crypto/tls" Package](#using-cryptotls-package)
- [Using Sockets](#using-sockets)
## "net" Package
TinyGo's "net" package is ported from Go. The port offers a subset of Go's
"net" package. The subset maintains Go 1 compatiblity guarantee. A Go
application that uses "net" will most-likey just work on TinyGo if the usage is
within the subset offered. (There may be external constraints such as limited
SRAM on some targets that may limit full "net" functionality).
Continue below for details on using "net" and "net/http" packages.
See src/net/READMD.md in the TinyGo repo for more details on maintaining
TinyGo's "net" package.
## Using "net" Package
Ideally, TinyGo's "net" package would be Go's "net" package and applications
using "net" would just work, as-is. TinyGo's net package is a partial port of
Go's net package, so some things may not work because they have not been
ported.
There are a few features excluded during the porting process, in particular:
- No IPv6 support
- No DualStack support
Run ```go doc -all ./src/net``` in TinyGo repo to see full listing of what has
been ported. Here is a list of things known to work. You can find examples
of these at [examples/net](examples/net/).
### What is Known to Work
(These are all IPv4 only).
- TCP client and server
- UDP client
- TLS client
- HTTP client and server
- HTTPS client
- NTP client (UDP)
- MQTT client (paho & natiu)
- WebSocket client and server
Multiple sockets can be opened in a single app. For example, the app could run
as an http server listen on port :80 and also use NTP to get the current time
or send something over MQTT. There is a practical limit to the number of
active sockets per app, around 8 or 10, so don't go crazy.
Applications using Go's net package will need a few setup steps to work with
TinyGo's net package. The steps are required before using "net".
### Step 1: Probe to Load Network Driver
Call Probe() to load the correct network driver for your target. Probe()
allows the app to work on multiple targets.
```go
package main
import (
"tinygo.org/x/drivers/netlink/probe"
)
func main() {
// load network driver for target
link, dev := probe.Probe()
...
}
```
Probe() will load the driver with default configuration for the target. For
custom configuration, the app can open code Probe() for the target
requirements.
Probe() returns a [Netlinker](netlink/README.md) and a
[Netdever](netdev/README.md), interfaces implemented by the network driver.
Next, we'll use the Netlinker interface to connect the target to an IP network.
### Step 2: Connect to an IP Network
Before the net package is fully functional, we need to connect the target to an
IP network.
```go
package main
import (
"tinygo.org/x/drivers/netlink"
"tinygo.org/x/drivers/netlink/probe"
)
func main() {
// load network driver for target
link, _ := probe.Probe()
// Connect target to IP network
link.NetConnect(&netlink.ConnectParams{
Ssid: "my SSID",
Passphrase: "my passphrase",
})
// OK to use "net" from here on
...
}
```
Optionally, get notified of IP network connects and disconnects:
```go
link.Notify(func(e netlink.Event) {
switch e {
case netlink.EventNetUp: println("Network UP")
case netlink.EventNetDown: println("Network DOWN")
})
```
Here is an example of an http server listening on port :8080:
```go
package main
import (
"fmt"
"net/http"
"tinygo.org/x/drivers/netlink"
"tinygo.org/x/drivers/netlink/probe"
)
func HelloServer(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:])
}
func main() {
// load network driver for target
link, _ := probe.Probe()
// Connect target to IP network
link.NetConnect(&netlink.ConnectParams{
Ssid: "my SSID",
Passphrase: "my passphrase",
})
// Serve it up
http.HandleFunc("/", HelloServer)
http.ListenAndServe(":8080", nil)
}
```
## Using "net/http" Package
TinyGo's net/http package is a partial port of Go's net/http package, providing
a subset of the full net/http package. There are a few features excluded
during the porting process, in particular:
- No HTTP/2 support
- No TLS support for HTTP servers (no https servers)
- HTTP client request can't be reused
HTTP client methods (http.Get, http.Head, http.Post, and http.PostForm) are
functional. Dial clients support both HTTP and HTTPS URLs.
HTTP server methods and objects are mostly ported, but for HTTP only; HTTPS
servers are not supported.
HTTP request and response handling code is mostly ported, so most the intricacy
of parsing and writing headers is handled as in the full net/http package.
Run ```go doc -all ./src/net/http``` in TinyGo repo to see full listing.
## Using "crypto/tls" Package
TinyGo's TLS support (crypto/tls) relies on hardware offload of the TLS
protocol. This is different from Go's crypto/tls package which handles the TLS
protocol in software.
TinyGo's TLS support is only available for client applications. You can
http.Get() to an https:// address, but you cannot http.ListenAndServeTLS() an
https server.
The offloading hardware has pre-defined TLS certificates built-in.
## Using Sockets
The Netdever interface is a BSD socket-like interface so an application can make direct
socket calls, bypassing the "net" package for the lowest overhead.
Here is a simple TCP client application using direct sockets:
```go
package main
import (
"net" // only need to parse IP address
"tinygo.org/x/drivers/netdev"
"tinygo.org/x/drivers/netlink"
"tinygo.org/x/drivers/netlink/probe"
)
func main() {
// load network driver for target
link, dev := probe.Probe()
// Connect target to IP network
link.NetConnect(&netlink.ConnectParams{
Ssid: "my SSID",
Passphrase: "my passphrase",
})
// omit error handling
sock, _ := dev.Socket(netdev.AF_INET, netdev.SOCK_STREAM, netdev.IPPROTO_TCP)
dev.Connect(sock, "", net.ParseIP("10.0.0.100"), 8080)
dev.Send(sock, []bytes("hello"), 0, 0)
dev.Close(sock)
link.NetDisconnect()
}
```
+4 -99
View File
@@ -3,7 +3,10 @@
[![PkgGoDev](https://pkg.go.dev/badge/tinygo.org/x/drivers)](https://pkg.go.dev/tinygo.org/x/drivers) [![Build](https://github.com/tinygo-org/drivers/actions/workflows/build.yml/badge.svg?branch=dev)](https://github.com/tinygo-org/drivers/actions/workflows/build.yml)
This package provides a collection of hardware drivers for devices such as sensors and displays that can be used together with [TinyGo](https://tinygo.org).
This package provides a collection of 101 different hardware drivers for devices such as sensors and displays that can be used together with [TinyGo](https://tinygo.org).
For the complete list, please see:
https://tinygo.org/docs/reference/devices/
## Installing
@@ -50,104 +53,6 @@ func main() {
}
```
## Currently supported devices
The following 90 devices are supported.
| Device Name | Interface Type |
|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------|
| [ADT7410 I2C Temperature Sensor](https://www.analog.com/media/en/technical-documentation/data-sheets/ADT7410.pdf) | I2C |
| [ADXL345 accelerometer](http://www.analog.com/media/en/technical-documentation/data-sheets/ADXL345.pdf) | I2C |
| [AHT20 I2C Temperature and Humidity Sensor](http://www.aosong.com/userfiles/files/media/AHT20%20%E8%8B%B1%E6%96%87%E7%89%88%E8%AF%B4%E6%98%8E%E4%B9%A6%20A0%2020201222.pdf) | I2C |
| [AMG88xx 8x8 Thermal camera sensor](https://cdn-learn.adafruit.com/assets/assets/000/043/261/original/Grid-EYE_SPECIFICATIONS%28Reference%29.pdf) | I2C |
| [APA102 RGB LED](https://cdn-shop.adafruit.com/product-files/2343/APA102C.pdf) | SPI |
| [APDS9960 Digital proximity, ambient light, RGB and gesture sensor](https://cdn.sparkfun.com/assets/learn_tutorials/3/2/1/Avago-APDS-9960-datasheet.pdf) | I2C |
| [AT24CX 2-wire serial EEPROM](https://www.openimpulse.com/blog/wp-content/uploads/wpsc/downloadables/24C32-Datasheet.pdf) | I2C |
| [AXP192 single Cell Li-Battery and Power System Management](https://github.com/m5stack/M5-Schematic/blob/master/Core/AXP192%20Datasheet_v1.1_en_draft_2211.pdf) | I2C |
| [BBC micro:bit LED matrix](https://github.com/bbcmicrobit/hardware/blob/master/SCH_BBC-Microbit_V1.3B.pdf) | GPIO |
| [BH1750 ambient light sensor](https://www.mouser.com/ds/2/348/bh1750fvi-e-186247.pdf) | I2C |
| [BlinkM RGB LED](http://thingm.com/fileadmin/thingm/downloads/BlinkM_datasheet.pdf) | I2C |
| [BME280 humidity/pressure sensor](https://cdn-shop.adafruit.com/datasheets/BST-BME280_DS001-10.pdf) | I2C |
| [BMI160 accelerometer/gyroscope](https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmi160-ds000.pdf) | SPI |
| [BMP180 barometer](https://cdn-shop.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf) | I2C |
| [BMP280 temperature/barometer](https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp280-ds001.pdf) | I2C |
| [BMP388 pressure sensor](https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp388-ds001.pdf) | I2C |
| [Buzzer](https://en.wikipedia.org/wiki/Buzzer#Piezoelectric) | GPIO |
| [DHTXX thermometer and humidity sensor](https://cdn-shop.adafruit.com/datasheets/Digital+humidity+and+temperature+sensor+AM2302.pdf) | GPIO |
| [DS1307 real time clock](https://datasheets.maximintegrated.com/en/ds/DS1307.pdf) | I2C |
| [DS3231 real time clock](https://datasheets.maximintegrated.com/en/ds/DS3231.pdf) | I2C |
| [ESP32 as WiFi Coprocessor with Arduino nina-fw](https://github.com/arduino/nina-fw) | SPI |
| [ESP8266/ESP32 AT Command set for WiFi/TCP/UDP](https://github.com/espressif/esp32-at) | UART |
| [FT6336 touch controller](https://focuslcds.com/content/FT6236.pdf) | I2C |
| [GPS module](https://www.u-blox.com/en/product/neo-6-series) | I2C/UART |
| [HC-SR04 Ultrasonic distance sensor](https://cdn.sparkfun.com/datasheets/Sensors/Proximity/HCSR04.pdf) | GPIO |
| [HD44780 LCD controller](https://www.sparkfun.com/datasheets/LCD/HD44780.pdf) | GPIO/I2C |
| [HTS221 digital humidity and temperature sensor](https://www.st.com/resource/en/datasheet/hts221.pdf) | I2C |
| [HUB75 RGB led matrix](https://cdn-learn.adafruit.com/downloads/pdf/32x16-32x32-rgb-led-matrix.pdf) | SPI |
| [software I2C driver](https://www.ti.com/lit/an/slva704/slva704.pdf) | GPIO |
| [ILI9341 TFT color display](https://cdn-shop.adafruit.com/datasheets/ILI9341.pdf) | SPI |
| [INA260 Volt/Amp/Power meter](https://www.ti.com/lit/ds/symlink/ina260.pdf) | I2C |
| [Infrared remote control](https://en.wikipedia.org/wiki/Consumer_IR) | GPIO |
| [IS31FL3731 matrix LED driver](https://www.lumissil.com/assets/pdf/core/IS31FL3731_DS.pdf) | I2C |
| [4x4 Membrane Keypad](https://cdn.sparkfun.com/assets/f/f/a/5/0/DS-16038.pdf) | GPIO |
| [L293x motor driver](https://www.ti.com/lit/ds/symlink/l293d.pdf) | GPIO/PWM |
| [L9110x motor driver](https://www.elecrow.com/download/datasheet-l9110.pdf) | GPIO/PWM |
| [LIS2MDL magnetometer](https://www.st.com/resource/en/datasheet/lis2mdl.pdf) | I2C |
| [LIS3DH accelerometer](https://www.st.com/resource/en/datasheet/lis3dh.pdf) | I2C |
| [LPS22HB MEMS nano pressure sensor](https://www.st.com/resource/en/datasheet/dm00140895.pdf) | I2C |
| [LSM6DS3 accelerometer](https://www.st.com/resource/en/datasheet/lsm6ds3.pdf) | I2C |
| [LSM6DSOX accelerometer](https://www.st.com/resource/en/datasheet/lsm6dsox.pdf) | I2C |
| [LSM6DS3TR accelerometer](https://www.st.com/resource/en/datasheet/lsm6ds3tr.pdf) | I2C |
| [LSM303AGR accelerometer](https://www.st.com/resource/en/datasheet/lsm303agr.pdf) | I2C |
| [LSM9DS1 accelerometer](https://www.st.com/resource/en/datasheet/lsm9ds1.pdf) | I2C |
| [Makey Button](https://makeymakey.com/) | GPIO |
| [MAG3110 magnetometer](https://www.nxp.com/docs/en/data-sheet/MAG3110.pdf) | I2C |
| [MAX7219 & MAX7221 display driver](https://datasheets.maximintegrated.com/en/ds/MAX7219-MAX7221.pdf) | SPI |
| [MCP2515 Stand-Alone CAN Controller with SPI Interface](https://ww1.microchip.com/downloads/en/DeviceDoc/MCP2515-Family-Data-Sheet-DS20001801K.pdf) | SPI |
| [MCP3008 analog to digital converter (ADC)](http://ww1.microchip.com/downloads/en/DeviceDoc/21295d.pdf) | SPI |
| [MCP23017 port expander](https://ww1.microchip.com/downloads/en/DeviceDoc/20001952C.pdf) | I2C |
| [Microphone - PDM](https://cdn-learn.adafruit.com/assets/assets/000/049/977/original/MP34DT01-M.pdf) | I2S/PDM |
| [MMA8653 accelerometer](https://www.nxp.com/docs/en/data-sheet/MMA8653FC.pdf) | I2C |
| [MPU6050 accelerometer/gyroscope](https://store.invensense.com/datasheets/invensense/MPU-6050_DataSheet_V3%204.pdf) | I2C |
| [P1AM-100 Base Controller](https://facts-engineering.github.io/modules/P1AM-100/P1AM-100.html) | SPI |
| [PCD8544 display](http://eia.udg.edu/~forest/PCD8544_1.pdf) | SPI |
| [PCF8563 real time clock](https://www.nxp.com/docs/en/data-sheet/PCF8563.pdf) | I2C |
| [QMI8658C accelerometer/gyroscope](https://www.qstcorp.com/upload/pdf/202202/%EF%BC%88%E5%B7%B2%E4%BC%A0%EF%BC%89QMI8658C%20datasheet%20rev%200.9.pdf) | I2C |
| [Resistive Touchscreen (4-wire)](http://ww1.microchip.com/downloads/en/Appnotes/doc8091.pdf) | GPIO |
| [RTL8720DN 2.4G/5G Dual Bands Wireless and BLE5.0](https://www.seeedstudio.com/Realtek8720DN-2-4G-5G-Dual-Bands-Wireless-and-BLE5-0-Combo-Module-p-4442.html) | UART |
| [SCD4x CO2 Sensor](https://sensirion.com/media/documents/C4B87CE6/627C2DCD/CD_DS_SCD40_SCD41_Datasheet_D1.pdf) | I2C |
| [Semihosting](https://wiki.segger.com/Semihosting) | Debug |
| [Servo](https://learn.sparkfun.com/tutorials/hobby-servo-tutorial/all) | PWM |
| [Shift register (PISO)](https://en.wikipedia.org/wiki/Shift_register#Parallel-in_serial-out_\(PISO\)) | GPIO |
| [Shift registers (SIPO)](https://en.wikipedia.org/wiki/Shift_register#Serial-in_parallel-out_(SIPO)) | GPIO |
| [SH1106 OLED display](https://www.velleman.eu/downloads/29/infosheets/sh1106_datasheet.pdf) | I2C / SPI |
| [SHT3x Digital Humidity Sensor](https://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/2_Humidity_Sensors/Datasheets/Sensirion_Humidity_Sensors_SHT3x_Datasheet_digital.pdf) | I2C |
| [SHTC3 Digital Humidity Sensor (RH/T)](https://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/2_Humidity_Sensors/Datasheets/Sensirion_Humidity_Sensors_SHTC3_Datasheet.pdf) | I2C |
| [SPI NOR Flash Memory](https://en.wikipedia.org/wiki/Flash_memory#NOR_flash) | SPI/QSPI |
| [SPI SDCARD/MMC](https://en.wikipedia.org/wiki/SD_card) | SPI |
| [SSD1306 OLED display](https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf) | I2C / SPI |
| [SSD1331 TFT color display](https://www.crystalfontz.com/controllers/SolomonSystech/SSD1331/381/) | SPI |
| [SSD1351 OLED display](https://download.mikroe.com/documents/datasheets/ssd1351-revision-1.3.pdf) | SPI |
| [ST7735 TFT color display](https://www.crystalfontz.com/controllers/Sitronix/ST7735R/319/) | SPI |
| [ST7789 TFT color display](https://cdn-shop.adafruit.com/product-files/3787/3787_tft_QT154H2201__________20190228182902.pdf) | SPI |
| [Stepper motor "Easystepper" controller](https://en.wikipedia.org/wiki/Stepper_motor) | GPIO |
| [Thermistor](https://www.farnell.com/datasheets/33552.pdf) | ADC |
| [TM1637 7-segment LED display](https://www.mcielectronics.cl/website_MCI/static/documents/Datasheet_TM1637.pdf) | I2C |
| [TMP102 I2C Temperature Sensor](https://download.mikroe.com/documents/datasheets/tmp102-data-sheet.pdf) | I2C |
| [UC8151 All-in-one driver IC for ESL](https://www.buydisplay.com/download/ic/UC8151C.pdf) | I2C |
| [VEML6070 UV light sensor](https://www.vishay.com/docs/84277/veml6070.pdf) | I2C |
| [VL53L1X time-of-flight distance sensor](https://www.st.com/resource/en/datasheet/vl53l1x.pdf) | I2C |
| [VL6180X time-of-flight distance sensor](https://www.st.com/resource/en/datasheet/vl6180x.pdf) | I2C |
| [Waveshare 2.13" (B & C) e-paper display](https://www.waveshare.com/w/upload/d/d3/2.13inch-e-paper-b-Specification.pdf) | SPI |
| [Waveshare 2.13" e-paper display](https://www.waveshare.com/w/upload/e/e6/2.13inch_e-Paper_Datasheet.pdf) | SPI |
| [Waveshare 2.9" e-paper display (V1)](https://www.waveshare.com/w/upload/e/e6/2.9inch_e-Paper_Datasheet.pdf) | SPI |
| [Waveshare 4.2" e-paper B/W display](https://www.waveshare.com/w/upload/6/6a/4.2inch-e-paper-specification.pdf) | SPI |
| [Waveshare GC9A01 TFT round display](https://www.waveshare.com/w/upload/5/5e/GC9A01A.pdf) | SPI |
| [WS2812 RGB LED](https://cdn-shop.adafruit.com/datasheets/WS2812.pdf) | GPIO |
| [XPT2046 touch controller](http://grobotronics.com/images/datasheets/xpt2046-datasheet.pdf) | GPIO |
| [Semtech SX126x Lora](https://www.semtech.com/products/wireless-rf/lora-connect/sx1261) | SPI |
| [SSD1289 TFT color display](http://aitendo3.sakura.ne.jp/aitendo_data/product_img/lcd/tft2/M032C1289TP/3.2-SSD1289.pdf) | GPIO |
## Contributing
Your contributions are welcome!
+196
View File
@@ -0,0 +1,196 @@
// Package adafruit4650 implements a driver for the Adafruit FeatherWing OLED - 128x64 OLED display.
// The display is backed itself by a SH1107 driver chip.
//
// Store: https://www.adafruit.com/product/4650
//
// Documentation: https://learn.adafruit.com/adafruit-128x64-oled-featherwing
package adafruit4650
import (
"image/color"
"time"
"tinygo.org/x/drivers"
)
const DefaultAddress = 0x3c
const (
commandSetLowColumn = 0x00
commandSetHighColumn = 0x10
commandSetPage = 0xb0
)
const (
width = 128
height = 64
)
// Device represents an Adafruit 4650 device
type Device struct {
bus drivers.I2C
Address uint8
buffer []byte
width int16
height int16
}
// New creates a new device, not configuring anything yet.
func New(bus drivers.I2C) Device {
return Device{
bus: bus,
Address: DefaultAddress,
width: width,
height: height,
}
}
// Configure initializes the display with default configuration
func (d *Device) Configure() error {
bufferSize := d.width * d.height / 8
d.buffer = make([]byte, bufferSize)
// This sequence is an amalgamation of the datasheet, official Arduino driver, CircuitPython driver and other drivers
initSequence := []byte{
0xae, // display off, sleep mode
//0xd5, 0x41, // set display clock divider (from original datasheet)
0xd5, 0x51, // set display clock divider (from Adafruit driver)
0xd9, 0x22, // pre-charge/dis-charge period mode: 2 DCLKs/2 DCLKs (POR)
0x20, // memory mode
0x81, 0x4f, // contrast setting = 0x4f
0xad, 0x8a, // set dc/dc pump
0xa0, // segment remap, flip-x
0xc0, // common output scan direction
0xdc, 0x00, // set display start line 0 (POR=0)
0xa8, 0x3f, // multiplex ratio, height - 1 = 0x3f
0xd3, 0x60, // set display offset mode = 0x60
0xdb, 0x35, // VCOM deselect level = 0.770 (POR)
0xa4, // entire display off, retain RAM, normal status (POR)
0xa6, // normal (not reversed) display
0xaf, // display on
}
err := d.writeCommands(initSequence)
if err != nil {
return err
}
// recommended in the datasheet, same in other drivers
time.Sleep(100 * time.Millisecond)
return nil
}
// ClearDisplay clears the image buffer as well as the actual display
func (d *Device) ClearDisplay() error {
d.ClearBuffer()
return d.Display()
}
// ClearBuffer clears the buffer
func (d *Device) ClearBuffer() {
bzero(d.buffer)
}
// SetPixel modifies the internal buffer. Since this display has a bit-depth of 1 bit any non-zero
// color component will be treated as 'on', otherwise 'off'.
func (d *Device) SetPixel(x int16, y int16, c color.RGBA) {
if x < 0 || x >= d.width || y < 0 || y >= d.height {
return
}
// RAM layout
// *-----> y
// |
// x| col0 col1 ... col63
// v p0 a0 b0 ..
// a1 b1 ..
// .. .. ..
// a7 b7 ..
// p1 a0 b0
// a1 b1
//
//flip y - so the display orientation matches the silk screen labeling etc.
y = d.height - y - 1
page := x / 8
bytesPerPage := d.height
byteIndex := y + bytesPerPage*page
bit := x % 8
if (c.R | c.G | c.B) != 0 {
d.buffer[byteIndex] |= 1 << uint8(bit)
} else {
d.buffer[byteIndex] &^= 1 << uint8(bit)
}
}
// Display sends the whole buffer to the screen
func (d *Device) Display() error {
bytesPerPage := d.height
pages := (d.width + 7) / 8
for page := int16(0); page < pages; page++ {
err := d.setRAMPosition(uint8(page), 0)
if err != nil {
return err
}
offset := page * bytesPerPage
err = d.writeRAM(d.buffer[offset : offset+bytesPerPage])
if err != nil {
return err
}
}
return nil
}
// setRAMPosition updates the device's current page and column position
func (d *Device) setRAMPosition(page uint8, column uint8) error {
if page > 15 {
panic("page out of bounds")
}
if column > 127 {
panic("column out of bounds")
}
setPage := commandSetPage | (page & 0xF)
lo := column & 0xF
setLowColumn := commandSetLowColumn | lo
hi := (column >> 4) & 0x7
setHighColumn := commandSetHighColumn | hi
cmds := []byte{
setPage,
setLowColumn,
setHighColumn,
}
return d.writeCommands(cmds)
}
// Size returns the current size of the display.
func (d *Device) Size() (w, h int16) {
return d.width, d.height
}
func (d *Device) writeCommands(commands []byte) error {
onlyCommandsFollowing := byte(0x00)
return d.bus.Tx(uint16(d.Address), append([]byte{onlyCommandsFollowing}, commands...), nil)
}
func (d *Device) writeRAM(data []byte) error {
onlyRAMFollowing := byte(0x40)
return d.bus.Tx(uint16(d.Address), append([]byte{onlyRAMFollowing}, data...), nil)
}
func bzero(buf []byte) {
for i := range buf {
buf[i] = 0
}
}
+176
View File
@@ -0,0 +1,176 @@
package adafruit4650
import (
"bytes"
_ "embed"
"encoding/hex"
"fmt"
"image"
"image/color"
"image/draw"
"image/png"
"os"
"testing"
"time"
"tinygo.org/x/drivers"
"tinygo.org/x/tinyfont"
"tinygo.org/x/tinyfont/freemono"
)
//go:embed expected_hello_world.png
var expectedHelloWorld []byte
// mockBus mocks a fake i2c device adafruit4650 display.
// The memory layout assumes that clients set up the device in a particular way and always send complete
// pages to the device buffer.
type mockBus struct {
img draw.Image
line int
addr uint8
currentPage int
currentColumn int
}
func (m *mockBus) Tx(addr uint16, w, r []byte) error {
if addr != uint16(m.addr) {
panic("unexpected address")
}
if r != nil {
panic("mock does not support reads")
}
if w[0] == 0x00 {
if w[1]&0xf0 == 0xb0 {
m.currentPage = int(w[1] & 0x0f)
lo := w[2] & 0x0f
hi := w[2] & 0x07
m.currentColumn = int(hi<<4 | lo)
}
return nil
}
if w[0] != 0x40 {
panic("unexpected first byte: " + hex.EncodeToString(w[0:1]))
}
return m.writeRAM(w[1:])
}
func newMock() *mockBus {
m := image.NewRGBA(image.Rect(0, 0, width, height))
return &mockBus{img: m, addr: DefaultAddress, currentPage: -1, currentColumn: -1}
}
func (m *mockBus) writeRAM(data []byte) error {
// RAM layout
// *-----> y
// |
// x| col0 col1 ... col63
// v p0 a0 b0 ..
// a1 b1 ..
// .. .. ..
// a7 b7 ..
// p1 a0 b0
// a1 b1
//
fmt.Printf("writing page %d\n", m.currentPage)
// assuming entire pages will be written
for x := 0; x < 8; x++ {
for y := 0; y < height; y++ {
col := data[y]
c := color.Black
if col&(1<<x) != 0 {
c = color.White
}
m.img.Set(x+m.currentPage*8, height-y-1, c)
}
}
return nil
}
func (m *mockBus) toImage() *image.RGBA {
container := image.NewRGBA(m.img.Bounds().Inset(-1))
draw.Draw(container, container.Bounds(), image.NewUniform(color.RGBA{G: 255, A: 255}), image.Point{}, draw.Over)
draw.Draw(container, m.img.Bounds(), m.img, image.Point{}, draw.Over)
return container
}
func TestDevice_Display(t *testing.T) {
bus := newMock()
dev := New(bus)
dev.Configure()
drawPlus(&dev)
drawHellowWorld(&dev)
//when
dev.Display()
//then
actual := bus.toImage()
expected, err := png.Decode(bytes.NewReader(expectedHelloWorld))
if err != nil {
panic(err)
}
assertEqualImages(t, actual, expected)
}
func drawPlus(d drivers.Displayer) {
for i := int16(0); i < 128; i++ {
d.SetPixel(i, 32, color.RGBA{R: 1})
}
for i := int16(0); i < 64; i++ {
d.SetPixel(64, i, color.RGBA{R: 1})
}
}
func drawHellowWorld(d drivers.Displayer) {
tinyfont.WriteLine(d, &freemono.Regular9pt7b, 0, 32, "Hello World!", color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff})
}
func assertEqualImages(t testing.TB, actual, expected image.Image) {
if actual.Bounds().Dx() != expected.Bounds().Dx() || actual.Bounds().Dy() != expected.Bounds().Dy() {
f := writeImage(actual)
t.Fatalf("differing size: was %v, expected %v, saved actual to %s", actual.Bounds(), expected.Bounds(), f)
}
bb := expected.Bounds()
for x := bb.Min.X; x < bb.Max.X; x++ {
for y := bb.Min.Y; y < bb.Max.Y; y++ {
actualBB := actual.Bounds()
if actual.At(x+actualBB.Min.X, y+actualBB.Min.Y) != expected.At(x, y) {
f := writeImage(actual)
t.Fatalf("different pixel at %d/%d: %v != %v, saved actual at %s", x, y, actual.At(x, y), expected.At(x, y), f)
}
}
}
}
func writeImage(img image.Image) string {
fn := fmt.Sprintf("%d.png", time.Now().Unix())
f, err := os.OpenFile(fn, os.O_RDWR|os.O_CREATE, 0644)
if err != nil {
panic(err)
}
defer f.Close()
err = png.Encode(f, img)
if err != nil {
panic(err)
}
return fn
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 449 B

+4 -3
View File
@@ -7,6 +7,7 @@ import (
"time"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
)
type Error uint8
@@ -54,7 +55,7 @@ func (d *Device) Configure() (err error) {
// Connected returns whether sensor has been found.
func (d *Device) Connected() bool {
data := []byte{0}
d.bus.ReadRegister(uint8(d.Address), RegID, data)
legacy.ReadRegister(d.bus, uint8(d.Address), RegID, data)
return data[0]&0xF8 == 0xC8
}
@@ -81,11 +82,11 @@ func (d *Device) writeByte(reg uint8, data byte) {
}
func (d *Device) readByte(reg uint8) byte {
d.bus.ReadRegister(d.Address, reg, d.buf)
legacy.ReadRegister(d.bus, d.Address, reg, d.buf)
return d.buf[0]
}
func (d *Device) readUint16(reg uint8) uint16 {
d.bus.ReadRegister(d.Address, reg, d.buf)
legacy.ReadRegister(d.bus, d.Address, reg, d.buf)
return uint16(d.buf[0])<<8 | uint16(d.buf[1])
}
+13 -10
View File
@@ -5,7 +5,10 @@
// Datasheet JP: http://www.analog.com/media/jp/technical-documentation/data-sheets/ADXL345_jp.pdf
package adxl345 // import "tinygo.org/x/drivers/adxl345"
import "tinygo.org/x/drivers"
import (
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
)
type Range uint8
type Rate uint8
@@ -68,21 +71,21 @@ func New(bus drivers.I2C) Device {
// Configure sets up the device for communication
func (d *Device) Configure() {
d.bus.WriteRegister(uint8(d.Address), REG_BW_RATE, []byte{d.bwRate.toByte()})
d.bus.WriteRegister(uint8(d.Address), REG_POWER_CTL, []byte{d.powerCtl.toByte()})
d.bus.WriteRegister(uint8(d.Address), REG_DATA_FORMAT, []byte{d.dataFormat.toByte()})
legacy.WriteRegister(d.bus, uint8(d.Address), REG_BW_RATE, []byte{d.bwRate.toByte()})
legacy.WriteRegister(d.bus, uint8(d.Address), REG_POWER_CTL, []byte{d.powerCtl.toByte()})
legacy.WriteRegister(d.bus, uint8(d.Address), REG_DATA_FORMAT, []byte{d.dataFormat.toByte()})
}
// Halt stops the sensor, values will not updated
func (d *Device) Halt() {
d.powerCtl.measure = 0
d.bus.WriteRegister(uint8(d.Address), REG_POWER_CTL, []byte{d.powerCtl.toByte()})
legacy.WriteRegister(d.bus, uint8(d.Address), REG_POWER_CTL, []byte{d.powerCtl.toByte()})
}
// Restart makes reading the sensor working again after a halt
func (d *Device) Restart() {
d.powerCtl.measure = 1
d.bus.WriteRegister(uint8(d.Address), REG_POWER_CTL, []byte{d.powerCtl.toByte()})
legacy.WriteRegister(d.bus, uint8(d.Address), REG_POWER_CTL, []byte{d.powerCtl.toByte()})
}
// ReadAcceleration reads the current acceleration from the device and returns
@@ -103,7 +106,7 @@ func (d *Device) ReadAcceleration() (x int32, y int32, z int32, err error) {
// from the adxl345.
func (d *Device) ReadRawAcceleration() (x int32, y int32, z int32) {
data := []byte{0, 0, 0, 0, 0, 0}
d.bus.ReadRegister(uint8(d.Address), REG_DATAX0, data)
legacy.ReadRegister(d.bus, uint8(d.Address), REG_DATAX0, data)
x = readIntLE(data[0], data[1])
y = readIntLE(data[2], data[3])
@@ -119,20 +122,20 @@ func (d *Device) UseLowPower(power bool) {
} else {
d.bwRate.lowPower = 0
}
d.bus.WriteRegister(uint8(d.Address), REG_BW_RATE, []byte{d.bwRate.toByte()})
legacy.WriteRegister(d.bus, uint8(d.Address), REG_BW_RATE, []byte{d.bwRate.toByte()})
}
// SetRate change the current rate of the sensor
func (d *Device) SetRate(rate Rate) bool {
d.bwRate.rate = rate & 0x0F
d.bus.WriteRegister(uint8(d.Address), REG_BW_RATE, []byte{d.bwRate.toByte()})
legacy.WriteRegister(d.bus, uint8(d.Address), REG_BW_RATE, []byte{d.bwRate.toByte()})
return true
}
// SetRange change the current range of the sensor
func (d *Device) SetRange(sensorRange Range) bool {
d.dataFormat.sensorRange = sensorRange & 0x03
d.bus.WriteRegister(uint8(d.Address), REG_DATA_FORMAT, []byte{d.dataFormat.toByte()})
legacy.WriteRegister(d.bus, uint8(d.Address), REG_DATA_FORMAT, []byte{d.dataFormat.toByte()})
return true
}
+17 -16
View File
@@ -8,6 +8,7 @@ import (
"time"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
)
// Device wraps an I2C connection to a AMG88xx device.
@@ -48,7 +49,7 @@ func (d *Device) Configure(cfg Config) {
// ReadPixels returns the 64 values (8x8 grid) of the sensor converted to millicelsius
func (d *Device) ReadPixels(buffer *[64]int16) {
d.bus.ReadRegister(uint8(d.Address), PIXEL_OFFSET, d.data)
legacy.ReadRegister(d.bus, uint8(d.Address), PIXEL_OFFSET, d.data)
for i := 0; i < 64; i++ {
buffer[i] = int16((uint16(d.data[2*i+1]) << 8) | uint16(d.data[2*i]))
if (buffer[i] & (1 << 11)) > 0 { // temperature negative
@@ -61,17 +62,17 @@ func (d *Device) ReadPixels(buffer *[64]int16) {
// SetPCTL sets the PCTL
func (d *Device) SetPCTL(pctl uint8) {
d.bus.WriteRegister(uint8(d.Address), PCTL, []byte{pctl})
legacy.WriteRegister(d.bus, uint8(d.Address), PCTL, []byte{pctl})
}
// SetReset sets the reset value
func (d *Device) SetReset(rst uint8) {
d.bus.WriteRegister(uint8(d.Address), RST, []byte{rst})
legacy.WriteRegister(d.bus, uint8(d.Address), RST, []byte{rst})
}
// SetFrameRate configures the frame rate
func (d *Device) SetFrameRate(framerate uint8) {
d.bus.WriteRegister(uint8(d.Address), FPSC, []byte{framerate & 0x01})
legacy.WriteRegister(d.bus, uint8(d.Address), FPSC, []byte{framerate & 0x01})
}
// SetMovingAverageMode sets the moving average mode
@@ -80,7 +81,7 @@ func (d *Device) SetMovingAverageMode(mode bool) {
if mode {
value = 1
}
d.bus.WriteRegister(uint8(d.Address), AVE, []byte{value << 5})
legacy.WriteRegister(d.bus, uint8(d.Address), AVE, []byte{value << 5})
}
// SetInterruptLevels sets the interrupt levels
@@ -97,8 +98,8 @@ func (d *Device) SetInterruptLevelsHysteresis(high int16, low int16, hysteresis
if high > 4095 {
high = 4095
}
d.bus.WriteRegister(uint8(d.Address), INTHL, []byte{uint8(high & 0xFF)})
d.bus.WriteRegister(uint8(d.Address), INTHL, []byte{uint8((high & 0xFF) >> 4)})
legacy.WriteRegister(d.bus, uint8(d.Address), INTHL, []byte{uint8(high & 0xFF)})
legacy.WriteRegister(d.bus, uint8(d.Address), INTHL, []byte{uint8((high & 0xFF) >> 4)})
low = low / PIXEL_TEMP_CONVERSION
if low < -4095 {
@@ -107,8 +108,8 @@ func (d *Device) SetInterruptLevelsHysteresis(high int16, low int16, hysteresis
if low > 4095 {
low = 4095
}
d.bus.WriteRegister(uint8(d.Address), INTHL, []byte{uint8(low & 0xFF)})
d.bus.WriteRegister(uint8(d.Address), INTHL, []byte{uint8((low & 0xFF) >> 4)})
legacy.WriteRegister(d.bus, uint8(d.Address), INTHL, []byte{uint8(low & 0xFF)})
legacy.WriteRegister(d.bus, uint8(d.Address), INTHL, []byte{uint8((low & 0xFF) >> 4)})
hysteresis = hysteresis / PIXEL_TEMP_CONVERSION
if hysteresis < -4095 {
@@ -117,32 +118,32 @@ func (d *Device) SetInterruptLevelsHysteresis(high int16, low int16, hysteresis
if hysteresis > 4095 {
hysteresis = 4095
}
d.bus.WriteRegister(uint8(d.Address), INTHL, []byte{uint8(hysteresis & 0xFF)})
d.bus.WriteRegister(uint8(d.Address), INTHL, []byte{uint8((hysteresis & 0xFF) >> 4)})
legacy.WriteRegister(d.bus, uint8(d.Address), INTHL, []byte{uint8(hysteresis & 0xFF)})
legacy.WriteRegister(d.bus, uint8(d.Address), INTHL, []byte{uint8((hysteresis & 0xFF) >> 4)})
}
// EnableInterrupt enables the interrupt pin on the device
func (d *Device) EnableInterrupt() {
d.interruptEnable = 1
d.bus.WriteRegister(uint8(d.Address), INTC, []byte{((uint8(d.interruptMode) << 1) | d.interruptEnable) & 0x03})
legacy.WriteRegister(d.bus, uint8(d.Address), INTC, []byte{((uint8(d.interruptMode) << 1) | d.interruptEnable) & 0x03})
}
// DisableInterrupt disables the interrupt pin on the device
func (d *Device) DisableInterrupt() {
d.interruptEnable = 0
d.bus.WriteRegister(uint8(d.Address), INTC, []byte{((uint8(d.interruptMode) << 1) | d.interruptEnable) & 0x03})
legacy.WriteRegister(d.bus, uint8(d.Address), INTC, []byte{((uint8(d.interruptMode) << 1) | d.interruptEnable) & 0x03})
}
// SetInterruptMode sets the interrupt mode
func (d *Device) SetInterruptMode(mode InterruptMode) {
d.interruptMode = mode
d.bus.WriteRegister(uint8(d.Address), INTC, []byte{((uint8(d.interruptMode) << 1) | d.interruptEnable) & 0x03})
legacy.WriteRegister(d.bus, uint8(d.Address), INTC, []byte{((uint8(d.interruptMode) << 1) | d.interruptEnable) & 0x03})
}
// GetInterrupt reads the state of the triggered interrupts
func (d *Device) GetInterrupt() []uint8 {
data := make([]uint8, 8)
d.bus.ReadRegister(uint8(d.Address), INT_OFFSET, data)
legacy.ReadRegister(d.bus, uint8(d.Address), INT_OFFSET, data)
return data
}
@@ -154,6 +155,6 @@ func (d *Device) ClearInterrupt() {
// ReadThermistor reads the onboard thermistor
func (d *Device) ReadThermistor() int16 {
data := make([]uint8, 2)
d.bus.ReadRegister(uint8(d.Address), TTHL, data)
legacy.ReadRegister(d.bus, uint8(d.Address), TTHL, data)
return (int16((uint16(data[1])<<8)|uint16(data[0])) * THERMISTOR_CONVERSION) / 10
}
+26 -25
View File
@@ -8,6 +8,7 @@ import (
"time"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
)
// Device wraps an I2C connection to a APDS-9960 device.
@@ -68,7 +69,7 @@ func New(bus drivers.I2C) Device {
// It does a "who am I" request and checks the response.
func (d *Device) Connected() bool {
data := []byte{0}
d.bus.ReadRegister(d.Address, APDS9960_ID_REG, data)
legacy.ReadRegister(d.bus, d.Address, APDS9960_ID_REG, data)
return data[0] == 0xAB
}
@@ -80,7 +81,7 @@ func (d *Device) GetMode() uint8 {
// DisableAll turns off the device and all functions
func (d *Device) DisableAll() {
d.enable(enableConfig{})
d.bus.WriteRegister(d.Address, APDS9960_GCONF4_REG, []byte{0x00})
legacy.WriteRegister(d.bus, d.Address, APDS9960_GCONF4_REG, []byte{0x00})
d.mode = MODE_NONE
d.gesture.detected = GESTURE_NONE
}
@@ -88,13 +89,13 @@ func (d *Device) DisableAll() {
// SetProximityPulse sets proximity pulse length (4, 8, 16, 32) and count (1~64)
// default: 16, 64
func (d *Device) SetProximityPulse(length, count uint8) {
d.bus.WriteRegister(d.Address, APDS9960_PPULSE_REG, []byte{getPulseLength(length)<<6 | getPulseCount(count)})
legacy.WriteRegister(d.bus, d.Address, APDS9960_PPULSE_REG, []byte{getPulseLength(length)<<6 | getPulseCount(count)})
}
// SetGesturePulse sets gesture pulse length (4, 8, 16, 32) and count (1~64)
// default: 16, 64
func (d *Device) SetGesturePulse(length, count uint8) {
d.bus.WriteRegister(d.Address, APDS9960_GPULSE_REG, []byte{getPulseLength(length)<<6 | getPulseCount(count)})
legacy.WriteRegister(d.bus, d.Address, APDS9960_GPULSE_REG, []byte{getPulseLength(length)<<6 | getPulseCount(count)})
}
// SetADCIntegrationCycles sets ALS/color ADC internal integration cycles (1~256, 1 cycle = 2.78 ms)
@@ -103,14 +104,14 @@ func (d *Device) SetADCIntegrationCycles(cycles uint16) {
if cycles > 256 {
cycles = 256
}
d.bus.WriteRegister(d.Address, APDS9960_ATIME_REG, []byte{uint8(256 - cycles)})
legacy.WriteRegister(d.bus, d.Address, APDS9960_ATIME_REG, []byte{uint8(256 - cycles)})
}
// SetGains sets proximity/gesture gain (1, 2, 4, 8x) and ALS/color gain (1, 4, 16, 64x)
// default: 1, 1, 4
func (d *Device) SetGains(proximityGain, gestureGain, colorGain uint8) {
d.bus.WriteRegister(d.Address, APDS9960_CONTROL_REG, []byte{getProximityGain(proximityGain)<<2 | getALSGain(colorGain)})
d.bus.WriteRegister(d.Address, APDS9960_GCONF2_REG, []byte{getProximityGain(gestureGain) << 5})
legacy.WriteRegister(d.bus, d.Address, APDS9960_CONTROL_REG, []byte{getProximityGain(proximityGain)<<2 | getALSGain(colorGain)})
legacy.WriteRegister(d.bus, d.Address, APDS9960_GCONF2_REG, []byte{getProximityGain(gestureGain) << 5})
}
// LEDBoost sets proximity and gesture LED current level (100, 150, 200, 300 (%))
@@ -127,7 +128,7 @@ func (d *Device) LEDBoost(percent uint16) {
case 300:
v = 3
}
d.bus.WriteRegister(d.Address, APDS9960_CONFIG2_REG, []byte{0x01 | v<<4})
legacy.WriteRegister(d.bus, d.Address, APDS9960_CONFIG2_REG, []byte{0x01 | v<<4})
}
// Setthreshold sets threshold (0~255) for detecting gestures
@@ -168,7 +169,7 @@ func (d *Device) ReadProximity() (proximity int32) {
return 0
}
data := []byte{0}
d.bus.ReadRegister(d.Address, APDS9960_PDATA_REG, data)
legacy.ReadRegister(d.bus, d.Address, APDS9960_PDATA_REG, data)
return 255 - int32(data[0])
}
@@ -195,14 +196,14 @@ func (d *Device) ReadColor() (r int32, g int32, b int32, clear int32) {
return
}
data := []byte{0, 0, 0, 0, 0, 0, 0, 0}
d.bus.ReadRegister(d.Address, APDS9960_CDATAL_REG, data[:1])
d.bus.ReadRegister(d.Address, APDS9960_CDATAH_REG, data[1:2])
d.bus.ReadRegister(d.Address, APDS9960_RDATAL_REG, data[2:3])
d.bus.ReadRegister(d.Address, APDS9960_RDATAH_REG, data[3:4])
d.bus.ReadRegister(d.Address, APDS9960_GDATAL_REG, data[4:5])
d.bus.ReadRegister(d.Address, APDS9960_GDATAH_REG, data[5:6])
d.bus.ReadRegister(d.Address, APDS9960_BDATAL_REG, data[6:7])
d.bus.ReadRegister(d.Address, APDS9960_BDATAH_REG, data[7:])
legacy.ReadRegister(d.bus, d.Address, APDS9960_CDATAL_REG, data[:1])
legacy.ReadRegister(d.bus, d.Address, APDS9960_CDATAH_REG, data[1:2])
legacy.ReadRegister(d.bus, d.Address, APDS9960_RDATAL_REG, data[2:3])
legacy.ReadRegister(d.bus, d.Address, APDS9960_RDATAH_REG, data[3:4])
legacy.ReadRegister(d.bus, d.Address, APDS9960_GDATAL_REG, data[4:5])
legacy.ReadRegister(d.bus, d.Address, APDS9960_GDATAH_REG, data[5:6])
legacy.ReadRegister(d.bus, d.Address, APDS9960_BDATAL_REG, data[6:7])
legacy.ReadRegister(d.bus, d.Address, APDS9960_BDATAH_REG, data[7:])
clear = int32(uint16(data[1])<<8 | uint16(data[0]))
r = int32(uint16(data[3])<<8 | uint16(data[2]))
g = int32(uint16(data[5])<<8 | uint16(data[4]))
@@ -234,13 +235,13 @@ func (d *Device) GestureAvailable() bool {
data := []byte{0, 0, 0, 0}
// check GVALID
d.bus.ReadRegister(d.Address, APDS9960_GSTATUS_REG, data[:1])
legacy.ReadRegister(d.bus, d.Address, APDS9960_GSTATUS_REG, data[:1])
if data[0]&0x01 == 0 {
return false
}
// get number of data sets available in FIFO
d.bus.ReadRegister(d.Address, APDS9960_GFLVL_REG, data[:1])
legacy.ReadRegister(d.bus, d.Address, APDS9960_GFLVL_REG, data[:1])
availableDataSets := data[0]
if availableDataSets == 0 {
return false
@@ -249,10 +250,10 @@ func (d *Device) GestureAvailable() bool {
// read up, down, left and right proximity data from FIFO
var dataSets [32][4]uint8
for i := uint8(0); i < availableDataSets; i++ {
d.bus.ReadRegister(d.Address, APDS9960_GFIFO_U_REG, data[:1])
d.bus.ReadRegister(d.Address, APDS9960_GFIFO_D_REG, data[1:2])
d.bus.ReadRegister(d.Address, APDS9960_GFIFO_L_REG, data[2:3])
d.bus.ReadRegister(d.Address, APDS9960_GFIFO_R_REG, data[3:4])
legacy.ReadRegister(d.bus, d.Address, APDS9960_GFIFO_U_REG, data[:1])
legacy.ReadRegister(d.bus, d.Address, APDS9960_GFIFO_D_REG, data[1:2])
legacy.ReadRegister(d.bus, d.Address, APDS9960_GFIFO_L_REG, data[2:3])
legacy.ReadRegister(d.bus, d.Address, APDS9960_GFIFO_R_REG, data[3:4])
for j := uint8(0); j < 4; j++ {
dataSets[i][j] = data[j]
}
@@ -385,7 +386,7 @@ func (d *Device) enable(cfg enableConfig) {
}
data := []byte{gen<<6 | pien<<5 | aien<<4 | wen<<3 | pen<<2 | aen<<1 | pon}
d.bus.WriteRegister(d.Address, APDS9960_ENABLE_REG, data)
legacy.WriteRegister(d.bus, d.Address, APDS9960_ENABLE_REG, data)
if cfg.PON {
time.Sleep(time.Millisecond * 10)
@@ -394,7 +395,7 @@ func (d *Device) enable(cfg enableConfig) {
func (d *Device) readStatus(param string) bool {
data := []byte{0}
d.bus.ReadRegister(d.Address, APDS9960_STATUS_REG, data)
legacy.ReadRegister(d.bus, d.Address, APDS9960_STATUS_REG, data)
switch param {
case "CPSAT":
+172
View File
@@ -0,0 +1,172 @@
// Product: https://ams.com/as5600
// Datasheet: https://ams.com/documents/20143/36005/AS5600_DS000365_5-00.pdf
package as560x // import tinygo.org/x/drivers/ams560x
import (
"time"
"tinygo.org/x/drivers"
)
// AS5600 includes MPOS & MANG in addition to ZPOS to set a 'narrower angle range'
// ZPOS enables setting the 'zero position' of the device to any RAW_ANGLE value.
// MPOS ('max position') & MANG 'max angle' enable a 'partial range' on the AS5600.
// The value in ANGLE is scaled & adjusted by the device according to ZPOS and MPOS/MANG.
// The entire 12-bit range is 'compressed' into the RAW_ANGLE range of ZPOS->MPOS
// (or ZPOS->ZPOS+MANG) thus enabling a higher resolution for a partial range.
// if ZPOS > MPOS (or ZPOS + MANG > 4095) i.e. the incremental range 'crosses zero'
// then the device will automatically compensate for the correct range.
// For RAW_ANGLE values outside of the partial range, ANGLE will be 'capped' at either
// 0 or 4095, depending on 'which end of the partial range is closer.'
// AS5600Device represents an ams AS5600 device driver accessed over I2C
type AS5600Device struct {
// promote BaseDevice
BaseDevice
}
// NewAS5600 creates a new AS5600Device given an I2C bus
func NewAS5600(bus drivers.I2C) AS5600Device {
// Create base device
baseDev := newBaseDevice(bus)
// Add AS5600 specific registers
baseDev.registers[MPOS] = newI2CRegister(MPOS, 0, 0xfff, 2, reg_read|reg_write|reg_program)
baseDev.registers[MANG] = newI2CRegister(MANG, 0, 0xfff, 2, reg_read|reg_write|reg_program)
// Add AS5600 specific 'virtual registers'
conf, ok := baseDev.registers[CONF]
if ok {
baseDev.registers[PWMF] = newVirtualRegister(conf, 6, 0b11)
baseDev.registers[OUTS] = newVirtualRegister(conf, 4, 0b11)
}
// Return the device
return AS5600Device{baseDev}
}
// Configure sets up the AMS AS5600 sensor device with the given configuration.
func (d *AS5600Device) Configure(cfg Config) error {
// Call the BaseDevice method to do the actual Configure
d.BaseDevice.Configure(cfg)
// For AS5600 devices we need to calculate the maxAngle on startup from ZPOS/MPOS/MANG
// These could have been permanently BURN'ed (by writing BURN register with BURN_ANGLE/BURN_SETTING)
// or may have already been written in previous runs without a power cycle since.
mpos, err := d.ReadRegister(MPOS)
if nil != err {
return err
}
mang, err := d.ReadRegister(MANG)
if nil != err {
return err
}
// Read ZPOS for side effect of caching only so that next calculateEffectiveMaxAngle() can't fail
if _, err = d.ReadRegister(ZPOS); nil != err {
return err
}
if mpos != 0 {
// If MPOS is set, use MPOS regardless of MANG
err = d.calculateEffectiveMaxAngle(MPOS, mpos)
} else if mang != 0 {
// If MANG is set and MPOS == 0, use MANG
err = d.calculateEffectiveMaxAngle(MANG, mang)
} else {
// if neither is set, we have no narrow range
d.maxAngle = NATIVE_ANGLE_RANGE
}
return err
}
// calculateEffectiveMaxAngle calculates d.maxAngle after one of ZPOS/MPOS/MANG have been written
func (d *AS5600Device) calculateEffectiveMaxAngle(register uint8, value uint16) error {
var zpos, mpos uint16 = 0, 0
var err error = nil
switch register {
case MANG:
d.maxAngle = value // The easy case
return nil
case ZPOS:
zpos = value
mpos, err = d.ReadRegister(MPOS)
case MPOS:
mpos = value
zpos, err = d.ReadRegister(ZPOS)
default:
panic("calculateEffectiveMaxAngle() can only work from ZPOS, MPOS or MANG")
}
if nil != err {
return err
}
// MANG is effectively MPOS-ZPOS
mang := int(mpos) - int(zpos)
// correct for mpos < zpos
if mang < 0 {
mang += NATIVE_ANGLE_RANGE
}
d.maxAngle = uint16(mang)
return nil
}
// WriteRegister writes the given value for the given register to the AS560x device via I2C
func (d *AS5600Device) WriteRegister(address uint8, value uint16) error {
// Call the BaseDevice method to do the actual write
if err := d.BaseDevice.WriteRegister(address, value); err != nil {
return err
}
// When either ZPOS/MANG/MPOS are set we need to recalculate maxAngle
// We also may need to invalidate some cached values for the other two registers
recalc := false
switch address {
case ZPOS:
// Setting a new ZPOS invalidates MPOS but not MANG
d.registers[MPOS].invalidate()
recalc = true
case MPOS:
// Setting a new MPOS invalidates MANG but not ZPOS
d.registers[MANG].invalidate()
recalc = true
case MANG:
// Setting a new MANG invalidates MPOS but not ZPOS
d.registers[MPOS].invalidate()
recalc = true
}
if recalc {
// Datasheet tells us to wait at least 1ms before reading back
time.Sleep(time.Millisecond * 10) // conservative wait
return d.calculateEffectiveMaxAngle(address, value)
}
return nil
}
// GetMaxPosition returns the 'max position' (MPOS) in different units
func (d *AS5600Device) GetMaxPosition(units AngleUnit) (uint16, float32, error) {
mpos, err := d.ReadRegister(MPOS)
if nil != err {
return 0, 0.0, err
}
// Convert to requested units
i, f := convertFromNativeAngle(mpos, NATIVE_ANGLE_RANGE, units)
return i, f, nil
}
// SetMaxPosition sets the 'max position' (MPOS) in different units
func (d *AS5600Device) SetMaxPosition(mpos float32, units AngleUnit) error {
return d.WriteRegister(MPOS, convertToNativeAngle(mpos, units))
}
// GetMaxAngle returns the 'max position' (MANG) in different units
func (d *AS5600Device) GetMaxAngle(units AngleUnit) (uint16, float32, error) {
mang, err := d.ReadRegister(MANG)
if nil != err {
return 0, 0.0, err
}
// Convert to requested units
i, f := convertFromNativeAngle(mang, NATIVE_ANGLE_RANGE, units)
return i, f, nil
}
// SetMaxAngle sets the 'max angle' (MANG) in different units
func (d *AS5600Device) SetMaxAngle(mang float32, units AngleUnit) error {
return d.WriteRegister(MANG, convertToNativeAngle(mang, units))
}
+22
View File
@@ -0,0 +1,22 @@
// Product: https://ams.com/as5601
// Datasheet: https://ams.com/documents/20143/36005/AS5601_DS000395_3-00.pdf
package as560x // import tinygo.org/x/drivers/ams560x
import "tinygo.org/x/drivers"
// AS5601Device represents an ams AS5601 device driver accessed over I2C
type AS5601Device struct {
BaseDevice // promote base device
}
// NewAS5601 creates a new AS5601Device given an I2C bus
func NewAS5601(bus drivers.I2C) AS5601Device {
// Create base device
baseDev := newBaseDevice(bus)
// Add AS5601 specific registers
baseDev.registers[ABN] = newI2CRegister(ABN, 0, 0b1111, 1, reg_read|reg_write|reg_program)
baseDev.registers[PUSHTHR] = newI2CRegister(PUSHTHR, 0, 0xff, 1, reg_read|reg_write|reg_program)
// Return the device
return AS5601Device{baseDev}
}
+198
View File
@@ -0,0 +1,198 @@
// Package as560x implements drivers for the ams AS5600/AS5601 on-axis magnetic rotary position sensors
//
// Product Pages:
// AS5600: https://ams.com/as5600
// AS5601: https://ams.com/as5601
//
// Datasheets:
// AS5600: https://ams.com/documents/20143/36005/AS5600_DS000365_5-00.pdf
// AS5601: https://ams.com/documents/20143/36005/AS5601_DS000395_3-00.pdf
//
package as560x // import tinygo.org/x/drivers/ams560x
import (
"errors"
"tinygo.org/x/drivers"
)
// Config holds the configuration for the AMS AS560x sensor devices.
type Config struct {
// Address is the I2C address of the AS560x device. If left zero this will default to 0x36
Address uint8
}
// MagnetStrength is an enum to indicate the magnetic field strength detected by the AS560x sensors.
type MagnetStrength int
const (
// MagnetTooWeak indicates that the magnet strength is too weak (AGC maximum gain overflow) - move it closer
MagnetTooWeak MagnetStrength = iota - 1
// MagnetOk indicates that the magnet strength is about right.
MagnetOk
// MagnetTooStrong indicates that the magnet strength is too strong (AGC minimum gain overflow) - move it further away
MagnetTooStrong
)
// AngleUnit is an enum to allow the use of different units when reading/writing angles from the AS560x sensors.
type AngleUnit int
const (
// ANGLE_NATIVE uses the device's native angle measurement. i.e. 12-bit integer, 0 <= angle <= 0xfff (4095)
ANGLE_NATIVE AngleUnit = iota
// ANGLE_DEGREES_INT measures angles in degrees using integer arithmetic for speed. i.e. 0 <= angle < 360
ANGLE_DEGREES_INT
// ANGLE_DEGREES_FLOAT measures angles in degrees using floating point (slower). i.e. 0.0 <= angle < 360.0
ANGLE_DEGREES_FLOAT
// ANGLE_RADIANS measures angles in radians using floating point (slower). i.e. 0.0 <= angle < 2 * PI
ANGLE_RADIANS
)
const (
// NATIVE_ANGLE_MAX is the maximum valid value for a native angle for a AS560x device
NATIVE_ANGLE_MAX = (1 << 12) - 1 + iota
// NATIVE_ANGLE_RANGE is the number of unique values for native angles for a AS560x device
NATIVE_ANGLE_RANGE
)
var (
errRegisterNotFound = errors.New("Register not found")
errMaxBurnAngle = errors.New("Max BURN_ANGLE limit reached")
)
// BaseDevice handles the common behaviour between AS5600 & AS5601 devices
type BaseDevice struct {
bus drivers.I2C
address uint8
registers map[uint8]*i2cRegister
maxAngle uint16
}
// newBaseDevice creates a new base device given an I2C bus.
func newBaseDevice(bus drivers.I2C) BaseDevice {
// Add all 'base' registers, common to both AS5600 & AS5601
conf := newI2CRegister(CONF, 0, 0x3fff, 2, reg_read|reg_write|reg_program)
status := newI2CRegister(STATUS, 0, 0xff, 1, reg_read)
regs := map[uint8]*i2cRegister{
ZPOS: newI2CRegister(ZPOS, 0, 0xfff, 2, reg_read|reg_write|reg_program),
CONF: conf,
RAW_ANGLE: newI2CRegister(RAW_ANGLE, 0, 0xfff, 2, reg_read),
ANGLE: newI2CRegister(ANGLE, 0, 0xfff, 2, reg_read),
STATUS: status,
AGC: newI2CRegister(AGC, 0, 0xff, 1, reg_read),
MAGNITUDE: newI2CRegister(MAGNITUDE, 0, 0xfff, 2, reg_read),
BURN: newI2CRegister(BURN, 0, 0xff, 1, reg_write),
// Add common 'virtual registers' These are bitfields within the common registers above
// A virtual register provides a convenient way to access the fields of a registers
// by handling all of the necessary bitfield shifting and masking operations
WD: newVirtualRegister(conf, 13, 0b1),
FTH: newVirtualRegister(conf, 10, 0b111),
SF: newVirtualRegister(conf, 8, 0b11),
HYST: newVirtualRegister(conf, 2, 0b11),
PM: newVirtualRegister(conf, 0, 0b11),
MD: newVirtualRegister(status, 5, 0b1),
ML: newVirtualRegister(status, 4, 0b1),
MH: newVirtualRegister(status, 3, 0b1),
}
return BaseDevice{bus, DefaultAddress, regs, NATIVE_ANGLE_RANGE}
}
// Configure sets up the AMS AS560x sensor device with the given configuration.
func (d *BaseDevice) Configure(cfg Config) {
if cfg.Address == 0 {
cfg.Address = DefaultAddress
}
d.address = cfg.Address
}
// ReadRegister reads the value for the given register from the AS560x device via I2C
func (d *BaseDevice) ReadRegister(address uint8) (uint16, error) {
reg, ok := d.registers[address]
if !ok {
return 0, errRegisterNotFound
}
return reg.read(d.bus, d.address)
}
// WriteRegister writes the given value for the given register to the AS560x device via I2C
func (d *BaseDevice) WriteRegister(address uint8, value uint16) error {
reg, ok := d.registers[address]
if !ok {
return errRegisterNotFound
}
return reg.write(d.bus, d.address, value)
}
// GetZeroPosition returns the 'zero position' (ZPOS) in various units
func (d *BaseDevice) GetZeroPosition(units AngleUnit) (uint16, float32, error) {
zpos, err := d.ReadRegister(ZPOS)
if nil != err {
return 0, 0.0, err
}
// Convert to requested units
i, f := convertFromNativeAngle(zpos, NATIVE_ANGLE_RANGE, units)
return i, f, nil
}
// SetZeroPosition sets the 'zero position' (ZPOS) in various units
func (d *BaseDevice) SetZeroPosition(zpos float32, units AngleUnit) error {
return d.WriteRegister(ZPOS, convertToNativeAngle(zpos, units))
}
// RawAngle reads the (unscaled & unadjusted) RAW_ANGLE register in various units
func (d *BaseDevice) RawAngle(units AngleUnit) (uint16, float32, error) {
angle, err := d.ReadRegister(RAW_ANGLE)
if nil != err {
return 0, 0.0, err
}
// Convert to requested units
i, f := convertFromNativeAngle(angle, NATIVE_ANGLE_RANGE, units)
return i, f, nil
}
// Angle reads the (scaled & adjusted) ANGLE register in various units
func (d *BaseDevice) Angle(units AngleUnit) (uint16, float32, error) {
// ZPOS enables setting the 'zero position' of the device to any RAW_ANGLE value
// ANGLE is RAW_ANGLE adjusted relative to ZPOS.
angle, err := d.ReadRegister(ANGLE)
if nil != err {
return 0, 0.0, err
}
// Convert to requested units
i, f := convertFromNativeAngle(angle, d.maxAngle, units)
return i, f, nil
}
// MagnetStatus reads the STATUS register and reports magnet position characteristics
func (d *BaseDevice) MagnetStatus() (detected bool, strength MagnetStrength, err error) {
status, err := d.ReadRegister(STATUS)
if nil != err {
return false, MagnetOk, err
}
detected = (status & STATUS_MD) != 0
strength = MagnetOk
if (status & STATUS_ML) != 0 {
strength = MagnetTooWeak
} else if (status & STATUS_MH) != 0 {
strength = MagnetTooStrong
}
return
}
// Burn is a convenience method to program the device permanently by writing to the BURN register (limited number of times use!)
func (d *BaseDevice) Burn(burnCmd BURN_CMD) error {
if BURN_ANGLE == burnCmd {
// BURN_ANGLE can only be executed up to 3 times.
// We can check this in advance by reading ZMCO before writing to the BURN register.
numBurns, err := d.ReadRegister(ZMCO)
if nil != err {
return err
}
if numBurns >= BURN_ANGLE_COUNT_MAX {
// We're outta BURNs :(
return errMaxBurnAngle
}
}
return d.WriteRegister(BURN, uint16(burnCmd))
}
+95
View File
@@ -0,0 +1,95 @@
package as560x // import tinygo.org/x/drivers/ams560x
import "math"
// convertFromNativeAngle converts and scales an angle from the device's native 12-bit range to the requested units
func convertFromNativeAngle(angle uint16, maxAngle uint16, units AngleUnit) (uint16, float32) {
// MANG == 0 & MANG == NATIVE_ANGLE_RANGE (1 << 12) mean the same thing: use full circle range
// but the latter makes the maths/code simpler
if 0 == maxAngle {
maxAngle = NATIVE_ANGLE_RANGE
}
switch units {
case ANGLE_NATIVE:
// For native angles, scaling has already been done by the device
return angle, float32(angle)
case ANGLE_DEGREES_INT:
// Convert to degrees using integer arithmetic. Less accuracy but faster
var deg int = 0
if NATIVE_ANGLE_RANGE == maxAngle {
// Simplify the conversion when using the full range
deg = int(angle) * 360 >> 12
} else {
// Using an integer degrees scale with a narrower native range is pointless since we don't
// benefit at all from the increase in native resolution, in fact we LOSE precision.
// Alas, we have to return something
// First get maxAngle on the degrees scale
degMang, _ := convertFromNativeAngle(maxAngle, NATIVE_ANGLE_RANGE, units)
// Now scale angle
deg = int(angle) * int(degMang) / NATIVE_ANGLE_RANGE
}
return uint16(deg), float32(deg)
case ANGLE_DEGREES_FLOAT:
// Convert to degrees using floating point. More accuracy at expense of speed
var degF float32 = 0.0
if NATIVE_ANGLE_RANGE == maxAngle {
// Simplify the conversion when using the full range
degF = float32(angle) * 360.0 / NATIVE_ANGLE_RANGE
} else {
// Scale to degrees using a narrower native range
// First get maxAngle on the degrees scale
_, degMangF := convertFromNativeAngle(maxAngle, NATIVE_ANGLE_RANGE, units)
// Now scale angle
degF = float32(angle) * degMangF / NATIVE_ANGLE_RANGE
}
return uint16(degF), degF
case ANGLE_RADIANS:
// Convert to radians. Can only be done using floating point.
var rad float32 = 0.0
if NATIVE_ANGLE_RANGE == maxAngle {
// Simplify the conversion when using the full range
rad = float32(angle) * 2 * math.Pi / NATIVE_ANGLE_RANGE
} else {
// Scale to radians using a narrower native range
// First get maxAngle on the radians scale
_, radMang := convertFromNativeAngle(maxAngle, NATIVE_ANGLE_RANGE, units)
// Now scale angle
rad = float32(angle) * radMang / NATIVE_ANGLE_RANGE
}
return uint16(rad), rad
default:
panic("Unknown angle measurement unit")
}
}
// convertToNativeAngle converts an angle from the requested units to the device's native 12-bit range.
func convertToNativeAngle(angle float32, units AngleUnit) uint16 {
var pos uint16 = 0
switch units {
case ANGLE_NATIVE:
pos = uint16(angle)
case ANGLE_DEGREES_INT:
fallthrough
case ANGLE_DEGREES_FLOAT:
// Convert from degrees
angle = float32(math.Mod(float64(angle), 360.0))
if angle < 0.0 {
angle += 360.0
}
pos = uint16(math.Round(float64(angle) * NATIVE_ANGLE_RANGE / 360.0))
case ANGLE_RADIANS:
// Convert from radians
const circRad = 2.0 * math.Pi
angle = float32(math.Mod(float64(angle), circRad))
if angle < 0.0 {
angle += circRad
}
pos = uint16(math.Round(float64(angle) * NATIVE_ANGLE_RANGE / circRad))
default:
panic("Unknown angle measurement unit")
}
if pos > NATIVE_ANGLE_MAX {
pos = NATIVE_ANGLE_MAX
}
return pos
}
+170
View File
@@ -0,0 +1,170 @@
package as560x // import tinygo.org/x/drivers/ams560x
import (
"encoding/binary"
"errors"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
)
// registerAttributes is a bitfield of attributes for a register
type registerAttributes uint8
const (
// reg_read indicates that the register is readable
reg_read registerAttributes = 1 << iota
// reg_write indicates that the register is writeable
reg_write
// reg_program indicates that the register can be permanently programmed ('BURNed')
reg_program
)
var (
errRegisterNotReadable = errors.New("Register is not readable")
errRegisterNotWriteable = errors.New("Register is not writeable")
)
// i2cRegister encapsulates the address, structure and read/write logic for a register on a AS560x device
type i2cRegister struct {
// host is the 'host register' for virtual registers. Physical/root registers have this set to self
host *i2cRegister
// address is the i2c address of the register. For 2-byte (word) addresses it's the low byte which holds the MSBs
address uint8
// shift is the number of bits the value is 'left shifted' into the register byte/word (0-15)
shift uint16
// mask is a bitwise mask applied to the register AFTER 'right shifting' to mask the register value
mask uint16
// num_bytes is the width of the register in bytes, 1 or 2.
num_bytes uint8
// attributes holds the register attributes. A bitfield of REG_xyz constants
attributes registerAttributes
// cached indicates whether we are holding a cached value of the register in value
cached bool
// value can be used as a 'cache' of the register's value for writeable registers.
value uint16
}
// newI2CRegister returns a pointer to a new i2cRegister with no cached value
func newI2CRegister(address uint8, shift uint16, mask uint16, num_bytes uint8, attributes registerAttributes) *i2cRegister {
reg := &i2cRegister{
address: address,
shift: shift,
mask: mask,
num_bytes: num_bytes,
attributes: attributes,
}
// root registers host themselves
reg.host = reg
return reg
}
// newVirtualRegister returns a pointer to a new i2cRegister with the given host register and shift/mask.
func newVirtualRegister(host *i2cRegister, shift uint16, mask uint16) *i2cRegister {
return &i2cRegister{
host: host,
address: host.address,
shift: shift,
mask: mask,
num_bytes: host.num_bytes,
attributes: host.attributes,
}
}
// invalidate invalidates any cached value for the register and forces an I2C read on the next read()
func (r *i2cRegister) invalidate() {
r.host.cached = false
r.host.value = 0
}
// readShiftAndMask is an internal method to read a value for the register over the given I2C bus from the device with the given address applying the given shift and mask
func (r *i2cRegister) readShiftAndMask(bus drivers.I2C, deviceAddress uint8, shift uint16, mask uint16) (uint16, error) {
if r.host.attributes&reg_read == 0 {
return 0, errRegisterNotReadable
}
// Only read over I2C if we don't have the host register value cached
var val uint16 = r.host.value
if !r.host.cached {
// To avoid an alloc we always use an array of 2 bytes
var buffer [2]byte
var buf []byte
if r.host.num_bytes < 2 {
buf = buffer[:1]
} else {
buf = buffer[:]
}
// Read the host register over I2C
err := legacy.ReadRegister(bus, deviceAddress, r.host.address, buf)
if nil != err {
return 0, err
}
// Unpack data from I2C
if r.host.num_bytes > 1 {
val = binary.BigEndian.Uint16(buf)
} else {
val = uint16(buf[0])
}
// cache this value if the host register is writeable. Note we cache the entire buffer without applying shift/mask
if r.host.attributes&reg_write != 0 {
r.host.value = val
r.host.cached = true
}
}
// Shift and mask the value before returning
val >>= shift
val &= mask
return val, nil
}
// read reads a value for the register over the given I2C bus from the device with the given address.
func (r *i2cRegister) read(bus drivers.I2C, deviceAddress uint8) (uint16, error) {
return r.readShiftAndMask(bus, deviceAddress, r.shift, r.mask)
}
// write writes a value for the register over the given I2C bus to the device with the given address.
func (r *i2cRegister) write(bus drivers.I2C, deviceAddress uint8, value uint16) error {
if r.host.attributes&reg_write == 0 {
return errRegisterNotWriteable
}
var newValue uint16 = 0
// Data sheet tells us to do a read first, modify only the desired bits and then write back
// since (quote:) 'Blank fields may contain factory settings'
// We will also need to do this anyway to support virtualRegister mappings on some registers
// (e.g. CONF/STATUS)
if (r.host.attributes & reg_read) > 0 { // not all registers are readable, e.g. BURN
// read the host register's entire host byte/word, regardless of shift & mask
readValue, error := r.readShiftAndMask(bus, deviceAddress, 0, 0xffff)
if error != nil {
return error
}
// Zero-out ONLY the relevant bits in newValue we just read
readValue &= (0xffff ^ (r.mask << r.shift))
newValue = readValue
}
// Mask the new value and shift it into place
value &= r.mask
value <<= r.shift
// OR the masked & shifted value back into newValue to be written
newValue |= value
// Pack newValue into a byte buffer to write. To avoid an alloc we always use an array of 2 bytes
var buffer [2]byte
var buf []byte
if r.host.num_bytes < 2 {
buf = buffer[:1]
buf[0] = uint8(newValue & 0xff)
} else {
buf = buffer[:]
binary.BigEndian.PutUint16(buf, newValue)
}
// Write the register from the buffer over I2C
err := legacy.WriteRegister(bus, deviceAddress, r.host.address, buf)
// after successful I2C write, cache this value if the host register (if also readable)
// Note we cache the entire buffer without applying shift/mask
if nil == err && r.host.attributes&reg_read != 0 {
r.host.value = newValue
r.host.cached = true
}
return err
}
+208
View File
@@ -0,0 +1,208 @@
package as560x // import tinygo.org/x/drivers/ams560x
// DefaultAddress is the default I2C address of the AMS AS560x sensors (0x36).
const DefaultAddress uint8 = 0x36
// AS560x common device registers
const (
// ZMCO contains the number of times a BURN_ANGLE command has been executed (max 3 burns)
ZMCO = 0x00
// ZPOS is the zero (start) position in RAW_ANGLE terms.
ZPOS = 0x01
// CONF supports custom config. Raw 14-bit register. See datasheet for mapping or use 'virtual registers' below.
CONF = 0x07
// STATUS indicates magnet position. Encapsulates MD, ML & MH. See also 'virtual registers' below.
STATUS = 0x0b
// RAW_ANGLE is the raw unscaled & unadjusted angle (12 bit: 0-4095/0xfff)
RAW_ANGLE = 0x0c
// ANGLE is RAW_ANGLE scaled & adjusted according to ZPOS (and MPOS/MANG on AS5600). (12 bit: 0-4095/0xfff)
ANGLE = 0x0e
// AGC is the Automatic Gain Control based on temp, airgap etc. 0-255 @ 5V, 0-128 @ 3.3V.
AGC = 0x1a
// MAGNITUDE indicates the magnitude value of the internal CORDIC output. See datasheet for more info.
MAGNITUDE = 0x1b
// BURN performs permanent programming of some registers. See BURN_XYZ cmd constants below for commands.
BURN = 0xff
)
// AS5600 specific registers
const (
// MPOS is the maximum position in RAW_ANGLE terms. With ZPOS, defines a 'narrower angle' for higher resolution.
MPOS = 0x03
// MANG is the maximum angle. With ZPOS, defines a 'narrower angle' for higher resolution.
MANG = 0x05
)
// AS5601 specific registers
const (
// ABN. See datasheet for mapping
ABN = 0x09
// PUSHTHR. Configures push-button function. See datasheet and AGC
PUSHTHR = 0x0a
)
// 'Virtual Registers' (VRs) are bitfields within the registers above.
// These are not real register addresses recognized by the chip,
// but they are recognized by the driver for convenience.
// virtualRegisterStartAddress defines the start of the virtual register address range.
const virtualRegisterStartAddress = 0xa0
const (
// VRs for CONF
// WD is a Virtual Register for the Watchdog timer. See WATCHDOG_TIMER consts.
WD = iota + virtualRegisterStartAddress
// FTH is a Virtual Register for the Fast Filter Threshold. See FAST_FILTER_THRESHOLD consts.
FTH
// SF is a Virtual Register for the Slow Filter. See SLOW_FILTER_RESPONSE consts.
SF
// PWMF is a Virtual Register for PWM Frequency (AS5600 ONLY). See PWM_FREQUENCY consts.
PWMF
// OUTS is a Virtual Register for the Output Stage (AS5600 ONLY). See OUTPUT_STAGE consts.
OUTS
// HYST is a Virtual Register for Hysteresis. See HYSTERESIS consts.
HYST
// PM is a Virtual Register for the Power Mode. See POWER_MODE consts.
PM
// VRs for STATUS (0 = unset, 1 = set)
// MD is a Virtual Register for the 'Magnet was detected' flag.
MD
// ML is a Virtual Register for the 'AGC maximum gain overflow' a.k.a 'magnet too weak' flag.
ML
// MH is a Virtual Register for the 'AGC minimum gain overflow' a.k.a 'magnet too strong' flag.
MH
)
// POWER_MODE values for the PM component of CONF (and the PM VR)
const (
// PM_NOM is the normal 'always on' power mode. No polling, max 6.5mA current
PM_NOM = iota
// PM_LPM1 is Low Power Mode 1. 5ms polling, max 3.4mA current
PM_LPM1
// PM_LPM2 is Low Power Mode 2. 20ms polling, max 1.8mA current
PM_LPM2
// PM_LPM3 is Low Power Mode 3. 100ms polling, max 1.5mA current
PM_LPM3
)
// HYSTERESIS values for the HYST component of CONF (and the HYST VR)
const (
// HYST_OFF disables any hysteresis of the output
HYST_OFF = iota
// HYST_1LSB enables output hysteresis using 1 LSB
HYST_1LSB
// HYST_2LSB enables output hysteresis using 2 LSBs
HYST_2LSB
// HYST_3LSB enables output hysteresis using 3 LSBs
HYST_3LSB
)
// OUTPUT_STAGE values for the OUTS component of CONF (and the OUTS VR - AS5600 ONLY)
const (
// OS_ANALOG_FULL_RANGE enables analog output with full range (0%-100% VDD)
OS_ANALOG_FULL_RANGE = iota
// OS_ANALOG_REDUCED_RANGE enables analog output with reduced range (10%-90% VDD)
OS_ANALOG_REDUCED_RANGE
// OS_DIGITAL_PWM enables digital PWM output. Frequency determined by PWMF
OS_DIGITAL_PWM
)
// PWM_FREQUENCY values for the PWMF component of CONF (and the PWMF VR - ASS5600 ONLY)
const (
// PWMF_115_HZ enables PWM at 115 Hz
PWMF_115_HZ = iota
// PWMF_230_HZ enables PWM at 230 Hz
PWMF_230_HZ
// PWMF_460_HZ enables PWM at 460 Hz
PWMF_460_HZ
// PWMF_920_HZ enables PWM at 920 Hz
PWMF_920_HZ
)
// SLOW_FILTER_RESPONSE values for the SF (slow filter) component of CONF (and the SF VR)
const (
// SF_16X enables a 16x Slow Filter step response
SF_16X = iota
// SF_8X enables a 8x Slow Filter step response
SF_8X
// SF_4X enables a 4x Slow Filter step response
SF_4X
// SF_2X enables a 2x Slow Filter step response
SF_2X
)
// FAST_FILTER_THRESHOLD values for the FTH (fast filter threshold) component of CONF (and the FTH VR)
const (
// FTH_NONE disables the fast filter (slow filter only)
FTH_NONE = iota
// FTH_6LSB enables a fast filter threshold with 6 LSBs
FTH_6LSB
// FTH_7LSB enables a fast filter threshold with 7 LSBs
FTH_7LSB
// FTH_9LSB enables a fast filter threshold with 9 LSBs
FTH_9LSB
// FTH_18LSB enables a fast filter threshold with 18 LSBs
FTH_18LSB
// FTH_21LSB enables a fast filter threshold with 21 LSBs
FTH_21LSB
// FTH_24LSB enables a fast filter threshold with 24 LSBs
FTH_24SB
// FTH_10LSB enables a fast filter threshold with 10 LSBs
FTH_10LSB
)
// WATCHDOG_TIMER values for the WD component of CONF (and the WD VR)
const (
// WD_OFF disables the Watchdog Timer
WD_OFF = iota
// WD_ON enables the Watchdog Timer (automatic entry into LPM3 low-power mode enabled)
WD_ON
)
// constants for the raw STATUS register bitfield value.
const (
// STATUS_MH is set in STATUS when the magnet field is too strong (AGC minimum gain overflow)
STATUS_MH = 1 << (iota + 3)
// STATUS_ML is set in STATUS when the magnet field is too weak (AGC maximum gain overflow)
STATUS_ML
// STATUS_MD is set n STATUS when the magnet is detected. Doesn't seem to work with some units.
STATUS_MD
)
// ABN_MAPPING values for the ABN register (AS5601 ONLY)
const (
// ABN_8 configures 8 output positions (61 Hz)
ABN_8 = iota
// ABN_16 configures 16 output positions (122 Hz)
ABN_16
// ABN_32 configures 32 output positions (244 Hz)
ABN_32
// ABN_64 configures 64 output positions (488 Hz)
ABN_64
// ABN_128 configures 128 output positions (976 Hz)
ABN_128
// ABN_256 configures 256 output positions (1.95 KHz)
ABN_256
// ABN_512 configures 512 output positions (3.9 KHz)
ABN_512
// ABN_1024 configures 1024 output positions (7.8 KHz)
ABN_1024
// ABN_2048 configures 2048 output positions (15.6 KHz)
ABN_2048
)
// BURN_CMD is a command to write to the BURN register.
type BURN_CMD uint16
const (
// BURN_ANGLE is the value to write to BURN to permanently program ZPOS & MPOS (Max 3 times!)
BURN_ANGLE BURN_CMD = 0x80
// BURN_SETTING is the value to write to BURN to permanently program MANG & CONF (ONCE ONLY!)
BURN_SETTING BURN_CMD = 0x40
)
// BURN_ANGLE_COUNT_MAX is a constant for the maximum number of times a BURN_ANGLE command can be executed. Compare this with ZMCO
const BURN_ANGLE_COUNT_MAX uint16 = 3
+1 -1
View File
@@ -11,7 +11,7 @@ import (
"tinygo.org/x/drivers"
)
// Device wraps an I2C connection to a DS3231 device.
// Device wraps an I2C connection to an AT24CX device.
type Device struct {
bus drivers.I2C
Address uint16
+3 -2
View File
@@ -7,6 +7,7 @@ package axp192 // import "tinygo.org/x/drivers/axp192"
import (
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
)
type Error uint8
@@ -248,10 +249,10 @@ func (d *Device) SetLDOEnable(number uint8, state bool) {
}
func (d *Device) write1Byte(reg, data uint8) {
d.bus.WriteRegister(d.Address, reg, []byte{data})
legacy.WriteRegister(d.bus, d.Address, reg, []byte{data})
}
func (d *Device) read8bit(reg uint8) uint8 {
d.bus.ReadRegister(d.Address, reg, d.buf[:1])
legacy.ReadRegister(d.bus, d.Address, reg, d.buf[:1])
return d.buf[0]
}
Binary file not shown.
Binary file not shown.
+352
View File
@@ -0,0 +1,352 @@
// Package bma42x provides a driver for the BMA421 and BMA425 accelerometer
// chips.
//
// Here is a reasonably good datasheet:
// https://datasheet.lcsc.com/lcsc/1912111437_Bosch-Sensortec-BMA425_C437656.pdf
//
// This driver was originally written for the PineTime, using the datasheet as a
// guide. There is an open source C driver provided by Bosch, but unfortunately
// it needs some small modifications to work with other chips (most importantly,
// the "config file").
// The InfiniTime and Wasp-OS drivers for this accelerometer have also been used
// to figure out some driver details (especially step counting).
package bma42x
import (
_ "embed"
"errors"
"reflect"
"time"
"unsafe"
"tinygo.org/x/drivers"
)
// Driver for BMA421 and BMA425:
// BMA421: https://files.pine64.org/doc/datasheet/pinetime/BST-BMA421-FL000.pdf
// BMA425: https://datasheet.lcsc.com/lcsc/1912111437_Bosch-Sensortec-BMA425_C437656.pdf
// This is the BMA421 firmware from the Wasp-OS project.
// It is identical to the so-called BMA423 firmware in InfiniTime, which I
// suspect to be actually a BMA421 firmware. I don't know where this firmware
// comes from or what the licensing status is.
// It has the FEATURES_IN command prepended, so that it can be written directly
// using I2C.Tx.
// Source: https://github.com/wasp-os/bma42x-upy/blob/master/BMA42X-Sensor-API/bma421.h
//
//go:embed bma421-config-waspos.bin
var bma421Firmware string
// Same as the BMA421 firmware, but for the BMA425.
// Source: https://github.com/wasp-os/bma42x-upy/blob/master/BMA42X-Sensor-API/bma425.h
//
//go:embed bma425-config-waspos.bin
var bma425Firmware string
var (
errUnknownDevice = errors.New("bma42x: unknown device")
errUnsupportedDevice = errors.New("bma42x: device not part of config")
errConfigMismatch = errors.New("bma42x: config mismatch")
errTimeout = errors.New("bma42x: timeout")
errInitFailed = errors.New("bma42x: failed to initialize")
)
const Address = 0x18 // BMA421/BMA425 address
type DeviceType uint8
const (
DeviceBMA421 DeviceType = 1 << iota
DeviceBMA425
AnyDevice = DeviceBMA421 | DeviceBMA425
noDevice DeviceType = 0
)
// Features to enable while configuring the accelerometer.
type Features uint8
const (
FeatureStepCounting = 1 << iota
)
type Config struct {
// Which devices to support (OR the device types together as needed).
Device DeviceType
// Which features to enable. With Features == 0, only the accelerometer will
// be enabled.
Features Features
}
type Device struct {
bus drivers.I2C
address uint8
accelData [6]byte
combinedTempSteps [5]uint8 // [0:3] steps, [4] temperature
dataBuf [2]byte
}
func NewI2C(i2c drivers.I2C, address uint8) *Device {
return &Device{
bus: i2c,
address: address,
}
}
func (d *Device) Connected() bool {
val, err := d.read1(_CHIP_ID)
return err == nil && identifyChip(val) != noDevice
}
func (d *Device) Configure(config Config) error {
if config.Device == 0 {
config.Device = AnyDevice
}
// Check chip ID, to check the connection and to determine which BMA42x
// device we're dealing with.
chipID, err := d.read1(_CHIP_ID)
if err != nil {
return err
}
// Determine which firmware (config file?) we'll be using.
// There is an extra check for the device before using the given firmware.
// This check will typically be optimized away if the given device is not
// configured, so that the firmware (which is 6kB in size!) won't be linked
// into the binary.
var firmware string
switch identifyChip(chipID) {
case DeviceBMA421:
if config.Device&DeviceBMA421 == 0 {
return errUnsupportedDevice
}
firmware = bma421Firmware
case DeviceBMA425:
if config.Device&DeviceBMA425 == 0 {
return errUnsupportedDevice
}
firmware = bma425Firmware
default:
return errUnknownDevice
}
// Reset the chip, to be able to initialize it properly.
// The datasheet says a delay is needed after a SoftReset, but it doesn't
// say how long this delay should be. The bma423 driver however uses a 200ms
// delay, so that's what we'll be using.
err = d.write1(_CMD, cmdSoftReset)
if err != nil {
return err
}
time.Sleep(200 * time.Millisecond)
// Disable power saving.
err = d.write1(_PWR_CONF, 0x00)
if err != nil {
return err
}
time.Sleep(450 * time.Microsecond)
// Start initialization (because the datasheet says so).
err = d.write1(_INIT_CTRL, 0x00)
if err != nil {
return err
}
// Write "config file" (actually a firmware, I think) to the chip.
// To do this, unsafely cast the string to a byte slice to avoid putting it
// in RAM. This is safe in this case because Tx won't write to the 'w'
// slice.
err = d.bus.Tx(uint16(d.address), unsafeStringToSlice(firmware), nil)
if err != nil {
return err
}
// Read the config data back.
// We don't do that, as it slows down configuration and it probably isn't
// _really_ necessary with a reasonably stable I2C bus.
if false {
data := make([]byte, len(firmware)-1)
err = d.readn(_FEATURES_IN, data)
if err != nil {
return err
}
for i, c := range data {
if firmware[i+1] != c {
return errConfigMismatch
}
}
}
// Enable sensors.
err = d.write1(_INIT_CTRL, 0x01)
if err != nil {
return err
}
// Wait until the device is initialized.
start := time.Now()
status := uint8(0) // busy
for status == 0 {
status, err = d.read1(_INTERNAL_STATUS)
if err != nil {
return err // I2C bus error.
}
if status > 1 {
// Expected either 0 ("not_init") or 1 ("init_ok").
return errInitFailed
}
if time.Since(start) >= 150*time.Millisecond {
// The datasheet says initialization should not take longer than
return errTimeout
}
// Don't bother the chip all the time while it's initializing.
time.Sleep(50 * time.Microsecond)
}
if config.Features&FeatureStepCounting != 0 {
// Enable step counter.
// TODO: support step counter parameters.
var buf [71]byte
buf[0] = _FEATURES_IN // prefix buf with the command
data := buf[1:]
err = d.readn(_FEATURES_IN, data)
if err != nil {
return err
}
data[0x3A+1] |= 0x10 // enable step counting by setting a magical bit
err = d.bus.Tx(uint16(d.address), buf[:], nil)
if err != nil {
return err
}
}
// Enable the accelerometer.
err = d.write1(_PWR_CTRL, 0x04)
if err != nil {
return err
}
// Configure accelerometer for low power usage:
// acc_perf_mode=0 (power saving enabled)
// acc_bwp=osr4_avg1 (no averaging)
// acc_odr=50Hz (50Hz sampling interval, enough for the step counter)
const accelConf = 0x00<<7 | 0x00<<4 | 0x07<<0
err = d.write1(_ACC_CONF, accelConf)
if err != nil {
return err
}
// Reduce current consumption.
// With power saving enabled (and the above ACC_CONF) the chip consumes only
// 14µA.
err = d.write1(_PWR_CONF, 0x03)
if err != nil {
return err
}
return nil
}
func (d *Device) Update(which drivers.Measurement) error {
// TODO: combine temperature and step counter into a single read.
if which&drivers.Temperature != 0 {
val, err := d.read1(_TEMPERATURE)
if err != nil {
return err
}
d.combinedTempSteps[4] = val
}
if which&drivers.Acceleration != 0 {
// The acceleration data is stored in DATA8 through DATA13 as 3 12-bit
// values.
err := d.readn(_DATA_8, d.accelData[:]) // ACC_X(LSB)
if err != nil {
return err
}
err = d.readn(_STEP_COUNTER_0, d.combinedTempSteps[:4])
if err != nil {
return err
}
}
return nil
}
// Temperature returns the last read temperature in celsius milli degrees (1°C
// is 1000).
func (d *Device) Temperature() int32 {
// The temperature value is a two's complement number (meaning: signed) in
// units of 1 kelvin, with 0 being 23°C.
return (int32(int8(d.combinedTempSteps[4])) + 23) * 1000
}
// Acceleration returns the last read acceleration in µg (micro-gravity).
// When one of the axes is pointing straight to Earth and the sensor is not
// moving the returned value will be around 1000000 or -1000000.
func (d *Device) Acceleration() (x, y, z int32) {
// Combine raw data from d.accelData (stored as 12-bit signed values) into a
// number (0..4095):
x = int32(d.accelData[0])>>4 | int32(d.accelData[1])<<4
y = int32(d.accelData[2])>>4 | int32(d.accelData[3])<<4
z = int32(d.accelData[4])>>4 | int32(d.accelData[5])<<4
// Sign extend this number to -2048..2047:
x = (x << 20) >> 20
y = (y << 20) >> 20
z = (z << 20) >> 20
// Scale from -512..511 to -1000_000..998_046.
// Or, at the maximum range (4g), from -2048..2047 to -2000_000..3998_046.
// The formula derived as follows (where 512 is the expected value at 1g):
// x = x * 1000_000 / 512
// x = x * (1000_000/64) / (512/64)
// x = x * 15625 / 8
x = x * 15625 / 8
y = y * 15625 / 8
z = z * 15625 / 8
return
}
// Steps returns the number of steps counted since the BMA42x sensor was
// initialized.
func (d *Device) Steps() (steps uint32) {
steps |= uint32(d.combinedTempSteps[0]) << 0
steps |= uint32(d.combinedTempSteps[1]) << 8
steps |= uint32(d.combinedTempSteps[2]) << 16
steps |= uint32(d.combinedTempSteps[3]) << 24
return
}
func (d *Device) read1(register uint8) (uint8, error) {
d.dataBuf[0] = register
err := d.bus.Tx(uint16(d.address), d.dataBuf[:1], d.dataBuf[1:2])
return d.dataBuf[1], err
}
func (d *Device) readn(register uint8, data []byte) error {
d.dataBuf[0] = register
return d.bus.Tx(uint16(d.address), d.dataBuf[:1], data)
}
func (d *Device) write1(register uint8, data uint8) error {
d.dataBuf[0] = register
d.dataBuf[1] = data
return d.bus.Tx(uint16(d.address), d.dataBuf[:2], nil)
}
func unsafeStringToSlice(s string) []byte {
// TODO: use unsafe.Slice(unsafe.StringData(...)) once we require Go 1.20.
sh := (*reflect.StringHeader)(unsafe.Pointer(&s))
return unsafe.Slice((*byte)(unsafe.Pointer(sh.Data)), len(s))
}
func identifyChip(chipID uint8) DeviceType {
switch chipID {
case 0x11:
return DeviceBMA421
case 0x13:
return DeviceBMA425
default:
return noDevice
}
}
+73
View File
@@ -0,0 +1,73 @@
package bma42x
const (
// I2C registers
_CHIP_ID = 0x00
_ERR_REG = 0x02
_STATUS = 0x03
_DATA_0 = 0x0A
_DATA_1 = 0x0B
_DATA_2 = 0x0C
_DATA_3 = 0x0D
_DATA_4 = 0x0E
_DATA_5 = 0x0F
_DATA_6 = 0x10
_DATA_7 = 0x11
_DATA_8 = 0x12
_DATA_9 = 0x13
_DATA_10 = 0x14
_DATA_11 = 0x15
_DATA_12 = 0x16
_DATA_13 = 0x17
_SENSORTIME_0 = 0x18
_SENSORTIME_1 = 0x19
_SENSORTIME_2 = 0x1A
_EVENT = 0x1B
_INT_STATUS_0 = 0x1C
_INT_STATUS_1 = 0x1D
_STEP_COUNTER_0 = 0x1E
_STEP_COUNTER_1 = 0x1F
_STEP_COUNTER_2 = 0x20
_STEP_COUNTER_3 = 0x21
_TEMPERATURE = 0x22
_FIFO_LENGTH_0 = 0x24
_FIFO_LENGTH_1 = 0x25
_FIFO_DATA = 0x26
_ACTIVITY_TYPE = 0x27
_INTERNAL_STATUS = 0x2A
_ACC_CONF = 0x40
_ACC_RANGE = 0x41
_AUX_CONF = 0x44
_FIFO_DOWNS = 0x45
_FIFO_WTM_0 = 0x46
_FIFO_WTM_1 = 0x47
_FIFO_CONFIG_0 = 0x48
_FIFO_CONFIG_1 = 0x49
_AUX_DEV_ID = 0x4B
_AUX_IF_CONF = 0x4C
_AUX_RD_ADDR = 0x4D
_AUX_WR_ADDR = 0x4E
_AUX_WR_DATA = 0x4F
_INT1_IO_CTRL = 0x53
_INT2_IO_CTRL = 0x54
_INT_LATCH = 0x55
_INT1_MAP = 0x56
_INT2_MAP = 0x57
_INT_MAP_DATA = 0x58
_INIT_CTRL = 0x59
_FEATURES_IN = 0x5E
_INTERNAL_ERROR = 0x5F
_NVM_CONF = 0x6A
_IF_CONF = 0x6B
_ACC_SELF_TEST = 0x6D
_NV_CONF = 0x70
_OFFSET_0 = 0x71
_OFFSET_1 = 0x72
_OFFSET_2 = 0x73
_PWR_CONF = 0x7C
_PWR_CTRL = 0x7D
_CMD = 0x7E
// Commands send to regCommand.
cmdSoftReset = 0xB6
)
+12 -11
View File
@@ -10,6 +10,7 @@ import (
"time"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
)
// calibrationCoefficients reads at startup and stores the calibration coefficients
@@ -98,19 +99,19 @@ func (d *Device) ConfigureWithSettings(config Config) {
}
var data [24]byte
err := d.bus.ReadRegister(uint8(d.Address), REG_CALIBRATION, data[:])
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_CALIBRATION, data[:])
if err != nil {
return
}
var h1 [1]byte
err = d.bus.ReadRegister(uint8(d.Address), REG_CALIBRATION_H1, h1[:])
err = legacy.ReadRegister(d.bus, uint8(d.Address), REG_CALIBRATION_H1, h1[:])
if err != nil {
return
}
var h2lsb [7]byte
err = d.bus.ReadRegister(uint8(d.Address), REG_CALIBRATION_H2LSB, h2lsb[:])
err = legacy.ReadRegister(d.bus, uint8(d.Address), REG_CALIBRATION_H2LSB, h2lsb[:])
if err != nil {
return
}
@@ -137,12 +138,12 @@ func (d *Device) ConfigureWithSettings(config Config) {
d.Reset()
d.bus.WriteRegister(uint8(d.Address), CTRL_CONFIG, []byte{byte(d.Config.Period<<5) | byte(d.Config.IIR<<2)})
d.bus.WriteRegister(uint8(d.Address), CTRL_HUMIDITY_ADDR, []byte{byte(d.Config.Humidity)})
legacy.WriteRegister(d.bus, uint8(d.Address), CTRL_CONFIG, []byte{byte(d.Config.Period<<5) | byte(d.Config.IIR<<2)})
legacy.WriteRegister(d.bus, uint8(d.Address), CTRL_HUMIDITY_ADDR, []byte{byte(d.Config.Humidity)})
// Normal mode, start measuring now
if d.Config.Mode == ModeNormal {
d.bus.WriteRegister(uint8(d.Address), CTRL_MEAS_ADDR, []byte{
legacy.WriteRegister(d.bus, uint8(d.Address), CTRL_MEAS_ADDR, []byte{
byte(d.Config.Temperature<<5) |
byte(d.Config.Pressure<<2) |
byte(d.Config.Mode)})
@@ -153,13 +154,13 @@ func (d *Device) ConfigureWithSettings(config Config) {
// It does a "who am I" request and checks the response.
func (d *Device) Connected() bool {
data := []byte{0}
d.bus.ReadRegister(uint8(d.Address), WHO_AM_I, data)
legacy.ReadRegister(d.bus, uint8(d.Address), WHO_AM_I, data)
return data[0] == CHIP_ID
}
// Reset the device
func (d *Device) Reset() {
d.bus.WriteRegister(uint8(d.Address), CMD_RESET, []byte{0xB6})
legacy.WriteRegister(d.bus, uint8(d.Address), CMD_RESET, []byte{0xB6})
}
// SetMode can set the device to Sleep, Normal or Forced mode
@@ -170,7 +171,7 @@ func (d *Device) Reset() {
func (d *Device) SetMode(mode Mode) {
d.Config.Mode = mode
d.bus.WriteRegister(uint8(d.Address), CTRL_MEAS_ADDR, []byte{
legacy.WriteRegister(d.bus, uint8(d.Address), CTRL_MEAS_ADDR, []byte{
byte(d.Config.Temperature<<5) |
byte(d.Config.Pressure<<2) |
byte(d.Config.Mode)})
@@ -252,7 +253,7 @@ func readIntLE(msb byte, lsb byte) int16 {
func (d *Device) readData() (data [8]byte, err error) {
if d.Config.Mode == ModeForced {
// Write the CTRL_MEAS register to trigger a measurement
d.bus.WriteRegister(uint8(d.Address), CTRL_MEAS_ADDR, []byte{
legacy.WriteRegister(d.bus, uint8(d.Address), CTRL_MEAS_ADDR, []byte{
byte(d.Config.Temperature<<5) |
byte(d.Config.Pressure<<2) |
byte(d.Config.Mode)})
@@ -260,7 +261,7 @@ func (d *Device) readData() (data [8]byte, err error) {
time.Sleep(d.measurementDelay())
}
err = d.bus.ReadRegister(uint8(d.Address), REG_PRESSURE, data[:])
err = legacy.ReadRegister(d.bus, uint8(d.Address), REG_PRESSURE, data[:])
if err != nil {
println(err)
return
+23 -6
View File
@@ -6,9 +6,11 @@
package bmp180 // import "tinygo.org/x/drivers/bmp180"
import (
"math"
"time"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
)
// OversamplingMode is the oversampling ratio of the pressure measurement.
@@ -54,7 +56,7 @@ func New(bus drivers.I2C) Device {
// It does a "who am I" request and checks the response.
func (d *Device) Connected() bool {
data := []byte{0}
d.bus.ReadRegister(uint8(d.Address), WHO_AM_I, data)
legacy.ReadRegister(d.bus, uint8(d.Address), WHO_AM_I, data)
return data[0] == CHIP_ID
}
@@ -62,7 +64,7 @@ func (d *Device) Connected() bool {
// read the calibration coefficients.
func (d *Device) Configure() {
data := make([]byte, 22)
err := d.bus.ReadRegister(uint8(d.Address), AC1_MSB, data)
err := legacy.ReadRegister(d.bus, uint8(d.Address), AC1_MSB, data)
if err != nil {
return
}
@@ -123,12 +125,27 @@ func (d *Device) ReadPressure() (pressure int32, err error) {
return 1000 * (p + ((x1 + x2 + 3791) >> 4)), nil
}
// ReadAltitude returns the current altitude in meters based on the
// current barometric pressure and estimated pressure at sea level.
// Calculation is based on code from Adafruit BME280 library
//
// https://github.com/adafruit/Adafruit_BME280_Library
func (d *Device) ReadAltitude() (int32, error) {
mPa, err := d.ReadPressure()
if err != nil {
return 0, err
}
atmP := float32(mPa) / 100000
return int32(44330.0 * (1.0 - math.Pow(float64(atmP/SEALEVEL_PRESSURE), 0.1903))), nil
}
// rawTemp returns the sensor's raw values of the temperature
func (d *Device) rawTemp() (int32, error) {
d.bus.WriteRegister(uint8(d.Address), REG_CTRL, []byte{CMD_TEMP})
legacy.WriteRegister(d.bus, uint8(d.Address), REG_CTRL, []byte{CMD_TEMP})
time.Sleep(5 * time.Millisecond)
data := make([]byte, 2)
err := d.bus.ReadRegister(uint8(d.Address), REG_TEMP_MSB, data)
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_TEMP_MSB, data)
if err != nil {
return 0, err
}
@@ -144,10 +161,10 @@ func (d *Device) calculateB5(rawTemp int32) int32 {
// rawPressure returns the sensor's raw values of the pressure
func (d *Device) rawPressure(mode OversamplingMode) (int32, error) {
d.bus.WriteRegister(uint8(d.Address), REG_CTRL, []byte{CMD_PRESSURE + byte(mode<<6)})
legacy.WriteRegister(d.bus, uint8(d.Address), REG_CTRL, []byte{CMD_PRESSURE + byte(mode<<6)})
time.Sleep(pauseForReading(mode))
data := make([]byte, 3)
err := d.bus.ReadRegister(uint8(d.Address), REG_PRESSURE_MSB, data)
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_PRESSURE_MSB, data)
if err != nil {
return 0, err
}
+4
View File
@@ -28,3 +28,7 @@ const (
// ULTRAHIGHRESOLUTION is the highest oversampling mode of the pressure measurement.
ULTRAHIGHRESOLUTION
)
const (
SEALEVEL_PRESSURE float32 = 1013.25 // in hPa
)
+9 -8
View File
@@ -4,6 +4,7 @@ import (
"time"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
)
// OversamplingMode is the oversampling ratio of the temperature or pressure measurement.
@@ -64,14 +65,14 @@ func New(bus drivers.I2C) Device {
// It does a "who am I" request and checks the response.
func (d *Device) Connected() bool {
data := make([]byte, 1)
d.bus.ReadRegister(uint8(d.Address), REG_ID, data)
legacy.ReadRegister(d.bus, uint8(d.Address), REG_ID, data)
return data[0] == CHIP_ID
}
// Reset preforms complete power-on-reset procedure.
// It is required to call Configure afterwards.
func (d *Device) Reset() {
d.bus.WriteRegister(uint8(d.Address), REG_RESET, []byte{CMD_RESET})
legacy.WriteRegister(d.bus, uint8(d.Address), REG_RESET, []byte{CMD_RESET})
}
// Configure sets up the device for communication and
@@ -85,15 +86,15 @@ func (d *Device) Configure(standby Standby, filter Filter, temp Oversampling, pr
// Write the configuration (standby, filter, spi 3 wire)
config := uint(d.Standby<<5) | uint(d.Filter<<2) | 0x00
d.bus.WriteRegister(uint8(d.Address), REG_CONFIG, []byte{byte(config)})
legacy.WriteRegister(d.bus, uint8(d.Address), REG_CONFIG, []byte{byte(config)})
// Write the control (temperature oversampling, pressure oversampling,
config = uint(d.Temperature<<5) | uint(d.Pressure<<2) | uint(d.Mode)
d.bus.WriteRegister(uint8(d.Address), REG_CTRL_MEAS, []byte{byte(config)})
legacy.WriteRegister(d.bus, uint8(d.Address), REG_CTRL_MEAS, []byte{byte(config)})
// Read Calibration data
data := make([]byte, 24)
err := d.bus.ReadRegister(uint8(d.Address), REG_CALI, data)
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_CALI, data)
if err != nil {
return
}
@@ -207,18 +208,18 @@ func (d *Device) readData(register int, n int) ([]byte, error) {
// After the measurement in FORCED mode, the sensor will return to SLEEP mode
if d.Mode != MODE_NORMAL {
config := uint(d.Temperature<<5) | uint(d.Pressure<<2) | uint(MODE_FORCED)
d.bus.WriteRegister(uint8(d.Address), REG_CTRL_MEAS, []byte{byte(config)})
legacy.WriteRegister(d.bus, uint8(d.Address), REG_CTRL_MEAS, []byte{byte(config)})
}
// Check STATUS register, wait if data is not available yet
status := make([]byte, 1)
for d.bus.ReadRegister(uint8(d.Address), uint8(REG_STATUS), status[0:]); status[0] != 4 && status[0] != 0; d.bus.ReadRegister(uint8(d.Address), uint8(REG_STATUS), status[0:]) {
for legacy.ReadRegister(d.bus, uint8(d.Address), uint8(REG_STATUS), status[0:]); status[0] != 4 && status[0] != 0; legacy.ReadRegister(d.bus, uint8(d.Address), uint8(REG_STATUS), status[0:]) {
time.Sleep(time.Millisecond)
}
// Read the requested register
data := make([]byte, n)
err := d.bus.ReadRegister(uint8(d.Address), uint8(register), data[:])
err := legacy.ReadRegister(d.bus, uint8(d.Address), uint8(register), data[:])
return data, err
}
+3 -2
View File
@@ -4,6 +4,7 @@ import (
"errors"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
)
var (
@@ -240,10 +241,10 @@ func (d *Device) configurationError() bool {
func (d *Device) readRegister(register byte, len int) (data []byte, err error) {
data = make([]byte, len)
err = d.bus.ReadRegister(d.Address, register, data)
err = legacy.ReadRegister(d.bus, d.Address, register, data)
return
}
func (d *Device) writeRegister(register byte, data byte) error {
return d.bus.WriteRegister(d.Address, register, []byte{data})
return legacy.WriteRegister(d.bus, d.Address, register, []byte{data})
}
+54
View File
@@ -0,0 +1,54 @@
#include <stdint.h>
#include <stdbool.h>
// Loop the given times, where one loop takes four CPU cycles.
bool tinygo_drivers_sleep(uint32_t cycles) {
// In this function, a [n] comment indicates the number of cycles an
// instruction or a set of instructions take. This is typically 1 for most
// arithmetic instructions, and a bit more for branches.
#if __ARM_ARCH_6M__ || __ARM_ARCH_7M__ || __ARM_ARCH_7EM__
// Inline assembly for Cortex-M0/M0+/M3/M4/M7.
// The Cortex-M0 (but not M0+) takes one more cycle, so is off by 12.5%.
// Others should be basically cycle-accurate (with a slight overhead to
// calculate the number of cycles). Unfortunately, there doesn't appear to
// be a preprocessor macro to detect the Cortex-M0 specifically (although we
// could rely on macros like NRF51).
// Each loop takes 8 cycles (5 nops, 1 sub, and 2 for the branch).
uint32_t loops = (cycles + 7) / 8;
__asm__ __volatile__(
"1:\n\t"
"nop\n\t" // [5] nops
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"subs %[loops], #1\n\t" // [1]
"bne 1b" // [1-4], at least 2 cycles if taken
: [loops]"+r"(loops)
);
return true;
#elif __XTENSA__
// Inline assembly for Xtensa.
// I don't know exactly how many cycles a branch takes, so I've taken a
// conservative guess and assume it takes only one cycle. In practice, it's
// probably more than that.
uint32_t loops = (cycles + 7) / 8;
__asm__ __volatile__(
"1:\n\t"
"nop\n\t" // [6] nops
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"addi %[loops], %[loops], -1\n\t" // [1]
"bnez %[loops], 1b" // [1?]
: [loops]"+r"(loops)
);
return true;
#else
// Unknown architecture, so fall back to time.Sleep.
return false;
#endif
}
+57
View File
@@ -0,0 +1,57 @@
package delay
import (
"machine"
"time"
)
/*
#include <stdint.h>
#include <stdbool.h>
bool tinygo_drivers_sleep(uint32_t ticks);
*/
import "C"
// Sleep for a very precise short duration by busy-waiting for the given time.
// This is not an efficient way to sleep: it will needlessly burn cycles while
// sleeping. But it is useful for sleeping for a very short duration, for
// example for bit-banged protocols.
//
// Longer durations (longer than a few milliseconds) will be handled by calling
// time.Sleep instead.
//
// This function should be called with a constant duration value, in which case
// the call will typically be fully inlined and only take up around nine
// instructions for the entire loop.
//
//go:inline
func Sleep(duration time.Duration) {
if time.Duration(uint32(duration)&0xff_ffff) != duration {
// This is a long duration (more than 16ms) which shouldn't be done by
// busy-waiting.
time.Sleep(duration)
return
}
// Calculate the number of cycles we should sleep:
// cycles = duration * freq / 1e9
// Avoiding a 64-bit division:
// cycles = duration * (freq/1000_000) / 1000
//
// This assumes:
// * The CPU frequency is a constant and can trivially be
// const-propagated, therefore the divide by 1000_000 is done at compile
// time.
// * The CPU frequency is a multiple of 1000_000, which is true for most
// chips (examples: 16MHz, 48MHz, 120MHz, etc).
// * The division by 1000 can be done efficiently (Cortex-M3 and up), or
// can be fully const-propagated.
// * The CPU frequency is lower than 256MHz. If it is higher, long sleep
// times (1-16ms) may not work correctly.
cycles := uint32(duration) * (machine.CPUFrequency() / 1000_000) / 1000
slept := C.tinygo_drivers_sleep(C.uint32_t(cycles))
if !slept {
// Fallback for platforms without inline assembly support.
time.Sleep(duration)
}
}
+16
View File
@@ -12,3 +12,19 @@ type Displayer interface {
// Display sends the buffer (if any) to the screen.
Display() error
}
// Rotation is how much a display has been rotated. Displays can be rotated, and
// sometimes also mirrored.
type Rotation uint8
// Clockwise rotation of the screen.
const (
Rotation0 = iota
Rotation90
Rotation180
Rotation270
Rotation0Mirror
Rotation90Mirror
Rotation180Mirror
Rotation270Mirror
)
+5 -4
View File
@@ -9,6 +9,7 @@ import (
"time"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
)
// Device wraps an I2C connection to a DS1307 device.
@@ -44,7 +45,7 @@ func (d *Device) SetTime(t time.Time) error {
// ReadTime returns the date and time
func (d *Device) ReadTime() (time.Time, error) {
data := make([]byte, 8)
err := d.bus.ReadRegister(d.Address, uint8(TimeDate), data)
err := legacy.ReadRegister(d.bus, d.Address, uint8(TimeDate), data)
if err != nil {
return time.Time{}, err
}
@@ -105,7 +106,7 @@ func (d *Device) Read(data []uint8) (n int, err error) {
if int(d.AddressSRAM)+len(data)-1 > SRAMEndAddress {
return 0, errors.New("EOF")
}
err = d.bus.ReadRegister(d.Address, d.AddressSRAM, data)
err = legacy.ReadRegister(d.bus, d.Address, d.AddressSRAM, data)
if err != nil {
return 0, err
}
@@ -124,7 +125,7 @@ func (d *Device) SetOscillatorFrequency(sqw uint8) error {
// IsOscillatorRunning returns if the oscillator is running
func (d *Device) IsOscillatorRunning() bool {
data := []byte{0}
err := d.bus.ReadRegister(d.Address, uint8(TimeDate), data)
err := legacy.ReadRegister(d.bus, d.Address, uint8(TimeDate), data)
if err != nil {
return false
}
@@ -134,7 +135,7 @@ func (d *Device) IsOscillatorRunning() bool {
// SetOscillatorRunning starts/stops internal oscillator by toggling halt bit
func (d *Device) SetOscillatorRunning(running bool) error {
data := make([]byte, 3)
err := d.bus.ReadRegister(d.Address, uint8(TimeDate), data)
err := legacy.ReadRegister(d.bus, d.Address, uint8(TimeDate), data)
if err != nil {
return err
}
+89
View File
@@ -0,0 +1,89 @@
// Package ds18b20 provides a driver for the DS18B20 digital thermometer
//
// Datasheet:
// https://www.analog.com/media/en/technical-documentation/data-sheets/DS18B20.pdf
package ds18b20 // import "tinygo.org/x/drivers/ds18b20"
import (
"errors"
)
// Device ROM commands
const (
CONVERT_TEMPERATURE uint8 = 0x44
READ_SCRATCHPAD uint8 = 0xBE
WRITE_SCRATCHPAD uint8 = 0x4E
)
type OneWireDevice interface {
Write(uint8)
Read() uint8
Select([]uint8) error
Сrc8([]uint8, int) uint8
}
// Device wraps a connection to an 1-Wire devices.
type Device struct {
owd OneWireDevice
}
// Errors list
var (
errReadTemperature = errors.New("Error: DS18B20. Read temperature error: CRC mismatch.")
)
func New(owd OneWireDevice) Device {
return Device{
owd: owd,
}
}
// Configure. Initializes the device, left for compatibility reasons.
func (d Device) Configure() {}
// ThermometerResolution sets thermometer resolution from 9 to 12 bits
func (d Device) ThermometerResolution(romid []uint8, resolution uint8) {
if 9 <= resolution && resolution <= 12 {
d.owd.Select(romid)
d.owd.Write(WRITE_SCRATCHPAD) // send three data bytes to scratchpad (TH, TL, and config)
d.owd.Write(0xFF) // to TH
d.owd.Write(0x00) // to TL
d.owd.Write(((resolution - 9) << 5) | 0x1F) // to resolution config
}
}
// RequestTemperature sends request to device
func (d Device) RequestTemperature(romid []uint8) {
d.owd.Select(romid)
d.owd.Write(CONVERT_TEMPERATURE)
}
// ReadTemperatureRaw returns the raw temperature.
// ScratchPad memory map:
// byte 0: Temperature LSB
// byte 1: Temperature MSB
func (d Device) ReadTemperatureRaw(romid []uint8) ([]uint8, error) {
spb := make([]uint8, 9) // ScratchPad buffer
d.owd.Select(romid)
d.owd.Write(READ_SCRATCHPAD)
for i := 0; i < 9; i++ {
spb[i] = d.owd.Read()
}
if d.owd.Сrc8(spb, 8) != spb[8] {
return nil, errReadTemperature
}
return spb[:2:2], nil
}
// ReadTemperature returns the temperature in celsius milli degrees (°C/1000)
func (d Device) ReadTemperature(romid []uint8) (int32, error) {
raw, err := d.ReadTemperatureRaw(romid)
if err != nil {
return 0, err
}
t := int32(uint16(raw[0]) | uint16(raw[1])<<8)
if t&0x8000 == 0x8000 {
t -= 0x10000
}
return (t * 625 / 10), nil
}
+46 -11
View File
@@ -8,6 +8,7 @@ import (
"time"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
)
type Mode uint8
@@ -37,7 +38,7 @@ func (d *Device) Configure() bool {
// IsTimeValid return true/false is the time in the device is valid
func (d *Device) IsTimeValid() bool {
data := []byte{0}
err := d.bus.ReadRegister(uint8(d.Address), REG_STATUS, data)
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_STATUS, data)
if err != nil {
return false
}
@@ -47,7 +48,7 @@ func (d *Device) IsTimeValid() bool {
// IsRunning returns if the oscillator is running
func (d *Device) IsRunning() bool {
data := []uint8{0}
err := d.bus.ReadRegister(uint8(d.Address), REG_CONTROL, data)
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_CONTROL, data)
if err != nil {
return false
}
@@ -57,7 +58,7 @@ func (d *Device) IsRunning() bool {
// SetRunning starts the internal oscillator
func (d *Device) SetRunning(isRunning bool) error {
data := []uint8{0}
err := d.bus.ReadRegister(uint8(d.Address), REG_CONTROL, data)
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_CONTROL, data)
if err != nil {
return err
}
@@ -66,22 +67,32 @@ func (d *Device) SetRunning(isRunning bool) error {
} else {
data[0] |= 1 << EOSC
}
err = d.bus.WriteRegister(uint8(d.Address), REG_CONTROL, data)
err = legacy.WriteRegister(d.bus, uint8(d.Address), REG_CONTROL, data)
if err != nil {
return err
}
return nil
}
// SetTime sets the date and time in the DS3231
// SetTime sets the date and time in the DS3231. The DS3231 hardware supports
// only a 2-digit year field, so the current year will be stored as an offset
// from the year 2000, which supports the year 2000 until 2100.
//
// The DS3231 also supports a one-bit 'century' flag which is set by the chip
// when the year field rolls over from 99 to 00. The current code interprets
// this flag to be the year 2100, which appears to extend the range of years
// until the year 2200. However the DS3231 does not incorporate the 'century'
// flag in its leap year calculation, so it will incorrectly identify the year
// 2100 as a leap year, causing it to increment from 2100-02-28 to 2100-02-29
// instead of 2100-03-01.
func (d *Device) SetTime(dt time.Time) error {
data := []byte{0}
err := d.bus.ReadRegister(uint8(d.Address), REG_STATUS, data)
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_STATUS, data)
if err != nil {
return err
}
data[0] &^= 1 << OSF
err = d.bus.WriteRegister(uint8(d.Address), REG_STATUS, data)
err = legacy.WriteRegister(d.bus, uint8(d.Address), REG_STATUS, data)
if err != nil {
return err
}
@@ -92,6 +103,10 @@ func (d *Device) SetTime(dt time.Time) error {
data[2] = uint8ToBCD(uint8(dt.Hour()))
year := uint8(dt.Year() - 2000)
// This code interprets the centuryFlag to be the year 2100. Warning: The
// DS3231 does not incorporate the centuryFlag in its leap year calculation.
// It will increment from 2100-02-28 to 2100-02-29, which is incorrect because
// the year 2100 is not a leap year in the Gregorian calendar.
centuryFlag := uint8(0)
if year >= 100 {
year -= 100
@@ -103,7 +118,7 @@ func (d *Device) SetTime(dt time.Time) error {
data[5] = uint8ToBCD(uint8(dt.Month()) | centuryFlag)
data[6] = uint8ToBCD(year)
err = d.bus.WriteRegister(uint8(d.Address), REG_TIMEDATE, data)
err = legacy.WriteRegister(d.bus, uint8(d.Address), REG_TIMEDATE, data)
if err != nil {
return err
}
@@ -114,7 +129,7 @@ func (d *Device) SetTime(dt time.Time) error {
// ReadTime returns the date and time
func (d *Device) ReadTime() (dt time.Time, err error) {
data := make([]uint8, 7)
err = d.bus.ReadRegister(uint8(d.Address), REG_TIMEDATE, data)
err = legacy.ReadRegister(d.bus, uint8(d.Address), REG_TIMEDATE, data)
if err != nil {
return
}
@@ -136,11 +151,31 @@ func (d *Device) ReadTime() (dt time.Time, err error) {
// ReadTemperature returns the temperature in millicelsius (mC)
func (d *Device) ReadTemperature() (int32, error) {
data := make([]uint8, 2)
err := d.bus.ReadRegister(uint8(d.Address), REG_TEMP, data)
err := legacy.ReadRegister(d.bus, uint8(d.Address), REG_TEMP, data)
if err != nil {
return 0, err
}
return int32(data[0])*1000 + int32((data[1]>>6)*25)*10, nil
return milliCelsius(data[0], data[1]), nil
}
// milliCelsius converts the raw temperature bytes (msb and lsb) from the DS3231
// into a 32-bit signed integer in units of milli Celsius (1/1000 deg C).
//
// According to the DS3231 datasheet: "Temperature is represented as a 10-bit
// code with a resolution of 0.25 deg C and is accessible at location 11h and
// 12h. The temperature is encoded in two's complement format. The upper 8 bits,
// the integer portion, are at location 11h and the lower 2 bits, the fractional
// portion, are in the upper nibble at location 12h."
//
// In other words, the msb and lsb bytes should be treated as a signed 16-bit
// integer in units of (1/256 deg C). It is possible to convert this into a
// 16-bit signed integer in units of centi Celsius (1/100 deg C) with no loss of
// precision or dynamic range. But for backwards compatibility, let's instead
// convert this into a 32-bit signed integer in units of milli Celsius.
func milliCelsius(msb uint8, lsb uint8) int32 {
t256 := int16(uint16(msb)<<8 | uint16(lsb))
t1000 := int32(t256) / 64 * 250
return t1000
}
// uint8ToBCD converts a byte to BCD for the DS3231
+76
View File
@@ -0,0 +1,76 @@
package ds3231
import (
"testing"
)
func TestPositiveMilliCelsius(t *testing.T) {
t1000 := milliCelsius(0, 0)
if t1000 != 0 {
t.Fatal(t1000)
}
t1000 = milliCelsius(0, 0b01000000)
if t1000 != 250 {
t.Fatal(t1000)
}
t1000 = milliCelsius(0, 0b10000000)
if t1000 != 500 {
t.Fatal(t1000)
}
t1000 = milliCelsius(0, 0b11000000)
if t1000 != 750 {
t.Fatal(t1000)
}
t1000 = milliCelsius(1, 0b00000000)
if t1000 != 1000 {
t.Fatal(t1000)
}
t1000 = milliCelsius(2, 0b00000000)
if t1000 != 2000 {
t.Fatal(t1000)
}
// highest temperature is 127.750C
t1000 = milliCelsius(0x7f, 0b11000000)
if t1000 != 127750 {
t.Fatal(t1000)
}
}
func TestNegativeMilliCelsius(t *testing.T) {
t1000 := milliCelsius(0xff, 0b11000000)
if t1000 != -250 {
t.Fatal(t1000)
}
t1000 = milliCelsius(0xff, 0b10000000)
if t1000 != -500 {
t.Fatal(t1000)
}
t1000 = milliCelsius(0xff, 0b01000000)
if t1000 != -750 {
t.Fatal(t1000)
}
t1000 = milliCelsius(0xff, 0b00000000)
if t1000 != -1000 {
t.Fatal(t1000)
}
t1000 = milliCelsius(0xfe, 0b00000000)
if t1000 != -2000 {
t.Fatal(t1000)
}
// lowest temperature is -128.000C
t1000 = milliCelsius(0x80, 0b00000000)
if t1000 != -128000 {
t.Fatal(t1000)
}
}
-20
View File
@@ -1,20 +0,0 @@
package espat
import (
"time"
"tinygo.org/x/drivers/net"
)
func (d *Device) ConnectToAccessPoint(ssid, pass string, timeout time.Duration) error {
if len(ssid) == 0 {
return net.ErrWiFiMissingSSID
}
d.SetWifiMode(WifiModeClient)
return d.ConnectToAP(ssid, pass, int(timeout.Seconds()))
}
func (d *Device) Disconnect() error {
return d.DisconnectFromAP()
}
+297 -34
View File
@@ -15,41 +15,302 @@
//
// AT command set:
// https://www.espressif.com/sites/default/files/documentation/4a-esp8266_at_instruction_set_en.pdf
//
// 02/2023 sfeldma@gmail.com Heavily modified to use netdev interface
package espat // import "tinygo.org/x/drivers/espat"
import (
"errors"
"fmt"
"machine"
"net"
"net/netip"
"strconv"
"strings"
"sync"
"time"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/net"
"tinygo.org/x/drivers/netdev"
"tinygo.org/x/drivers/netlink"
)
// Device wraps UART connection to the ESP8266/ESP32.
type Device struct {
bus drivers.UART
type Config struct {
// UART config
Uart *machine.UART
Tx machine.Pin
Rx machine.Pin
}
type socket struct {
inUse bool
protocol int
laddr netip.AddrPort
}
type Device struct {
cfg *Config
uart *machine.UART
// command responses that come back from the ESP8266/ESP32
response []byte
// data received from a TCP/UDP connection forwarded by the ESP8266/ESP32
socketdata []byte
data []byte
socket socket
mu sync.Mutex
}
// ActiveDevice is the currently configured Device in use. There can only be one.
var ActiveDevice *Device
// New returns a new espat driver. Pass in a fully configured UART bus.
func New(b drivers.UART) *Device {
return &Device{bus: b, response: make([]byte, 512), socketdata: make([]byte, 0, 1024)}
func NewDevice(cfg *Config) *Device {
return &Device{
cfg: cfg,
response: make([]byte, 1500),
data: make([]byte, 0, 1500),
}
}
// Configure sets up the device for communication.
func (d Device) Configure() {
ActiveDevice = &d
net.ActiveDevice = ActiveDevice
func (d *Device) NetConnect(params *netlink.ConnectParams) error {
if len(params.Ssid) == 0 {
return netlink.ErrMissingSSID
}
d.uart = d.cfg.Uart
d.uart.Configure(machine.UARTConfig{TX: d.cfg.Tx, RX: d.cfg.Rx})
// Connect to ESP8266/ESP32
fmt.Printf("Connecting to device...")
for i := 0; i < 5; i++ {
if d.Connected() {
break
}
time.Sleep(1 * time.Second)
}
if !d.Connected() {
fmt.Printf("FAILED\r\n")
return netlink.ErrConnectFailed
}
fmt.Printf("CONNECTED\r\n")
// Connect to Wifi AP
fmt.Printf("Connecting to Wifi SSID '%s'...", params.Ssid)
d.SetWifiMode(WifiModeClient)
err := d.ConnectToAP(params.Ssid, params.Passphrase, 10 /* secs */)
if err != nil {
fmt.Printf("FAILED\r\n")
return err
}
fmt.Printf("CONNECTED\r\n")
ip, err := d.Addr()
if err != nil {
return err
}
fmt.Printf("DHCP-assigned IP: %s\r\n", ip)
fmt.Printf("\r\n")
return nil
}
func (d *Device) NetDisconnect() {
d.DisconnectFromAP()
fmt.Printf("\r\nDisconnected from Wifi\r\n\r\n")
}
func (d *Device) NetNotify(cb func(netlink.Event)) {
// Not supported
}
func (d *Device) GetHostByName(name string) (netip.Addr, error) {
ip, err := d.GetDNS(name)
if err != nil {
return netip.Addr{}, err
}
return netip.ParseAddr(ip)
}
func (d *Device) GetHardwareAddr() (net.HardwareAddr, error) {
return net.HardwareAddr{}, netlink.ErrNotSupported
}
func (d *Device) Addr() (netip.Addr, error) {
resp, err := d.GetClientIP()
if err != nil {
return netip.Addr{}, err
}
prefix := "+CIPSTA:ip:"
for _, line := range strings.Split(resp, "\n") {
if ok := strings.HasPrefix(line, prefix); ok {
ip := line[len(prefix)+1 : len(line)-2]
return netip.ParseAddr(ip)
}
}
return netip.Addr{}, fmt.Errorf("Error getting IP address")
}
func (d *Device) Socket(domain int, stype int, protocol int) (int, error) {
switch domain {
case netdev.AF_INET:
default:
return -1, netdev.ErrFamilyNotSupported
}
switch {
case protocol == netdev.IPPROTO_TCP && stype == netdev.SOCK_STREAM:
case protocol == netdev.IPPROTO_TLS && stype == netdev.SOCK_STREAM:
case protocol == netdev.IPPROTO_UDP && stype == netdev.SOCK_DGRAM:
default:
return -1, netdev.ErrProtocolNotSupported
}
// Only supporting single connection mode, so only one socket at a time
if d.socket.inUse {
return -1, netdev.ErrNoMoreSockets
}
d.socket.inUse = true
d.socket.protocol = protocol
return 0, nil
}
func (d *Device) Bind(sockfd int, ip netip.AddrPort) error {
d.socket.laddr = ip
return nil
}
func (d *Device) Connect(sockfd int, host string, ip netip.AddrPort) error {
var err error
var addr = ip.Addr().String()
var rport = strconv.Itoa(int(ip.Port()))
var lport = strconv.Itoa(int(d.socket.laddr.Port()))
switch d.socket.protocol {
case netdev.IPPROTO_TCP:
err = d.ConnectTCPSocket(addr, rport)
case netdev.IPPROTO_UDP:
err = d.ConnectUDPSocket(addr, rport, lport)
case netdev.IPPROTO_TLS:
err = d.ConnectSSLSocket(host, rport)
}
if err != nil {
if host == "" {
return fmt.Errorf("Connect to %s timed out", ip)
} else {
return fmt.Errorf("Connect to %s:%d timed out", host, ip.Port())
}
}
return nil
}
func (d *Device) Listen(sockfd int, backlog int) error {
switch d.socket.protocol {
case netdev.IPPROTO_UDP:
default:
return netdev.ErrProtocolNotSupported
}
return nil
}
func (d *Device) Accept(sockfd int, ip netip.AddrPort) (int, error) {
return -1, netdev.ErrNotSupported
}
func (d *Device) sendChunk(sockfd int, buf []byte, deadline time.Time) (int, error) {
// Check if we've timed out
if !deadline.IsZero() {
if time.Now().After(deadline) {
return -1, netdev.ErrTimeout
}
}
err := d.StartSocketSend(len(buf))
if err != nil {
return -1, err
}
n, err := d.Write(buf)
if err != nil {
return -1, err
}
_, err = d.Response(1000)
if err != nil {
return -1, err
}
return n, err
}
func (d *Device) Send(sockfd int, buf []byte, flags int, deadline time.Time) (int, error) {
d.mu.Lock()
defer d.mu.Unlock()
// Break large bufs into chunks so we don't overrun the hw queue
chunkSize := 1436
for i := 0; i < len(buf); i += chunkSize {
end := i + chunkSize
if end > len(buf) {
end = len(buf)
}
_, err := d.sendChunk(sockfd, buf[i:end], deadline)
if err != nil {
return -1, err
}
}
return len(buf), nil
}
func (d *Device) Recv(sockfd int, buf []byte, flags int, deadline time.Time) (int, error) {
d.mu.Lock()
defer d.mu.Unlock()
var length = len(buf)
// Limit length read size to chunk large read requests
if length > 1436 {
length = 1436
}
for {
// Check if we've timed out
if !deadline.IsZero() {
if time.Now().After(deadline) {
return -1, netdev.ErrTimeout
}
}
n, err := d.ReadSocket(buf[:length])
if err != nil {
return -1, err
}
if n == 0 {
d.mu.Unlock()
time.Sleep(100 * time.Millisecond)
d.mu.Lock()
continue
}
return n, nil
}
}
func (d *Device) Close(sockfd int) error {
d.mu.Lock()
defer d.mu.Unlock()
d.socket.inUse = false
return d.DisconnectSocket()
}
func (d *Device) SetSockOpt(sockfd int, level int, opt int, value interface{}) error {
return netdev.ErrNotSupported
}
// Connected checks if there is communication with the ESP8266/ESP32.
@@ -57,7 +318,7 @@ func (d *Device) Connected() bool {
d.Execute(Test)
// handle response here, should include "OK"
_, err := d.Response(100)
_, err := d.Response(1000)
if err != nil {
return false
}
@@ -66,12 +327,12 @@ func (d *Device) Connected() bool {
// Write raw bytes to the UART.
func (d *Device) Write(b []byte) (n int, err error) {
return d.bus.Write(b)
return d.uart.Write(b)
}
// Read raw bytes from the UART.
func (d *Device) Read(b []byte) (n int, err error) {
return d.bus.Read(b)
return d.uart.Read(b)
}
// how long in milliseconds to pause after sending AT commands
@@ -100,9 +361,10 @@ func (d Device) Set(cmd, params string) error {
// Version returns the ESP8266/ESP32 firmware version info.
func (d Device) Version() []byte {
d.Execute(Version)
r, err := d.Response(100)
r, err := d.Response(2000)
if err != nil {
return []byte("unknown")
//return []byte("unknown")
return []byte(err.Error())
}
return r
}
@@ -132,16 +394,16 @@ func (d *Device) ReadSocket(b []byte) (n int, err error) {
d.Response(300)
count := len(b)
if len(b) >= len(d.socketdata) {
if len(b) >= len(d.data) {
// copy it all, then clear socket data
count = len(d.socketdata)
copy(b, d.socketdata[:count])
d.socketdata = d.socketdata[:0]
count = len(d.data)
copy(b, d.data[:count])
d.data = d.data[:0]
} else {
// copy all we can, then keep the remaining socket data around
copy(b, d.socketdata[:count])
copy(d.socketdata, d.socketdata[count:])
d.socketdata = d.socketdata[:len(d.socketdata)-count]
copy(b, d.data[:count])
copy(d.data, d.data[count:])
d.data = d.data[:len(d.data)-count]
}
return count, nil
@@ -157,11 +419,11 @@ func (d *Device) Response(timeout int) ([]byte, error) {
retries := timeout / pause
for {
size = d.bus.Buffered()
size = d.uart.Buffered()
if size > 0 {
end += size
d.bus.Read(d.response[start:end])
d.uart.Read(d.response[start:end])
// if "+IPD" then read socket data
if strings.Contains(string(d.response[:end]), "+IPD") {
@@ -204,18 +466,19 @@ func (d *Device) parseIPD(end int) error {
val := string(d.response[s+5 : e])
// TODO: verify count
_, err := strconv.Atoi(val)
v, err := strconv.Atoi(val)
if err != nil {
// not expected data here. what to do?
return err
}
// load up the socket data
d.socketdata = append(d.socketdata, d.response[e+1:end]...)
//d.data = append(d.data, d.response[e+1:end]...)
d.data = append(d.data, d.response[e+1:e+1+v]...)
return nil
}
// IsSocketDataAvailable returns of there is socket data available
func (d *Device) IsSocketDataAvailable() bool {
return len(d.socketdata) > 0 || d.bus.Buffered() > 0
return len(d.data) > 0 || d.uart.Buffered() > 0
}
+1 -1
View File
@@ -51,7 +51,7 @@ func (d *Device) ConnectTCPSocket(addr, port string) error {
// ConnectUDPSocket creates a new UDP connection for the ESP8266/ESP32.
func (d *Device) ConnectUDPSocket(addr, sendport, listenport string) error {
protocol := "UDP"
val := "\"" + protocol + "\",\"" + addr + "\"," + sendport + "," + listenport + ",2"
val := "\"" + protocol + "\",\"" + addr + "\"," + sendport + "," + listenport + ",0"
err := d.Set(TCPConnect, val)
if err != nil {
return err
+1 -4
View File
@@ -44,10 +44,7 @@ func (d *Device) ConnectToAP(ssid, pwd string, ws int) error {
d.Set(ConnectAP, val)
_, err := d.Response(ws * 1000)
if err != nil {
return err
}
return nil
return err
}
// DisconnectFromAP disconnects the ESP8266/ESP32 from the current access point.
+42
View File
@@ -0,0 +1,42 @@
package main
import (
"image/color"
"machine"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/adafruit4650"
"tinygo.org/x/tinyfont"
"tinygo.org/x/tinyfont/freemono"
)
func main() {
machine.I2C0.Configure(machine.I2CConfig{})
dev := adafruit4650.New(machine.I2C0)
err := dev.Configure()
if err != nil {
panic(err)
}
drawPlus(&dev)
drawHelloWorld(&dev)
err = dev.Display()
if err != nil {
panic(err)
}
}
func drawPlus(d drivers.Displayer) {
for i := int16(0); i < 128; i++ {
d.SetPixel(i, 32, color.RGBA{R: 1})
}
for i := int16(0); i < 64; i++ {
d.SetPixel(64, i, color.RGBA{R: 1})
}
}
func drawHelloWorld(d drivers.Displayer) {
tinyfont.WriteLine(d, &freemono.Regular9pt7b, 0, 32, "Hello World!", color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff})
}
+66
View File
@@ -0,0 +1,66 @@
package main
import (
"machine"
"machine/usb/hid/mouse"
"math"
"time"
"tinygo.org/x/drivers/as560x"
)
func main() {
// Let's use the AS5600 to make the world's most useless mouse with just a single X-axis & no buttons (!)
machine.I2C0.Configure(machine.I2CConfig{
Frequency: machine.TWI_FREQ_400KHZ,
SDA: machine.GPIO4,
SCL: machine.GPIO5,
})
as5600 := as560x.NewAS5600(machine.I2C0)
as5600.Configure(as560x.Config{})
mouse := mouse.New()
lastAngle := -1
for {
time.Sleep(time.Millisecond * 10)
// Get the magnet status of the AS5600
magnetDetected, magnetStrength, err := as5600.MagnetStatus()
if err != nil {
continue
}
// Get the raw angle from the AS5600
angle, _, err := as5600.RawAngle(as560x.ANGLE_NATIVE)
if err != nil {
continue
}
str := ""
if !magnetDetected {
str += "NOT "
}
str += "detected. Strength is "
switch magnetStrength {
case as560x.MagnetTooWeak:
str += "too weak"
case as560x.MagnetTooStrong:
str += "too strong"
default:
str += "ok"
}
println("Raw angle:", angle, "Magnet was", str)
if lastAngle != -1 {
diff := int(angle) - lastAngle
// correct the zero crossover glitch
if diff < -0xc00 {
diff += 0xfff
} else if diff > 0xc00 {
diff -= 0xfff
}
// debounce the noise (could use the sensor's filters/hysteresis instead?)
if math.Abs(float64(diff)) > 2 {
// move the mouse x-axis in response to the AS5600
mouse.Move(diff, 0)
}
}
lastAngle = int(angle)
}
}
+54
View File
@@ -0,0 +1,54 @@
package main
// Smoke test for the BMA421/BMA425 sensors.
// Warning: this code has _not been tested_. It's only here as a smoke test.
import (
"fmt"
"machine"
"time"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/bma42x"
)
func main() {
time.Sleep(5 * time.Second)
i2cBus := machine.I2C1
i2cBus.Configure(machine.I2CConfig{
Frequency: 400 * machine.KHz,
SDA: machine.SDA_PIN,
SCL: machine.SCL_PIN,
})
sensor := bma42x.NewI2C(i2cBus, bma42x.Address)
err := sensor.Configure(bma42x.Config{
Device: bma42x.DeviceBMA421 | bma42x.DeviceBMA425,
Features: bma42x.FeatureStepCounting,
})
if err != nil {
println("could not configure BMA421/BMA425:", err)
return
}
if !sensor.Connected() {
println("BMA42x not connected")
return
}
for {
time.Sleep(time.Second)
err := sensor.Update(drivers.Acceleration | drivers.Temperature)
if err != nil {
println("Error reading sensor", err)
continue
}
fmt.Printf("Temperature: %.2f °C\n", float32(sensor.Temperature())/1000)
accelX, accelY, accelZ := sensor.Acceleration()
fmt.Printf("Acceleration: %.2fg %.2fg %.2fg\n", float32(accelX)/1e6, float32(accelY)/1e6, float32(accelZ)/1e6)
}
}
+3
View File
@@ -27,6 +27,9 @@ func main() {
pressure, _ := sensor.ReadPressure()
println("Pressure", float32(pressure)/100000, "hPa")
altitude, _ := sensor.ReadAltitude()
println("Altitude", altitude, "meters")
time.Sleep(2 * time.Second)
}
}
+17
View File
@@ -0,0 +1,17 @@
package main
import (
"time"
"tinygo.org/x/drivers/delay"
)
func main() {
time.Sleep(time.Second) // wait for a serial console
start := time.Now()
for i := 0; i < 2000; i++ {
delay.Sleep(50 * time.Microsecond)
}
duration := time.Since(start)
println("sleep of 2000*50µs (100ms) took:", duration.String())
}
+58
View File
@@ -0,0 +1,58 @@
package main
import (
"encoding/hex"
"machine"
"time"
"tinygo.org/x/drivers/onewire"
"tinygo.org/x/drivers/ds18b20"
)
func main() {
// Define pin for DS18B20
pin := machine.D2
ow := onewire.New(pin)
romIDs, err := ow.Search(onewire.SEARCH_ROM)
if err != nil {
println(err)
}
sensor := ds18b20.New(ow)
for {
time.Sleep(3 * time.Second)
println()
println("Device:", machine.Device)
println()
println("Request Temperature.")
for _, romid := range romIDs {
println("Sensor RomID: ", hex.EncodeToString(romid))
sensor.RequestTemperature(romid)
}
// wait 750ms or more for DS18B20 convert T
time.Sleep(1 * time.Second)
println()
println("Read Temperature")
for _, romid := range romIDs {
raw, err := sensor.ReadTemperatureRaw(romid)
if err != nil {
println(err)
}
println()
println("Sensor RomID: ", hex.EncodeToString(romid))
println("Temperature Raw value: ", hex.EncodeToString(raw))
t, err := sensor.ReadTemperature(romid)
if err != nil {
println(err)
}
println("Temperature in celsius milli degrees (°C/1000): ", t)
}
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
// Connects to an MAG3110 I2C magnetometer.
// Connects to an DS3231 I2C Real Time Clock (RTC).
package main
import (
-145
View File
@@ -1,145 +0,0 @@
// This is a console to a ESP8266/ESP32 running on the device UART1.
// Allows you to type AT commands from your computer via the microcontroller.
//
// In other words:
// Your computer <--> UART0 <--> MCU <--> UART1 <--> ESP8266 <--> INTERNET
//
// More information on the Espressif AT command set at:
// https://www.espressif.com/sites/default/files/documentation/4a-esp8266_at_instruction_set_en.pdf
package main
import (
"machine"
"time"
"tinygo.org/x/drivers/espat"
)
// change actAsAP to true to act as an access point instead of connecting to one.
const actAsAP = false
var (
// access point info
ssid string
pass string
)
// these are the default pins for the Arduino Nano33 IoT.
// change these to connect to a different UART or pins for the ESP8266/ESP32
var (
uart = machine.UART1
tx = machine.PA22
rx = machine.PA23
console = machine.Serial
adaptor *espat.Device
)
func main() {
uart.Configure(machine.UARTConfig{TX: tx, RX: rx})
// Init esp8266
adaptor = espat.New(uart)
adaptor.Configure()
// first check if connected
if connectToESP() {
println("Connected to wifi adaptor.")
adaptor.Echo(false)
connectToAP()
} else {
println("")
failMessage("Unable to connect to wifi adaptor.")
return
}
println("Type an AT command then press enter:")
prompt()
input := make([]byte, 64)
i := 0
for {
if console.Buffered() > 0 {
data, _ := console.ReadByte()
switch data {
case 13:
// return key
console.Write([]byte("\r\n"))
// send command to ESP8266
input[i] = byte('\r')
input[i+1] = byte('\n')
adaptor.Write(input[:i+2])
// display response
r, _ := adaptor.Response(500)
console.Write(r)
// prompt
prompt()
i = 0
continue
default:
// just echo the character
console.WriteByte(data)
input[i] = data
i++
}
}
time.Sleep(10 * time.Millisecond)
}
}
func prompt() {
print("ESPAT>")
}
// connect to ESP8266/ESP32
func connectToESP() bool {
for i := 0; i < 5; i++ {
println("Connecting to wifi adaptor...")
if adaptor.Connected() {
return true
}
time.Sleep(1 * time.Second)
}
return false
}
// connect to access point
func connectToAP() {
println("Connecting to wifi network '" + ssid + "'")
if err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second); err != nil {
failMessage(err.Error())
}
println("Connected.")
ip, err := adaptor.GetClientIP()
if err != nil {
failMessage(err.Error())
}
println(ip)
}
// provide access point
func provideAP() {
println("Starting wifi network as access point '" + ssid + "'...")
adaptor.SetWifiMode(espat.WifiModeAP)
adaptor.SetAPConfig(ssid, pass, 7, espat.WifiAPSecurityWPA2_PSK)
println("Ready.")
ip, _ := adaptor.GetAPIP()
println(ip)
}
func failMessage(msg string) {
for {
println(msg)
time.Sleep(1 * time.Second)
}
}
-131
View File
@@ -1,131 +0,0 @@
// This is a sensor hub that uses a ESP8266/ESP32 running on the device UART1.
// It creates a UDP "server" you can use to get info to/from your computer via the microcontroller.
//
// In other words:
// Your computer <--> UART0 <--> MCU <--> UART1 <--> ESP8266 <--> INTERNET
package main
import (
"machine"
"time"
"tinygo.org/x/drivers/espat"
"tinygo.org/x/drivers/net"
)
// change actAsAP to true to act as an access point instead of connecting to one.
const actAsAP = false
var (
// access point info
ssid string
pass string
)
// these are the default pins for the Arduino Nano33 IoT.
// change these to connect to a different UART or pins for the ESP8266/ESP32
var (
uart = machine.UART1
tx = machine.PA22
rx = machine.PA23
adaptor *espat.Device
)
func main() {
uart.Configure(machine.UARTConfig{TX: tx, RX: rx})
// Init esp8266
adaptor = espat.New(uart)
adaptor.Configure()
readyled := machine.LED
readyled.Configure(machine.PinConfig{Mode: machine.PinOutput})
readyled.High()
// first check if connected
if connectToESP() {
println("Connected to wifi adaptor.")
adaptor.Echo(false)
connectToAP()
} else {
println("")
failMessage("Unable to connect to wifi adaptor.")
return
}
// now make UDP connection
laddr := &net.UDPAddr{Port: 2222}
println("Loading UDP listener...")
conn, _ := net.ListenUDP("UDP", laddr)
println("Waiting for data...")
data := make([]byte, 50)
blink := true
for {
n, _ := conn.Read(data)
if n > 0 {
println(string(data[:n]))
conn.Write([]byte("hello back\r\n"))
}
blink = !blink
if blink {
readyled.High()
} else {
readyled.Low()
}
time.Sleep(500 * time.Millisecond)
}
// Right now this code is never reached. Need a way to trigger it...
println("Disconnecting UDP...")
conn.Close()
println("Done.")
}
// connect to ESP8266/ESP32
func connectToESP() bool {
for i := 0; i < 5; i++ {
println("Connecting to wifi adaptor...")
if adaptor.Connected() {
return true
}
time.Sleep(1 * time.Second)
}
return false
}
// connect to access point
func connectToAP() {
println("Connecting to wifi network '" + ssid + "'")
if err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second); err != nil {
failMessage(err.Error())
}
println("Connected.")
ip, err := adaptor.GetClientIP()
if err != nil {
failMessage(err.Error())
}
println(ip)
}
// provide access point
func provideAP() {
println("Starting wifi network as access point '" + ssid + "'...")
adaptor.SetWifiMode(espat.WifiModeAP)
adaptor.SetAPConfig(ssid, pass, 7, espat.WifiAPSecurityWPA2_PSK)
println("Ready.")
ip, _ := adaptor.GetAPIP()
println(ip)
}
func failMessage(msg string) {
for {
println(msg)
time.Sleep(1 * time.Second)
}
}
-110
View File
@@ -1,110 +0,0 @@
// This is a sensor station that uses a ESP8266 or ESP32 running on the device UART1.
// It creates a UDP connection you can use to get info to/from your computer via the microcontroller.
//
// In other words:
// Your computer <--> UART0 <--> MCU <--> UART1 <--> ESP8266
package main
import (
"machine"
"time"
"tinygo.org/x/drivers/espat"
"tinygo.org/x/drivers/net"
)
var (
// access point info
ssid string
pass string
)
// IP address of the listener aka "hub". Replace with your own info.
const hubIP = "0.0.0.0"
// these are the default pins for the Arduino Nano33 IoT.
// change these to connect to a different UART or pins for the ESP8266/ESP32
var (
uart = machine.UART1
tx = machine.PA22
rx = machine.PA23
adaptor *espat.Device
)
func main() {
uart.Configure(machine.UARTConfig{TX: tx, RX: rx})
// Init esp8266/esp32
adaptor = espat.New(uart)
adaptor.Configure()
// first check if connected
if connectToESP() {
println("Connected to wifi adaptor.")
adaptor.Echo(false)
connectToAP()
} else {
println("")
failMessage("Unable to connect to wifi adaptor.")
return
}
// now make UDP connection
ip := net.ParseIP(hubIP)
raddr := &net.UDPAddr{IP: ip, Port: 2222}
laddr := &net.UDPAddr{Port: 2222}
println("Dialing UDP connection...")
conn, _ := net.DialUDP("udp", laddr, raddr)
for {
// send data
println("Sending data...")
conn.Write([]byte("hello\r\n"))
time.Sleep(1000 * time.Millisecond)
}
// Right now this code is never reached. Need a way to trigger it...
println("Disconnecting UDP...")
conn.Close()
println("Done.")
}
// connect to ESP8266/ESP32
func connectToESP() bool {
for i := 0; i < 5; i++ {
println("Connecting to wifi adaptor...")
if adaptor.Connected() {
return true
}
time.Sleep(1 * time.Second)
}
return false
}
// connect to access point
func connectToAP() {
println("Connecting to wifi network '" + ssid + "'")
if err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second); err != nil {
failMessage(err.Error())
}
println("Connected.")
ip, err := adaptor.GetClientIP()
if err != nil {
failMessage(err.Error())
}
println(ip)
}
func failMessage(msg string) {
for {
println(msg)
time.Sleep(1 * time.Second)
}
}
-144
View File
@@ -1,144 +0,0 @@
// This is a sensor station that uses a ESP8266 or ESP32 running on the device UART1.
// It creates an MQTT connection that publishes a message every second
// to an MQTT broker.
//
// In other words:
// Your computer <--> UART0 <--> MCU <--> UART1 <--> ESP8266 <--> Internet <--> MQTT broker.
//
// You must install the Paho MQTT package to build this program:
//
// go get -u github.com/eclipse/paho.mqtt.golang
package main
import (
"machine"
"math/rand"
"time"
"tinygo.org/x/drivers/espat"
"tinygo.org/x/drivers/net/mqtt"
)
var (
// access point info
ssid string
pass string
)
// IP address of the MQTT broker to use. Replace with your own info.
const server = "tcp://test.mosquitto.org:1883"
//const server = "ssl://test.mosquitto.org:8883"
// these are the default pins for the Arduino Nano33 IoT.
// change these to connect to a different UART or pins for the ESP8266/ESP32
var (
uart = machine.UART2
tx = machine.PA22
rx = machine.PA23
console = machine.Serial
adaptor *espat.Device
topic = "tinygo"
)
func main() {
time.Sleep(3000 * time.Millisecond)
uart.Configure(machine.UARTConfig{TX: tx, RX: rx})
rand.Seed(time.Now().UnixNano())
// Init esp8266/esp32
adaptor = espat.New(uart)
adaptor.Configure()
// first check if connected
if connectToESP() {
println("Connected to wifi adaptor.")
adaptor.Echo(false)
connectToAP()
} else {
println("")
failMessage("Unable to connect to wifi adaptor.")
return
}
opts := mqtt.NewClientOptions()
opts.AddBroker(server).SetClientID("tinygo-client-" + randomString(10))
println("Connecting to MQTT broker at", server)
cl := mqtt.NewClient(opts)
if token := cl.Connect(); token.Wait() && token.Error() != nil {
failMessage(token.Error().Error())
}
for {
println("Publishing MQTT message...")
data := []byte("{\"e\":[{ \"n\":\"hello\", \"v\":101 }]}")
token := cl.Publish(topic, 0, false, data)
token.Wait()
if token.Error() != nil {
println(token.Error().Error())
}
time.Sleep(1000 * time.Millisecond)
}
// Right now this code is never reached. Need a way to trigger it...
println("Disconnecting MQTT...")
cl.Disconnect(100)
println("Done.")
}
// connect to ESP8266/ESP32
func connectToESP() bool {
for i := 0; i < 5; i++ {
println("Connecting to wifi adaptor...")
if adaptor.Connected() {
return true
}
time.Sleep(1 * time.Second)
}
return false
}
// connect to access point
func connectToAP() {
println("Connecting to wifi network '" + ssid + "'")
if err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second); err != nil {
failMessage(err.Error())
}
println("Connected.")
ip, err := adaptor.GetClientIP()
if err != nil {
failMessage(err.Error())
}
println(ip)
}
// Returns an int >= min, < max
func randomInt(min, max int) int {
return min + rand.Intn(max-min)
}
// Generate a random string of A-Z chars with len = l
func randomString(len int) string {
bytes := make([]byte, len)
for i := 0; i < len; i++ {
bytes[i] = byte(randomInt(65, 90))
}
return string(bytes)
}
func failMessage(msg string) {
for {
println(msg)
time.Sleep(1 * time.Second)
}
}
-165
View File
@@ -1,165 +0,0 @@
// This is a sensor station that uses a ESP8266 or ESP32 running on the device UART1.
// It creates an MQTT connection that publishes a message every second
// to an MQTT broker.
//
// In other words:
// Your computer <--> UART0 <--> MCU <--> UART1 <--> ESP8266 <--> Internet <--> MQTT broker.
//
// You must also install the Paho MQTT package to build this program:
//
// go get -u github.com/eclipse/paho.mqtt.golang
package main
import (
"fmt"
"machine"
"math/rand"
"time"
"tinygo.org/x/drivers/espat"
"tinygo.org/x/drivers/net/mqtt"
)
var (
// access point info
ssid string
pass string
)
// IP address of the MQTT broker to use. Replace with your own info.
//const server = "tcp://test.mosquitto.org:1883"
const server = "ssl://test.mosquitto.org:8883"
// change these to connect to a different UART or pins for the ESP8266/ESP32
var (
// these are defaults for the Arduino Nano33 IoT.
uart = machine.UART1
tx = machine.PA22
rx = machine.PA23
console = machine.Serial
adaptor *espat.Device
cl mqtt.Client
topicTx = "tinygo/tx"
topicRx = "tinygo/rx"
)
func subHandler(client mqtt.Client, msg mqtt.Message) {
fmt.Printf("[%s] ", msg.Topic())
fmt.Printf("%s\r\n", msg.Payload())
}
func main() {
time.Sleep(3000 * time.Millisecond)
uart.Configure(machine.UARTConfig{TX: tx, RX: rx})
rand.Seed(time.Now().UnixNano())
// Init esp8266/esp32
adaptor = espat.New(uart)
adaptor.Configure()
// first check if connected
if connectToESP() {
println("Connected to wifi adaptor.")
adaptor.Echo(false)
connectToAP()
} else {
println("")
failMessage("Unable to connect to wifi adaptor.")
return
}
opts := mqtt.NewClientOptions()
opts.AddBroker(server).SetClientID("tinygo-client-" + randomString(10))
println("Connecting to MQTT broker at", server)
cl = mqtt.NewClient(opts)
if token := cl.Connect(); token.Wait() && token.Error() != nil {
failMessage(token.Error().Error())
}
// subscribe
token := cl.Subscribe(topicRx, 0, subHandler)
token.Wait()
if token.Error() != nil {
failMessage(token.Error().Error())
}
go publishing()
select {}
// Right now this code is never reached. Need a way to trigger it...
println("Disconnecting MQTT...")
cl.Disconnect(100)
println("Done.")
}
func publishing() {
for {
println("Publishing MQTT message...")
data := []byte("{\"e\":[{ \"n\":\"hello\", \"v\":101 }]}")
token := cl.Publish(topicTx, 0, false, data)
token.Wait()
if token.Error() != nil {
println(token.Error().Error())
}
time.Sleep(1000 * time.Millisecond)
}
}
// connect to ESP8266/ESP32
func connectToESP() bool {
for i := 0; i < 5; i++ {
println("Connecting to wifi adaptor...")
if adaptor.Connected() {
return true
}
time.Sleep(1 * time.Second)
}
return false
}
// connect to access point
func connectToAP() {
println("Connecting to wifi network '" + ssid + "'")
if err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second); err != nil {
failMessage(err.Error())
}
println("Connected.")
ip, err := adaptor.GetClientIP()
if err != nil {
failMessage(err.Error())
}
println(ip)
}
// Returns an int >= min, < max
func randomInt(min, max int) int {
return min + rand.Intn(max-min)
}
// Generate a random string of A-Z chars with len = l
func randomString(len int) string {
bytes := make([]byte, len)
for i := 0; i < len; i++ {
bytes[i] = byte(randomInt(65, 90))
}
return string(bytes)
}
func failMessage(msg string) {
for {
println(msg)
time.Sleep(1 * time.Second)
}
}
-113
View File
@@ -1,113 +0,0 @@
// This is a sensor station that uses a ESP8266 or ESP32 running on the device UART1.
// It creates a UDP connection you can use to get info to/from your computer via the microcontroller.
//
// In other words:
// Your computer <--> UART0 <--> MCU <--> UART1 <--> ESP8266
package main
import (
"machine"
"time"
"tinygo.org/x/drivers/espat"
"tinygo.org/x/drivers/net"
)
var (
// access point info
ssid string
pass string
)
// IP address of the server aka "hub". Replace with your own info.
const serverIP = "0.0.0.0"
// these are the default pins for the Arduino Nano33 IoT.
// change these to connect to a different UART or pins for the ESP8266/ESP32
var (
uart = machine.UART1
tx = machine.PA22
rx = machine.PA23
adaptor *espat.Device
)
func main() {
uart.Configure(machine.UARTConfig{TX: tx, RX: rx})
// Init esp8266/esp32
adaptor = espat.New(uart)
adaptor.Configure()
// first check if connected
if connectToESP() {
println("Connected to wifi adaptor.")
adaptor.Echo(false)
connectToAP()
} else {
println("")
failMessage("Unable to connect to wifi adaptor.")
return
}
// now make TCP connection
ip := net.ParseIP(serverIP)
raddr := &net.TCPAddr{IP: ip, Port: 8080}
laddr := &net.TCPAddr{Port: 8080}
println("Dialing TCP connection...")
conn, err := net.DialTCP("tcp", laddr, raddr)
if err != nil {
failMessage(err.Error())
}
for {
// send data
println("Sending data...")
conn.Write([]byte("hello\r\n"))
time.Sleep(1000 * time.Millisecond)
}
// Right now this code is never reached. Need a way to trigger it...
println("Disconnecting TCP...")
conn.Close()
println("Done.")
}
// connect to ESP8266/ESP32
func connectToESP() bool {
for i := 0; i < 5; i++ {
println("Connecting to wifi adaptor...")
if adaptor.Connected() {
return true
}
time.Sleep(1 * time.Second)
}
return false
}
// connect to access point
func connectToAP() {
println("Connecting to wifi network '" + ssid + "'")
if err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second); err != nil {
failMessage(err.Error())
}
println("Connected.")
ip, err := adaptor.GetClientIP()
if err != nil {
failMessage(err.Error())
}
println(ip)
}
func failMessage(msg string) {
for {
println(msg)
time.Sleep(1 * time.Second)
}
}
+23 -22
View File
@@ -8,15 +8,16 @@ import (
"tinygo.org/x/drivers/examples/ili9341/initdisplay"
"tinygo.org/x/drivers/examples/ili9341/pyportal_boing/graphics"
"tinygo.org/x/drivers/ili9341"
"tinygo.org/x/drivers/pixel"
)
const (
BGCOLOR = 0xAD75
GRIDCOLOR = 0xA815
BGSHADOW = 0x5285
GRIDSHADOW = 0x600C
RED = 0xF800
WHITE = 0xFFFF
BGCOLOR = pixel.RGB565BE(0x75AD)
GRIDCOLOR = pixel.RGB565BE(0x15A8)
BGSHADOW = pixel.RGB565BE(0x8552)
GRIDSHADOW = pixel.RGB565BE(0x0C60)
RED = pixel.RGB565BE(0x00F8)
WHITE = pixel.RGB565BE(0xFFFF)
YBOTTOM = 123 // Ball Y coord at bottom
YBOUNCE = -3.5 // Upward velocity on ball bounce
@@ -25,7 +26,7 @@ const (
)
var (
frameBuffer = [(graphics.BALLHEIGHT + 8) * (graphics.BALLWIDTH + 8) * 2]uint8{}
frameBuffer = pixel.NewImage[pixel.RGB565BE](graphics.BALLWIDTH+8, graphics.BALLHEIGHT+8)
startTime int64
frame int64
@@ -41,7 +42,7 @@ var (
balloldy float32
// Color table for ball rotation effect
palette [16]uint16
palette [16]pixel.RGB565BE
)
var (
@@ -108,6 +109,7 @@ func main() {
width = maxx - minx + 1
height = maxy - miny + 1
buffer := frameBuffer.Rescale(int(width), int(height))
// Ball animation frame # is incremented opposite the ball's X velocity
ballframe -= ballvx * 0.5
@@ -128,7 +130,7 @@ func main() {
}
// Only the changed rectangle is drawn into the 'renderbuf' array...
var c uint16 //, *destPtr;
var c pixel.RGB565BE //, *destPtr;
bx := minx - int16(ballx) // X relative to ball bitmap (can be negative)
by := miny - int16(bally) // Y relative to ball bitmap (can be negative)
bgx := minx // X relative to background bitmap (>= 0)
@@ -149,19 +151,20 @@ func main() {
(by >= 0) && (by < graphics.BALLHEIGHT) { // inside the ball bitmap area?
// Yes, do ball compositing math...
p = graphics.Ball[int(by*(graphics.BALLWIDTH/2))+int(bx1/2)] // Get packed value (2 pixels)
var nibble uint8
if (bx1 & 1) != 0 {
c = uint16(p & 0xF)
nibble = p & 0xF
} else {
c = uint16(p >> 4)
nibble = p >> 4
} // Unpack high or low nybble
if c == 0 { // Outside ball - just draw grid
if nibble == 0 { // Outside ball - just draw grid
if graphics.Background[bgidx]&(0x80>>(bgx1&7)) != 0 {
c = GRIDCOLOR
} else {
c = BGCOLOR
}
} else if c > 1 { // In ball area...
c = palette[c]
} else if nibble > 1 { // In ball area...
c = palette[nibble]
} else { // In shadow area...
if graphics.Background[bgidx]&(0x80>>(bgx1&7)) != 0 {
c = GRIDSHADOW
@@ -176,8 +179,7 @@ func main() {
c = BGCOLOR
}
}
frameBuffer[(y*int(width)+x)*2] = byte(c >> 8)
frameBuffer[(y*int(width)+x)*2+1] = byte(c)
buffer.Set(x, y, c)
bx1++ // Increment bitmap position counters (X axis)
bgx1++
}
@@ -188,7 +190,7 @@ func main() {
bgy++
}
display.DrawRGBBitmap8(minx, miny, frameBuffer[:width*height*2], width, height)
display.DrawBitmap(minx, miny, buffer)
// Show approximate frame rate
frame++
@@ -205,6 +207,7 @@ func DrawBackground() {
w, h := display.Size()
byteWidth := (w + 7) / 8 // Bitmap scanline pad = whole byte
var b uint8
buffer := frameBuffer.Rescale(int(w), 1)
for j := int16(0); j < h; j++ {
for k := int16(0); k < w; k++ {
if k&7 > 0 {
@@ -213,13 +216,11 @@ func DrawBackground() {
b = graphics.Background[j*byteWidth+k/8]
}
if b&0x80 == 0 {
frameBuffer[2*k] = byte(BGCOLOR >> 8)
frameBuffer[2*k+1] = byte(BGCOLOR & 0xFF)
buffer.Set(int(k), 0, BGCOLOR)
} else {
frameBuffer[2*k] = byte(GRIDCOLOR >> 8)
frameBuffer[2*k+1] = byte(GRIDCOLOR & 0xFF)
buffer.Set(int(k), 0, GRIDCOLOR)
}
}
display.DrawRGBBitmap8(0, j, frameBuffer[0:w*2], w, 1)
display.DrawBitmap(0, j, buffer)
}
}
-29
View File
@@ -1,29 +0,0 @@
# AT-CMD implementation of at-lora command set
```
$ tinygo monitor
Connected to /dev/ttyACM0. Press Ctrl-C to exit.
+AT: OK
+VER: 0.0.1 (sx127x v18)
```
# Building
## Simulator
```
tinygo flash -target pico ./examples/lora/atcmd/
```
## PyBadge with LoRa Featherwing
```
tinygo flash -target pybadge -tags featherwing ./examples/lora/atcmd/
```
## LoRa-E5
```
tinygo flash -target lorae5 ./examples/lora/atcmd/
```
-37
View File
@@ -1,37 +0,0 @@
//go:build !featherwing && !gnse && !lorae5 && !nucleowl55jc
package main
// do simulator setup here
func setupLora() (LoraRadio, error) {
return &SimLoraRadio{}, nil
}
type SimLoraRadio struct {
}
func (sr *SimLoraRadio) Reset() {
}
func (sr *SimLoraRadio) Tx(pkt []uint8, timeoutMs uint32) error {
return nil
}
func (sr *SimLoraRadio) Rx(timeoutMs uint32) ([]uint8, error) {
return nil, nil
}
func (sr *SimLoraRadio) SetFrequency(freq uint32) {}
func (sr *SimLoraRadio) SetIqMode(mode uint8) {}
func (sr *SimLoraRadio) SetCodingRate(cr uint8) {}
func (sr *SimLoraRadio) SetBandwidth(bw uint8) {}
func (sr *SimLoraRadio) SetCrc(enable bool) {}
func (sr *SimLoraRadio) SetSpreadingFactor(sf uint8) {}
func firmwareVersion() string {
return "simulator " + currentVersion()
}
func lorarx() ([]byte, error) {
return nil, nil
}
-73
View File
@@ -1,73 +0,0 @@
//go:build gnse || lorae5 || nucleowl55jc
package main
import (
"device/stm32"
"machine"
"runtime/interrupt"
rfswitch "tinygo.org/x/drivers/examples/sx126x/rfswitch"
"tinygo.org/x/drivers/lora"
"tinygo.org/x/drivers/sx126x"
)
const (
FREQ = 868100000
LORA_DEFAULT_RXTIMEOUT_MS = 1000
LORA_DEFAULT_TXTIMEOUT_MS = 5000
)
var (
loraRadio *sx126x.Device
)
// do sx126x setup here
func setupLora() (LoraRadio, error) {
loraRadio = sx126x.New(machine.SPI3)
loraRadio.SetDeviceType(sx126x.DEVICE_TYPE_SX1262)
// Create RF Switch
var radioSwitch rfswitch.CustomSwitch
loraRadio.SetRfSwitch(radioSwitch)
if state := loraRadio.DetectDevice(); !state {
return nil, errRadioNotFound
}
// Add interrupt handler for Radio IRQs
intr := interrupt.New(stm32.IRQ_Radio_IRQ_Busy, radioIntHandler)
intr.Enable()
loraConf := lora.Config{
Freq: FREQ,
Bw: lora.Bandwidth_500_0,
Sf: lora.SpreadingFactor9,
Cr: lora.CodingRate4_7,
HeaderType: lora.HeaderExplicit,
Preamble: 12,
Ldr: lora.LowDataRateOptimizeOff,
Iq: lora.IQStandard,
Crc: lora.CRCOn,
SyncWord: lora.SyncPrivate,
LoraTxPowerDBm: 20,
}
loraRadio.LoraConfig(loraConf)
return loraRadio, nil
}
// radioIntHandler will take care of radio interrupts
func radioIntHandler(intr interrupt.Interrupt) {
loraRadio.HandleInterrupt()
}
func firmwareVersion() string {
return "sx126x"
}
func lorarx() ([]byte, error) {
return loraRadio.Rx(LORA_DEFAULT_RXTIMEOUT_MS)
}
-85
View File
@@ -1,85 +0,0 @@
//go:build featherwing
package main
import (
"strconv"
"machine"
"tinygo.org/x/drivers/lora"
"tinygo.org/x/drivers/sx127x"
)
const (
FREQ = 868100000
LORA_DEFAULT_RXTIMEOUT_MS = 1000
LORA_DEFAULT_TXTIMEOUT_MS = 5000
)
var (
// We assume LoRa Featherwing module is connected to PyBadge:
rstPin = machine.D11
csPin = machine.D10
dio0Pin = machine.D6
dio1Pin = machine.D9
spi = machine.SPI0
loraRadio *sx127x.Device
)
// do sx127x setup here
func setupLora() (LoraRadio, error) {
rstPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
dio0Pin.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
dio1Pin.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
spi.Configure(machine.SPIConfig{Frequency: 500000, Mode: 0})
loraRadio = sx127x.New(spi, csPin, rstPin)
loraRadio.Reset()
if state := loraRadio.DetectDevice(); !state {
return nil, errRadioNotFound
}
// Setup DIO0 interrupt Handling
if err := dio0Pin.SetInterrupt(machine.PinRising, dioIrqHandler); err != nil {
println("could not configure DIO0 pin interrupt:", err.Error())
}
// Setup DIO1 interrupt Handling
if err := dio1Pin.SetInterrupt(machine.PinRising, dioIrqHandler); err != nil {
println("could not configure DIO1 pin interrupt:", err.Error())
}
// Prepare for Lora Operation
loraConf := lora.Config{
Freq: FREQ,
Bw: lora.Bandwidth_500_0,
Sf: lora.SpreadingFactor9,
Cr: lora.CodingRate4_7,
HeaderType: lora.HeaderExplicit,
Preamble: 12,
Iq: lora.IQStandard,
Crc: lora.CRCOn,
SyncWord: lora.SyncPrivate,
LoraTxPowerDBm: 20,
}
loraRadio.LoraConfig(loraConf)
return loraRadio, nil
}
func dioIrqHandler(machine.Pin) {
loraRadio.HandleInterrupt()
}
func firmwareVersion() string {
v := loraRadio.GetVersion()
return "sx127x v" + strconv.Itoa(int(v))
}
func lorarx() ([]byte, error) {
return loraRadio.Rx(LORA_DEFAULT_RXTIMEOUT_MS)
}
-7
View File
@@ -1,7 +0,0 @@
package main
const VERSION = "0.0.1"
func currentVersion() string {
return VERSION
}
+52
View File
@@ -0,0 +1,52 @@
# AT-CMD implementation of at-lora command set
This example implements the AT command set as used by Seeed in the LoRa-E5 series of boards, but in the form of a TinyGo program that provides a serial interface.
See https://files.seeedstudio.com/products/317990687/res/LoRa-E5%20AT%20Command%20Specification_V1.0%20.pdf for more information.
```
$ tinygo monitor
Connected to /dev/ttyACM0. Press Ctrl-C to exit.
+AT: OK
+VER: 0.0.1 (sx127x v18)
```
# Building
Run the following commands from the main `drivers` directory.
## Simulator
Builds/flashes atcmd console application with simulator instead of actual LoRa radio.
```
tinygo flash -target pico ./examples/lora/lorawan/atcmd/
```
## PyBadge with LoRa Featherwing for EU868 region
Builds/flashes atcmd console application on PyBadge using LoRa Featherwing (RFM95/SX1276).
```
tinygo flash -target pybadge -tags featherwing -ldflags="-X main.reg=EU868" ./examples/lora/lorawan/atcmd/
```
## LoRa-E5 for US915 region
Builds/flashes atcmd console application on Lora-E5 using onboard SX126x.
```
tinygo flash -target lorae5 -ldflags="-X main.reg=US915" ./examples/lora/lorawan/atcmd/
```
## Joining a Public Lorawan Network
```
AT+ID=DevEui,0101010101010101
AT+ID=AppEui,0123012301230213
AT+KEY=APPKEY,AEAEAEAEAEAEAEAAEAEAEAEAEAEAAEAE
AT+LW=NET,ON
AT+JOIN
```
AT+LW=NET,(ON|OFF) command changes Lora Sync Word to connect on public network(ON) or private networks(OFF)
@@ -3,6 +3,9 @@ package main
import (
"encoding/hex"
"strings"
"tinygo.org/x/drivers/examples/lora/lorawan/common"
"tinygo.org/x/drivers/lora/lorawan"
)
// Use to test if connection to module is OK.
@@ -12,7 +15,7 @@ func quicktest() {
// Check firmware version.
func version() {
writeCommandOutput("VER", currentVersion()+" ("+firmwareVersion()+")")
writeCommandOutput("VER", common.CurrentVersion()+" ("+common.FirmwareVersion()+")")
}
// Use to check the ID of the LoRaWAN module, or change the ID.
@@ -23,13 +26,42 @@ func id(args string) error {
param, val, hasComma := strings.Cut(args, ",")
if hasComma {
// set
data := strings.Trim(val, "\"'")
// convert data from hex formatted string
data = strings.ReplaceAll(data, " ", "")
hexdata, err := hex.DecodeString(data)
if err != nil {
writeCommandOutput(cmd, err.Error())
return err
}
switch param {
case "DevAddr":
writeCommandOutput(cmd, "DevAddr, "+val)
if err := session.SetDevAddr(hexdata); err != nil {
writeCommandOutput(cmd, err.Error())
return err
}
writeCommandOutput(cmd, "DevAddr, "+session.GetDevAddr())
case "DevEui":
writeCommandOutput(cmd, "DevEui, "+val)
if err := otaa.SetDevEUI(hexdata); err != nil {
writeCommandOutput(cmd, err.Error())
return err
}
writeCommandOutput(cmd, "DevEui, "+otaa.GetDevEUI())
case "AppEui":
writeCommandOutput(cmd, "AppEui, "+val)
if err := otaa.SetAppEUI(hexdata); err != nil {
writeCommandOutput(cmd, err.Error())
return err
}
writeCommandOutput(cmd, "AppEui, "+otaa.GetAppEUI())
default:
return errInvalidCommand
}
@@ -40,15 +72,15 @@ func id(args string) error {
// get
switch param {
case "DevAddr":
writeCommandOutput(cmd, "DevAddr, xx:xx:xx:xx")
writeCommandOutput(cmd, "DevAddr, "+session.GetDevAddr())
case "DevEui":
writeCommandOutput(cmd, "DevEui, xx:xx:xx:xx:xx:xx:xx:xx")
writeCommandOutput(cmd, "DevEui, "+otaa.GetDevEUI())
case "AppEui":
writeCommandOutput(cmd, "AppEui, xx:xx:xx:xx:xx:xx:xx:xx")
writeCommandOutput(cmd, "AppEui, "+otaa.GetAppEUI())
default:
writeCommandOutput(cmd, "DevAddr, xx:xx:xx:xx")
writeCommandOutput(cmd, "DevEui, xx:xx:xx:xx:xx:xx:xx:xx")
writeCommandOutput(cmd, "AppEui, xx:xx:xx:xx:xx:xx:xx:xx")
writeCommandOutput(cmd, "DevAddr, "+session.GetDevAddr())
writeCommandOutput(cmd, "DevEui, "+otaa.GetDevEUI())
writeCommandOutput(cmd, "AppEui, "+otaa.GetAppEUI())
}
return nil
@@ -208,9 +240,38 @@ func rxwin1(setting string) error {
func key(setting string) error {
cmd := "KEY"
writeCommandOutput(cmd, setting)
return nil
// look for comma in args
param, val, hasComma := strings.Cut(setting, ",")
if hasComma {
// set
data := strings.Trim(val, "\"'")
// convert data from hex formatted string
data = strings.ReplaceAll(data, " ", "")
hexdata, err := hex.DecodeString(data)
if err != nil {
writeCommandOutput(cmd, err.Error())
return err
}
switch param {
case "APPKEY":
if err := otaa.SetAppKey(hexdata); err != nil {
writeCommandOutput(cmd, err.Error())
return err
}
writeCommandOutput(cmd, "APPKEY, "+otaa.GetAppKey())
default:
return errInvalidCommand
}
}
// cannot get keys
return errInvalidCommand
}
func fdefault(setting string) error {
@@ -228,9 +289,23 @@ func mode(setting string) error {
}
func join(setting string) error {
// TODO: check that DevEUI, AppEUI, and AppKey have values
cmd := "JOIN"
writeCommandOutput(cmd, "Starting")
if err := lorawan.Join(otaa, session); err != nil {
writeCommandOutput(cmd, err.Error())
return err
}
writeCommandOutput(cmd, "Network joined")
writeCommandOutput(cmd, "DevEui, "+otaa.GetDevEUI())
writeCommandOutput(cmd, "AppEui, "+otaa.GetAppEUI())
writeCommandOutput(cmd, "DevAddr, "+session.GetDevAddr())
writeCommandOutput(cmd, "NetID, "+otaa.GetNetID())
writeCommandOutput(cmd, "NwkSKey, "+session.GetNwkSKey())
writeCommandOutput(cmd, "AppSKey, "+session.GetAppSKey())
writeCommandOutput(cmd, "Done")
return nil
}
@@ -262,6 +337,17 @@ func delay(setting string) error {
func lw(setting string) error {
cmd := "LW"
param, val, hasComma := strings.Cut(setting, ",")
if hasComma {
if param == "NET" {
if val == "ON" {
lorawan.SetPublicNetwork(true)
} else {
lorawan.SetPublicNetwork(false)
}
}
}
writeCommandOutput(cmd, setting)
return nil
@@ -376,7 +462,7 @@ func sendhex(data string) error {
func recv(setting string) error {
cmd := "RECV"
data, err := lorarx()
data, err := common.Lorarx()
if err != nil {
writeCommandOutput(cmd, "ERROR "+err.Error())
return err
@@ -389,7 +475,7 @@ func recv(setting string) error {
func recvhex(setting string) error {
cmd := "RECVHEX"
data, err := lorarx()
data, err := common.Lorarx()
if err != nil {
writeCommandOutput(cmd, "ERROR "+err.Error())
return err
@@ -11,6 +11,11 @@ package main
import (
"machine"
"time"
"tinygo.org/x/drivers/examples/lora/lorawan/common"
"tinygo.org/x/drivers/lora"
"tinygo.org/x/drivers/lora/lorawan"
"tinygo.org/x/drivers/lora/lorawan/region"
)
// change these to test a different UART or pins if available
@@ -20,19 +25,39 @@ var (
rx = machine.UART_RX_PIN
input = make([]byte, 0, 64)
radio LoraRadio
radio lora.Radio
session *lorawan.Session
otaa *lorawan.Otaa
defaultTimeout uint32 = 1000
)
var reg string
func main() {
uart.Configure(machine.UARTConfig{TX: tx, RX: rx})
var err error
radio, err = setupLora()
radio, err = common.SetupLora()
if err != nil {
fail(err.Error())
}
session = &lorawan.Session{}
otaa = &lorawan.Otaa{}
lorawan.UseRadio(radio)
switch reg {
case "AU915":
lorawan.UseRegionSettings(region.AU915())
case "EU868":
lorawan.UseRegionSettings(region.EU868())
case "US915":
lorawan.UseRegionSettings(region.US915())
default:
lorawan.UseRegionSettings(region.EU868())
}
for {
if uart.Buffered() > 0 {
data, _ := uart.ReadByte()
@@ -0,0 +1,44 @@
# Simple Lorawan example
This demo code will connect Lorawan network and send sample uplink message
You may change your Lorawan keys (AppEUI, DevEUI, AppKEY) in key-default.go
```
$ tinygo monitor
Connected to /dev/ttyACM0. Press Ctrl-C to exit.
Lorawan Simple Demo
Start Lorawan Join sequence
loraConnect: Connected !
```
# Building
## Simulator
```
tinygo flash -target pico ./examples/lora/lorawan/basic-demo
```
## PyBadge with LoRa Featherwing for EU868 region
```
tinygo flash -target pybadge -tags featherwing -ldflags="-X main.reg=EU868" ./examples/lora/lorawan/basic-demo
```
## LoRa-E5 for US915 region
```
tinygo flash -target lorae5 -ldflags="-X main.reg=US915" ./examples/lora/lorawan/basic-demo
```
## Enable debugging
You can also enable some debug logs with ldflags :
```
$ tinygo build -ldflags="-X 'main.debug=true'" -target=lorae5
```
@@ -0,0 +1,17 @@
//go:build !customkeys
package main
import (
"tinygo.org/x/drivers/lora/lorawan"
)
// These are sample keys, so the example builds
// Either change here, or create a new go file and use customkeys build tag
func setLorawanKeys() {
otaa.SetAppEUI([]uint8{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
otaa.SetDevEUI([]uint8{0xB3, 0xD5, 0x41, 0x00, 0x0A, 0xF1, 0xA4, 0x45})
otaa.SetAppKey([]uint8{0x12, 0x22, 0xA3, 0xFF, 0x0C, 0x7B, 0x76, 0x7B, 0x8F, 0xD3, 0x12, 0x4F, 0xCE, 0x7A, 0x32, 0x16})
lorawan.SetPublicNetwork(true)
}
+120
View File
@@ -0,0 +1,120 @@
// Simple code for connecting to Lorawan network and uploading sample payload
package main
import (
"errors"
"strconv"
"time"
"tinygo.org/x/drivers/examples/lora/lorawan/common"
"tinygo.org/x/drivers/lora"
"tinygo.org/x/drivers/lora/lorawan"
"tinygo.org/x/drivers/lora/lorawan/region"
)
var (
reg string
debug string
)
const (
LORAWAN_JOIN_TIMEOUT_SEC = 180
LORAWAN_RECONNECT_DELAY_SEC = 15
LORAWAN_UPLINK_DELAY_SEC = 60
)
var (
radio lora.Radio
session *lorawan.Session
otaa *lorawan.Otaa
)
func loraConnect() error {
start := time.Now()
var err error
for time.Since(start) < LORAWAN_JOIN_TIMEOUT_SEC*time.Second {
println("Trying to join network")
err = lorawan.Join(otaa, session)
if err == nil {
println("Connected to network !")
return nil
}
println("Join error:", err, "retrying in", LORAWAN_RECONNECT_DELAY_SEC, "sec")
time.Sleep(time.Second * LORAWAN_RECONNECT_DELAY_SEC)
}
err = errors.New("Unable to join Lorawan network")
println(err.Error())
return err
}
func failMessage(err error) {
println("FATAL:", err)
for {
}
}
func main() {
println("*** Lorawan basic join and uplink demo ***")
// Board specific Lorawan initialization
var err error
radio, err = common.SetupLora()
if err != nil {
failMessage(err)
}
// Required for LoraWan operations
session = &lorawan.Session{}
otaa = &lorawan.Otaa{}
// Connect the lorawan with the Lora Radio device.
lorawan.UseRadio(radio)
switch reg {
case "AU915":
lorawan.UseRegionSettings(region.AU915())
case "EU868":
lorawan.UseRegionSettings(region.EU868())
case "US915":
lorawan.UseRegionSettings(region.US915())
default:
lorawan.UseRegionSettings(region.EU868())
}
// Configure AppEUI, DevEUI, APPKey, and public/private Lorawan Network
setLorawanKeys()
if debug != "" {
println("main: Network joined")
println("main: DevEui, " + otaa.GetDevEUI())
println("main: AppEui, " + otaa.GetAppEUI())
println("main: DevAddr, " + otaa.GetAppKey())
}
// Try to connect Lorawan network
if err := loraConnect(); err != nil {
failMessage(err)
}
if debug != "" {
println("main: NetID, " + otaa.GetNetID())
println("main: NwkSKey, " + session.GetNwkSKey())
println("main: AppSKey, " + session.GetAppSKey())
println("main: Done")
}
// Try to periodicaly send an uplink sample message
upCount := 1
for {
payload := "Hello TinyGo #" + strconv.Itoa(upCount)
if err := lorawan.SendUplink([]byte(payload), session); err != nil {
println("Uplink error:", err)
} else {
println("Uplink success, msg=", payload)
}
println("Sleeping for", LORAWAN_UPLINK_DELAY_SEC, "sec")
time.Sleep(time.Second * LORAWAN_UPLINK_DELAY_SEC)
upCount++
}
}
@@ -0,0 +1,14 @@
//go:build featherwing
package common
import "machine"
var (
// We assume LoRa Featherwing module with sx127x is connected to PyBadge
rstPin = machine.D11
csPin = machine.D10
dio0Pin = machine.D6
dio1Pin = machine.D9
spi = machine.SPI0
)
+13
View File
@@ -0,0 +1,13 @@
//go:build lgt92
package common
import "machine"
var (
rstPin = machine.PB0
csPin = machine.PA15
dio0Pin = machine.PC13
dio1Pin = machine.PB10
spi = machine.SPI0
)
+10
View File
@@ -0,0 +1,10 @@
package common
import (
"errors"
)
var (
errRadioNotFound = errors.New("radio not found")
errRxTimeout = errors.New("radio RX timeout")
)
+45
View File
@@ -0,0 +1,45 @@
//go:build !featherwing && !lgt92 && !stm32wlx && !sx126x
package common
import "tinygo.org/x/drivers/lora"
// do simulator setup here
func SetupLora() (lora.Radio, error) {
return &SimLoraRadio{}, nil
}
type SimLoraRadio struct {
}
func (sr *SimLoraRadio) Reset() {
}
func (sr *SimLoraRadio) Tx(pkt []uint8, timeoutMs uint32) error {
return nil
}
func (sr *SimLoraRadio) Rx(timeoutMs uint32) ([]uint8, error) {
return nil, nil
}
func (sr *SimLoraRadio) SetFrequency(freq uint32) {}
func (sr *SimLoraRadio) SetIqMode(mode uint8) {}
func (sr *SimLoraRadio) SetCodingRate(cr uint8) {}
func (sr *SimLoraRadio) SetBandwidth(bw uint8) {}
func (sr *SimLoraRadio) SetCrc(enable bool) {}
func (sr *SimLoraRadio) SetSpreadingFactor(sf uint8) {}
func (sr *SimLoraRadio) SetHeaderType(headerType uint8) {}
func (sr *SimLoraRadio) SetPreambleLength(pLen uint16) {}
func (sr *SimLoraRadio) SetPublicNetwork(enabled bool) {}
func (sr *SimLoraRadio) SetSyncWord(syncWord uint16) {}
func (sr *SimLoraRadio) SetTxPower(txPower int8) {}
func (sr *SimLoraRadio) LoraConfig(cnf lora.Config) {}
func FirmwareVersion() string {
return "simulator " + CurrentVersion()
}
func Lorarx() ([]byte, error) {
return nil, nil
}
+49
View File
@@ -0,0 +1,49 @@
//go:build stm32wlx
package common
import (
"machine"
"tinygo.org/x/drivers/lora"
"tinygo.org/x/drivers/sx126x"
)
const (
FREQ = 868100000
LORA_DEFAULT_RXTIMEOUT_MS = 1000
LORA_DEFAULT_TXTIMEOUT_MS = 5000
)
var (
loraRadio *sx126x.Device
)
var spi = machine.SPI3
func newRadioControl() sx126x.RadioController {
return sx126x.NewRadioControl()
}
// do sx126x setup here
func SetupLora() (lora.Radio, error) {
loraRadio = sx126x.New(spi)
loraRadio.SetDeviceType(sx126x.DEVICE_TYPE_SX1262)
// Create radio controller for target
loraRadio.SetRadioController(newRadioControl())
if state := loraRadio.DetectDevice(); !state {
return nil, errRadioNotFound
}
return loraRadio, nil
}
func FirmwareVersion() string {
return "sx126x"
}
func Lorarx() ([]byte, error) {
return loraRadio.Rx(LORA_DEFAULT_RXTIMEOUT_MS)
}
+53
View File
@@ -0,0 +1,53 @@
//go:build !stm32wlx && sx126x
package common
import (
"machine"
"tinygo.org/x/drivers/lora"
"tinygo.org/x/drivers/sx126x"
)
const (
FREQ = 868100000
LORA_DEFAULT_RXTIMEOUT_MS = 1000
LORA_DEFAULT_TXTIMEOUT_MS = 5000
)
var (
loraRadio *sx126x.Device
)
var (
spi = machine.SPI0
nssPin, busyPin, dio1Pin = machine.GP17, machine.GP10, machine.GP11
rxPin, txLowPin, txHighPin = machine.GP13, machine.GP12, machine.GP12
)
func newRadioControl() sx126x.RadioController {
return sx126x.NewRadioControl(nssPin, busyPin, dio1Pin, rxPin, txLowPin, txHighPin)
}
// do sx126x setup here
func SetupLora() (lora.Radio, error) {
loraRadio = sx126x.New(spi)
loraRadio.SetDeviceType(sx126x.DEVICE_TYPE_SX1262)
// Create radio controller for target
loraRadio.SetRadioController(newRadioControl())
if state := loraRadio.DetectDevice(); !state {
return nil, errRadioNotFound
}
return loraRadio, nil
}
func FirmwareVersion() string {
return "sx126x"
}
func Lorarx() ([]byte, error) {
return loraRadio.Rx(LORA_DEFAULT_RXTIMEOUT_MS)
}
+47
View File
@@ -0,0 +1,47 @@
//go:build featherwing || lgt92
package common
import (
"strconv"
"machine"
"tinygo.org/x/drivers/lora"
"tinygo.org/x/drivers/sx127x"
)
const (
FREQ = 868100000
LORA_DEFAULT_RXTIMEOUT_MS = 1000
LORA_DEFAULT_TXTIMEOUT_MS = 5000
)
var (
loraRadio *sx127x.Device
)
// do sx127x setup here
func SetupLora() (lora.Radio, error) {
rstPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
spi.Configure(machine.SPIConfig{Frequency: 500000, Mode: 0})
loraRadio = sx127x.New(spi, rstPin)
loraRadio.SetRadioController(sx127x.NewRadioControl(csPin, dio0Pin, dio1Pin))
loraRadio.Reset()
if state := loraRadio.DetectDevice(); !state {
return nil, errRadioNotFound
}
return loraRadio, nil
}
func FirmwareVersion() string {
v := loraRadio.GetVersion()
return "sx127x v" + strconv.Itoa(int(v))
}
func Lorarx() ([]byte, error) {
return loraRadio.Rx(LORA_DEFAULT_RXTIMEOUT_MS)
}
+7
View File
@@ -0,0 +1,7 @@
package common
const VERSION = "0.0.1"
func CurrentVersion() string {
return VERSION
}
+22
View File
@@ -0,0 +1,22 @@
// Connects to an MPU6886 I2C accelerometer/gyroscope.
package main
import (
"machine"
"time"
"tinygo.org/x/drivers/mpu6886"
)
func main() {
machine.I2C0.Configure(machine.I2CConfig{})
accel := mpu6886.New(machine.I2C0)
accel.Configure(mpu6886.Config{})
for {
x, y, z, _ := accel.ReadAcceleration()
println(x, y, z)
time.Sleep(time.Millisecond * 100)
}
}
+22
View File
@@ -0,0 +1,22 @@
// Connects to an MPU9150 I2C accelerometer/gyroscope.
package main
import (
"machine"
"time"
"tinygo.org/x/drivers/mpu9150"
)
func main() {
machine.I2C0.Configure(machine.I2CConfig{})
accel := mpu9150.New(machine.I2C0)
accel.Configure()
for {
x, y, z := accel.ReadAcceleration(mpu9150.ACCEL_XOUT_H)
println(x, y, z)
time.Sleep(time.Millisecond * 100)
}
}
+41
View File
@@ -0,0 +1,41 @@
package main
import (
"machine"
"time"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/ndir"
)
var (
ndirBus = machine.I2C0
)
func main() {
err := ndirBus.Configure(machine.I2CConfig{
Frequency: 100_000,
})
if err != nil {
panic("i2c config fail:" + err.Error())
}
// Set the address based on how the resistors are soldered.
// True means the left and middle pads are joined.
ndirAddr := ndir.Addr(true, false)
dev := ndir.NewDevI2C(ndirBus, ndirAddr)
err = dev.Init()
if err != nil {
panic("ndir init fail:" + err.Error())
}
// Datasheet tells us to wait 12 seconds before reading from the sensor.
time.Sleep(12 * time.Second)
for {
time.Sleep(time.Second)
err := dev.Update(drivers.AllMeasurements)
if err != nil {
println(err.Error())
continue
}
println("PPM:", dev.PPMCO2())
}
}
+88
View File
@@ -0,0 +1,88 @@
// This example gets an URL using http.Get(). URL scheme can be http or https.
//
// Note: It may be necessary to increase the stack size when using "net/http".
// Use the -stack-size=4KB command line option.
//
// Some targets (Arduino Nano33 IoT) don't have enough SRAM to run http.Get().
// Use the following for those targets:
//
// examples/net/webclient (for HTTP)
// examples/net/tlsclient (for HTTPS)
//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal
package main
import (
"fmt"
"io"
"log"
"machine"
"net/http"
"net/url"
"strings"
"time"
"tinygo.org/x/drivers/netlink"
"tinygo.org/x/drivers/netlink/probe"
)
var (
ssid string
pass string
)
func main() {
waitSerial()
link, _ := probe.Probe()
err := link.NetConnect(&netlink.ConnectParams{
Ssid: ssid,
Passphrase: pass,
})
if err != nil {
log.Fatal(err)
}
name := "John Doe"
occupation := "gardener"
params := "name=" + url.QueryEscape(name) + "&" +
"occupation=" + url.QueryEscape(occupation)
path := fmt.Sprintf("https://httpbin.org/get?%s", params)
cnt := 0
for {
fmt.Printf("Getting %s\r\n\r\n", path)
resp, err := http.Get(path)
if err != nil {
fmt.Printf("%s\r\n", err.Error())
time.Sleep(10 * time.Second)
continue
}
fmt.Printf("%s %s\r\n", resp.Proto, resp.Status)
for k, v := range resp.Header {
fmt.Printf("%s: %s\r\n", k, strings.Join(v, " "))
}
fmt.Printf("\r\n")
body, err := io.ReadAll(resp.Body)
println(string(body))
resp.Body.Close()
cnt++
fmt.Printf("-------- %d --------\r\n", cnt)
time.Sleep(10 * time.Second)
}
}
// Wait for user to open serial console
func waitSerial() {
for !machine.Serial.DTR() {
time.Sleep(100 * time.Millisecond)
}
}
+68
View File
@@ -0,0 +1,68 @@
// This example gets an URL using http.Head(). URL scheme can be http or https.
//
// Note: It may be necessary to increase the stack size when using "net/http".
// Use the -stack-size=4KB command line option.
//
// Some targets (Arduino Nano33 IoT) don't have enough SRAM to run http.Head().
// Use the following for those targets:
//
// examples/net/webclient (for HTTP)
// examples/net/tlsclient (for HTTPS)
//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal
package main
import (
"bytes"
"fmt"
"log"
"machine"
"net/http"
"time"
"tinygo.org/x/drivers/netlink"
"tinygo.org/x/drivers/netlink/probe"
)
var (
ssid string
pass string
url string = "https://httpbin.org"
)
func main() {
waitSerial()
link, _ := probe.Probe()
err := link.NetConnect(&netlink.ConnectParams{
Ssid: ssid,
Passphrase: pass,
})
if err != nil {
log.Fatal(err)
}
resp, err := http.Head(url)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
var buf bytes.Buffer
if err := resp.Write(&buf); err != nil {
log.Fatal(err)
}
fmt.Println(string(buf.Bytes()))
link.NetDisconnect()
}
// Wait for user to open serial console
func waitSerial() {
for !machine.Serial.DTR() {
time.Sleep(100 * time.Millisecond)
}
}
+72
View File
@@ -0,0 +1,72 @@
// This example posts an URL using http.Post(). URL scheme can be http or https.
//
// Note: It may be necessary to increase the stack size when using "net/http".
// Use the -stack-size=4KB command line option.
//
// Some targets (Arduino Nano33 IoT) don't have enough SRAM to run http.Post().
// Use the following for those targets:
//
// examples/net/webclient (for HTTP)
// examples/net/tlsclient (for HTTPS)
//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal
package main
import (
"bytes"
"fmt"
"io"
"log"
"machine"
"net/http"
"time"
"tinygo.org/x/drivers/netlink"
"tinygo.org/x/drivers/netlink/probe"
)
var (
ssid string
pass string
)
func main() {
waitSerial()
link, _ := probe.Probe()
err := link.NetConnect(&netlink.ConnectParams{
Ssid: ssid,
Passphrase: pass,
})
if err != nil {
log.Fatal(err)
}
path := "https://httpbin.org/post"
data := []byte("{\"name\":\"John Doe\",\"occupation\":\"gardener\"}")
resp, err := http.Post(path, "application/json", bytes.NewBuffer(data))
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(body))
link.NetDisconnect()
}
// Wait for user to open serial console
func waitSerial() {
for !machine.Serial.DTR() {
time.Sleep(100 * time.Millisecond)
}
}
+73
View File
@@ -0,0 +1,73 @@
// This example posts an URL using http.PostForm(). URL scheme can be http or https.
//
// Note: It may be necessary to increase the stack size when using "net/http".
// Use the -stack-size=4KB command line option.
//
// Some targets (Arduino Nano33 IoT) don't have enough SRAM to run
// http.PostForm(). Use the following for those targets:
//
// examples/net/webclient (for HTTP)
// examples/net/tlsclient (for HTTPS)
//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal
package main
import (
"fmt"
"io"
"log"
"machine"
"net/http"
"net/url"
"time"
"tinygo.org/x/drivers/netlink"
"tinygo.org/x/drivers/netlink/probe"
)
var (
ssid string
pass string
)
func main() {
waitSerial()
link, _ := probe.Probe()
err := link.NetConnect(&netlink.ConnectParams{
Ssid: ssid,
Passphrase: pass,
})
if err != nil {
log.Fatal(err)
}
path := "https://httpbin.org/post"
data := url.Values{
"name": {"John Doe"},
"occupation": {"gardener"},
}
resp, err := http.PostForm(path, data)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(body))
}
// Wait for user to open serial console
func waitSerial() {
for !machine.Serial.DTR() {
time.Sleep(100 * time.Millisecond)
}
}
+146
View File
@@ -0,0 +1,146 @@
// This example is an MQTT client built with the natiu-mqtt package. It sends
// machine.CPUFrequency() readings to the broker every second for 10 seconds.
//
// Note: It may be necessary to increase the stack size when using
// paho.mqtt.golang. Use the -stack-size=4KB command line option.
//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal || challenger_rp2040
package main
import (
"context"
"errors"
"fmt"
"io"
"log"
"machine"
"math/rand"
"net"
"time"
mqtt "github.com/soypat/natiu-mqtt"
"tinygo.org/x/drivers/netlink"
"tinygo.org/x/drivers/netlink/probe"
)
var (
ssid string
pass string
broker string = "test.mosquitto.org:1883"
topic string = "cpu/freq"
)
func main() {
waitSerial()
link, _ := probe.Probe()
err := link.NetConnect(&netlink.ConnectParams{
Ssid: ssid,
Passphrase: pass,
})
if err != nil {
log.Fatal(err)
}
clientId := "tinygo-client-" + randomString(10)
fmt.Printf("ClientId: %s\n", clientId)
// Get a transport for MQTT packets
fmt.Printf("Connecting to MQTT broker at %s\n", broker)
conn, err := net.Dial("tcp", broker)
if err != nil {
log.Fatal(err)
}
defer conn.Close()
// Create new client
client := mqtt.NewClient(mqtt.ClientConfig{
Decoder: mqtt.DecoderNoAlloc{make([]byte, 1500)},
OnPub: func(_ mqtt.Header, _ mqtt.VariablesPublish, r io.Reader) error {
message, _ := io.ReadAll(r)
fmt.Printf("Message %s received on topic %s\n", string(message), topic)
return nil
},
})
// Connect client
var varconn mqtt.VariablesConnect
varconn.SetDefaultMQTT([]byte(clientId))
ctx, _ := context.WithTimeout(context.Background(), 10*time.Second)
err = client.Connect(ctx, conn, &varconn)
if err != nil {
log.Fatal("failed to connect: ", err)
}
// Subscribe to topic
ctx, _ = context.WithTimeout(context.Background(), 10*time.Second)
err = client.Subscribe(ctx, mqtt.VariablesSubscribe{
PacketIdentifier: 23,
TopicFilters: []mqtt.SubscribeRequest{
{TopicFilter: []byte(topic), QoS: mqtt.QoS0},
},
})
if err != nil {
log.Fatal("failed to subscribe to", topic, err)
}
fmt.Printf("Subscribed to topic %s\n", topic)
// Publish on topic
pubFlags, _ := mqtt.NewPublishFlags(mqtt.QoS0, false, false)
pubVar := mqtt.VariablesPublish{
TopicName: []byte(topic),
}
for i := 0; i < 10; i++ {
if !client.IsConnected() {
log.Fatal("client disconnected: ", client.Err())
}
freq := float32(machine.CPUFrequency()) / 1000000
payload := fmt.Sprintf("%.02fMhz", freq)
pubVar.PacketIdentifier++
err = client.PublishPayload(pubFlags, pubVar, []byte(payload))
if err != nil {
log.Fatal("error transmitting message: ", err)
}
time.Sleep(time.Second)
conn.SetReadDeadline(time.Now().Add(10 * time.Second))
err = client.HandleNext()
if err != nil {
log.Fatal("handle next: ", err)
}
}
client.Disconnect(errors.New("disconnected gracefully"))
for {
select {}
}
}
// Returns an int >= min, < max
func randomInt(min, max int) int {
return min + rand.Intn(max-min)
}
// Generate a random string of A-Z chars with len = l
func randomString(len int) string {
bytes := make([]byte, len)
for i := 0; i < len; i++ {
bytes[i] = byte(randomInt(65, 90))
}
return string(bytes)
}
// Wait for user to open serial console
func waitSerial() {
for !machine.Serial.DTR() {
time.Sleep(100 * time.Millisecond)
}
}
+114
View File
@@ -0,0 +1,114 @@
// This example is an MQTT client built with the paho-mqtt package. It sends
// machine.CPUFrequency() readings to the broker every second for 10 seconds.
//
// Note: It may be necessary to increase the stack size when using
// paho.mqtt.golang. Use the -stack-size=4KB command line option.
//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal || challenger_rp2040
package main
import (
"fmt"
"log"
"machine"
"math/rand"
"time"
mqtt "github.com/eclipse/paho.mqtt.golang"
"tinygo.org/x/drivers/netlink"
"tinygo.org/x/drivers/netlink/probe"
)
var (
ssid string
pass string
broker string = "tcp://test.mosquitto.org:1883"
)
var messagePubHandler mqtt.MessageHandler = func(client mqtt.Client, msg mqtt.Message) {
fmt.Printf("Message %s received on topic %s\n", msg.Payload(), msg.Topic())
}
var connectHandler mqtt.OnConnectHandler = func(client mqtt.Client) {
fmt.Println("Connected")
}
var connectionLostHandler mqtt.ConnectionLostHandler = func(client mqtt.Client, err error) {
fmt.Printf("Connection Lost: %s\n", err.Error())
}
func main() {
waitSerial()
link, _ := probe.Probe()
err := link.NetConnect(&netlink.ConnectParams{
Ssid: ssid,
Passphrase: pass,
})
if err != nil {
log.Fatal(err)
}
clientId := "tinygo-client-" + randomString(10)
fmt.Printf("ClientId: %s\n", clientId)
options := mqtt.NewClientOptions()
options.AddBroker(broker)
options.SetClientID(clientId)
options.SetDefaultPublishHandler(messagePubHandler)
options.OnConnect = connectHandler
options.OnConnectionLost = connectionLostHandler
fmt.Printf("Connecting to MQTT broker at %s\n", broker)
client := mqtt.NewClient(options)
token := client.Connect()
if token.Wait() && token.Error() != nil {
panic(token.Error())
}
topic := "cpu/freq"
token = client.Subscribe(topic, 1, nil)
if token.Wait() && token.Error() != nil {
panic(token.Error())
}
fmt.Printf("Subscribed to topic %s\n", topic)
for i := 0; i < 10; i++ {
freq := float32(machine.CPUFrequency()) / 1000000
payload := fmt.Sprintf("%.02fMhz", freq)
token = client.Publish(topic, 0, false, payload)
if token.Wait() && token.Error() != nil {
panic(token.Error())
}
time.Sleep(time.Second)
}
client.Disconnect(100)
for {
select {}
}
}
// Returns an int >= min, < max
func randomInt(min, max int) int {
return min + rand.Intn(max-min)
}
// Generate a random string of A-Z chars with len = l
func randomString(len int) string {
bytes := make([]byte, len)
for i := 0; i < len; i++ {
bytes[i] = byte(randomInt(65, 90))
}
return string(bytes)
}
// Wait for user to open serial console
func waitSerial() {
for !machine.Serial.DTR() {
time.Sleep(100 * time.Millisecond)
}
}
+115
View File
@@ -0,0 +1,115 @@
// This is an example of an NTP client.
//
// It creates a UDP connection to request the current time and parse the
// response from a NTP server. The system time is set to NTP time.
//go:build pyportal || arduino_nano33 || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal || challenger_rp2040
package main
import (
"fmt"
"io"
"log"
"machine"
"net"
"runtime"
"time"
"tinygo.org/x/drivers/netlink"
"tinygo.org/x/drivers/netlink/probe"
)
var (
ssid string
pass string
// IP address of the server aka "hub". Replace with your own info.
ntpHost string = "0.pool.ntp.org:123"
)
const NTP_PACKET_SIZE = 48
var response = make([]byte, NTP_PACKET_SIZE)
func main() {
waitSerial()
link, _ := probe.Probe()
err := link.NetConnect(&netlink.ConnectParams{
Ssid: ssid,
Passphrase: pass,
})
if err != nil {
log.Fatal(err)
}
conn, err := net.Dial("udp", ntpHost)
if err != nil {
log.Fatal(err)
}
println("Requesting NTP time...")
t, err := getCurrentTime(conn)
if err != nil {
log.Fatal(fmt.Sprintf("Error getting current time: %v", err))
} else {
message("NTP time: %v", t)
}
conn.Close()
link.NetDisconnect()
runtime.AdjustTimeOffset(-1 * int64(time.Since(t)))
for {
message("Current time: %v", time.Now())
time.Sleep(time.Minute)
}
}
// Wait for user to open serial console
func waitSerial() {
for !machine.Serial.DTR() {
time.Sleep(100 * time.Millisecond)
}
}
func getCurrentTime(conn net.Conn) (time.Time, error) {
if err := sendNTPpacket(conn); err != nil {
return time.Time{}, err
}
n, err := conn.Read(response)
if err != nil && err != io.EOF {
return time.Time{}, err
}
if n != NTP_PACKET_SIZE {
return time.Time{}, fmt.Errorf("expected NTP packet size of %d: %d", NTP_PACKET_SIZE, n)
}
return parseNTPpacket(response), nil
}
func sendNTPpacket(conn net.Conn) error {
var request = [48]byte{
0xe3,
}
_, err := conn.Write(request[:])
return err
}
func parseNTPpacket(r []byte) time.Time {
// the timestamp starts at byte 40 of the received packet and is four bytes,
// this is NTP time (seconds since Jan 1 1900):
t := uint32(r[40])<<24 | uint32(r[41])<<16 | uint32(r[42])<<8 | uint32(r[43])
const seventyYears = 2208988800
return time.Unix(int64(t-seventyYears), 0)
}
func message(format string, args ...interface{}) {
println(fmt.Sprintf(format, args...), "\r")
}
+108
View File
@@ -0,0 +1,108 @@
// This example opens a TCP connection and sends some data using netdev sockets.
//
// You can open a server to accept connections from this program using:
//
// nc -lk 8080
//go:build pyportal || arduino_nano33 || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal || challenger_rp2040
package main
import (
"bytes"
"fmt"
"log"
"machine"
"net/netip"
"time"
"tinygo.org/x/drivers/netdev"
"tinygo.org/x/drivers/netlink"
"tinygo.org/x/drivers/netlink/probe"
)
var (
ssid string
pass string
addr string = "10.0.0.100:8080"
)
var buf = &bytes.Buffer{}
var link netlink.Netlinker
var dev netdev.Netdever
func main() {
waitSerial()
link, dev = probe.Probe()
err := link.NetConnect(&netlink.ConnectParams{
Ssid: ssid,
Passphrase: pass,
})
if err != nil {
log.Fatal(err)
}
for {
sendBatch()
time.Sleep(500 * time.Millisecond)
}
}
func sendBatch() {
addrPort, _ := netip.ParseAddrPort(addr)
// make TCP connection
message("---------------\r\nDialing TCP connection")
fd, _ := dev.Socket(netdev.AF_INET, netdev.SOCK_STREAM, netdev.IPPROTO_TCP)
err := dev.Connect(fd, "", addrPort)
for ; err != nil; err = dev.Connect(fd, "", addrPort) {
message(err.Error())
time.Sleep(5 * time.Second)
}
n := 0
w := 0
start := time.Now()
// send data
message("Sending data")
for i := 0; i < 1000; i++ {
buf.Reset()
fmt.Fprint(buf,
"\r---------------------------- i == ", i, " ----------------------------"+
"\r---------------------------- i == ", i, " ----------------------------")
if w, err = dev.Send(fd, buf.Bytes(), 0, time.Time{}); err != nil {
println("error:", err.Error(), "\r")
break
}
n += w
}
buf.Reset()
ms := time.Now().Sub(start).Milliseconds()
fmt.Fprint(buf, "\nWrote ", n, " bytes in ", ms, " ms\r\n")
message(buf.String())
if _, err := dev.Send(fd, buf.Bytes(), 0, time.Time{}); err != nil {
println("error:", err.Error(), "\r")
}
println("Disconnecting TCP...")
dev.Close(fd)
}
func message(msg string) {
println(msg, "\r")
}
// Wait for user to open serial console
func waitSerial() {
for !machine.Serial.DTR() {
time.Sleep(100 * time.Millisecond)
}
}
@@ -1,52 +1,60 @@
// This example opens a TCP connection and sends some data,
// for the purpose of testing speed and connectivity.
// This example opens a TCP connection and sends some data, for the purpose of
// testing speed and connectivity.
//
// You can open a server to accept connections from this program using:
//
// nc -w 5 -lk 8080
// nc -lk 8080
//go:build pyportal || arduino_nano33 || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal || challenger_rp2040 || pico
package main
import (
"bytes"
"fmt"
"log"
"machine"
"net"
"time"
"tinygo.org/x/drivers/net"
"tinygo.org/x/drivers/netlink"
"tinygo.org/x/drivers/netlink/probe"
)
var (
// access point info
ssid string
pass string
addr string = "10.0.0.100:8080"
)
// IP address of the server aka "hub". Replace with your own info.
const serverIP = ""
var buf = &bytes.Buffer{}
func main() {
initAdaptor()
connectToAP()
waitSerial()
link, _ := probe.Probe()
err := link.NetConnect(&netlink.ConnectParams{
Ssid: ssid,
Passphrase: pass,
})
if err != nil {
log.Fatal(err)
}
for {
sendBatch()
time.Sleep(500 * time.Millisecond)
}
println("Done.")
}
func sendBatch() {
// make TCP connection
ip := net.ParseIP(serverIP)
raddr := &net.TCPAddr{IP: ip, Port: 8080}
laddr := &net.TCPAddr{Port: 8080}
message("---------------\r\nDialing TCP connection")
conn, err := net.DialTCP("tcp", laddr, raddr)
for ; err != nil; conn, err = net.DialTCP("tcp", laddr, raddr) {
conn, err := net.Dial("tcp", addr)
for ; err != nil; conn, err = net.Dial("tcp", addr) {
message(err.Error())
time.Sleep(5 * time.Second)
}
@@ -65,7 +73,7 @@ func sendBatch() {
"\r---------------------------- i == ", i, " ----------------------------")
if w, err = conn.Write(buf.Bytes()); err != nil {
println("error:", err.Error(), "\r")
continue
break
}
n += w
}
@@ -79,34 +87,17 @@ func sendBatch() {
println("error:", err.Error(), "\r")
}
// Right now this code is never reached. Need a way to trigger it...
println("Disconnecting TCP...")
conn.Close()
}
// connect to access point
func connectToAP() {
time.Sleep(2 * time.Second)
println("Connecting to " + ssid)
err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second)
if err != nil { // error connecting to AP
for {
println(err)
time.Sleep(1 * time.Second)
}
}
println("Connected.")
time.Sleep(2 * time.Second)
ip, err := adaptor.GetClientIP()
for ; err != nil; ip, err = adaptor.GetClientIP() {
message(err.Error())
time.Sleep(1 * time.Second)
}
message(ip)
}
func message(msg string) {
println(msg, "\r")
}
// Wait for user to open serial console
func waitSerial() {
for !machine.Serial.DTR() {
time.Sleep(100 * time.Millisecond)
}
}
+67
View File
@@ -0,0 +1,67 @@
// This example listens on port :8080 for client connections. Bytes
// received from the client are echo'ed back to the client. Multiple
// clients can connect as the same time, each consuming a client socket,
// and being serviced by it's own go func.
//
// Example test using nc as client to copy file:
//
// $ nc 10.0.0.2 8080 <file >copy ; cmp file copy
//go:build pyportal || arduino_nano33 || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal
package main
import (
"io"
"log"
"net"
"time"
"tinygo.org/x/drivers/netlink"
"tinygo.org/x/drivers/netlink/probe"
)
var (
ssid string
pass string
port string = ":8080"
)
var buf [1024]byte
func echo(conn net.Conn) {
defer conn.Close()
_, err := io.CopyBuffer(conn, conn, buf[:])
if err != nil && err != io.EOF {
log.Fatal(err.Error())
}
}
func main() {
time.Sleep(time.Second)
link, _ := probe.Probe()
err := link.NetConnect(&netlink.ConnectParams{
Ssid: ssid,
Passphrase: pass,
})
if err != nil {
log.Fatal(err)
}
l, err := net.Listen("tcp", port)
if err != nil {
log.Fatal(err.Error())
}
defer l.Close()
for {
conn, err := l.Accept()
if err != nil {
log.Fatal(err.Error())
}
go echo(conn)
}
}
+106
View File
@@ -0,0 +1,106 @@
// This example uses TLS to send an HTTPS request to retrieve a webpage
//
// You shall see "strict-transport-security" header in the response,
// this confirms communication is indeed over HTTPS
//
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security
//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal
package main
import (
"bufio"
"crypto/tls"
"fmt"
"io"
"log"
"machine"
"net"
"strings"
"time"
"tinygo.org/x/drivers/netlink"
"tinygo.org/x/drivers/netlink/probe"
)
var (
ssid string
pass string
// HTTPS server address to hit with a GET / request
address string = "httpbin.org:443"
)
var conn net.Conn
// Wait for user to open serial console
func waitSerial() {
for !machine.Serial.DTR() {
time.Sleep(100 * time.Millisecond)
}
}
func check(err error) {
if err != nil {
println("Hit an error:", err.Error())
panic("BYE")
}
}
func readResponse() {
r := bufio.NewReader(conn)
resp, err := io.ReadAll(r)
check(err)
println(string(resp))
}
func closeConnection() {
conn.Close()
}
func dialConnection() {
var err error
println("\r\n---------------\r\nDialing TLS connection")
conn, err = tls.Dial("tcp", address, nil)
for ; err != nil; conn, err = tls.Dial("tcp", address, nil) {
println("Connection failed:", err.Error())
time.Sleep(5 * time.Second)
}
println("Connected!\r")
}
func makeRequest() {
print("Sending HTTPS request...")
w := bufio.NewWriter(conn)
fmt.Fprintln(w, "GET /get HTTP/1.1")
fmt.Fprintln(w, "Host:", strings.Split(address, ":")[0])
fmt.Fprintln(w, "User-Agent: TinyGo")
fmt.Fprintln(w, "Connection: close")
fmt.Fprintln(w)
check(w.Flush())
println("Sent!\r\n\r")
}
func main() {
waitSerial()
link, _ := probe.Probe()
err := link.NetConnect(&netlink.ConnectParams{
Ssid: ssid,
Passphrase: pass,
})
if err != nil {
log.Fatal(err)
}
for i := 0; ; i++ {
dialConnection()
makeRequest()
readResponse()
closeConnection()
println("--------", i, "--------\r\n")
time.Sleep(10 * time.Second)
}
}
+120
View File
@@ -0,0 +1,120 @@
// This example runs on wioterminal. It gets an URL using http.Get() and
// displays the output on the wioterminal LCD screen.
//
// Note: It may be necessary to increase the stack size when using "net/http".
// Use the -stack-size=4KB command line option.
//go:build wioterminal
package main
import (
"fmt"
"image/color"
"io"
"log"
"machine"
"net/http"
"net/url"
"strings"
"time"
"tinygo.org/x/drivers/ili9341"
"tinygo.org/x/drivers/netlink"
"tinygo.org/x/drivers/netlink/probe"
"tinygo.org/x/tinyfont/proggy"
"tinygo.org/x/tinyterm"
)
var (
ssid string
pass string
)
var (
display = ili9341.NewSPI(
machine.SPI3,
machine.LCD_DC,
machine.LCD_SS_PIN,
machine.LCD_RESET,
)
backlight = machine.LCD_BACKLIGHT
terminal = tinyterm.NewTerminal(display)
black = color.RGBA{0, 0, 0, 255}
white = color.RGBA{255, 255, 255, 255}
red = color.RGBA{255, 0, 0, 255}
blue = color.RGBA{0, 0, 255, 255}
green = color.RGBA{0, 255, 0, 255}
font = &proggy.TinySZ8pt7b
)
func main() {
machine.SPI3.Configure(machine.SPIConfig{
SCK: machine.LCD_SCK_PIN,
SDO: machine.LCD_SDO_PIN,
SDI: machine.LCD_SDI_PIN,
Frequency: 40000000,
})
display.Configure(ili9341.Config{})
display.FillScreen(black)
backlight.Configure(machine.PinConfig{machine.PinOutput})
backlight.High()
terminal.Configure(&tinyterm.Config{
Font: font,
FontHeight: 10,
FontOffset: 6,
})
fmt.Fprintf(terminal, "Connecting to %s...\r\n", ssid)
link, _ := probe.Probe()
err := link.NetConnect(&netlink.ConnectParams{
Ssid: ssid,
Passphrase: pass,
})
if err != nil {
log.Fatal(err)
}
name := "John Doe"
occupation := "gardener"
params := "name=" + url.QueryEscape(name) + "&" +
"occupation=" + url.QueryEscape(occupation)
path := fmt.Sprintf("https://httpbin.org/get?%s", params)
cnt := 0
for {
fmt.Fprintf(terminal, "Getting %s\r\n\r\n", path)
resp, err := http.Get(path)
if err != nil {
fmt.Fprintf(terminal, "%s\r\n", err.Error())
time.Sleep(10 * time.Second)
continue
}
fmt.Fprintf(terminal, "%s %s\r\n", resp.Proto, resp.Status)
for k, v := range resp.Header {
fmt.Fprintf(terminal, "%s: %s\r\n", k, strings.Join(v, " "))
}
fmt.Fprintf(terminal, "\r\n")
body, err := io.ReadAll(resp.Body)
fmt.Fprintf(terminal, string(body))
resp.Body.Close()
cnt++
fmt.Fprintf(terminal, "-------- %d --------\r\n", cnt)
time.Sleep(10 * time.Second)
}
}
+117
View File
@@ -0,0 +1,117 @@
// This example uses TCP to send an HTTP request to retrieve a webpage. The
// HTTP request is hand-rolled to avoid the overhead of using http.Get() from
// the "net/http" package. See example/net/http-get for the full http.Get()
// functionality.
//
// Example HTTP server:
// ---------------------------------------------------------------------------
// package main
//
// import "net/http"
//
// func main() {
// http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
// w.Write([]byte("hello"))
// })
// http.ListenAndServe(":8080", nil)
// }
// ---------------------------------------------------------------------------
//go:build pyportal || arduino_nano33 || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal
package main
import (
"bufio"
"fmt"
"io"
"log"
"machine"
"net"
"strings"
"time"
"tinygo.org/x/drivers/netlink"
"tinygo.org/x/drivers/netlink/probe"
)
var (
ssid string
pass string
// HTTP server address to hit with a GET / request
address string = "10.0.0.100:8080"
)
var conn net.Conn
// Wait for user to open serial console
func waitSerial() {
for !machine.Serial.DTR() {
time.Sleep(100 * time.Millisecond)
}
}
func dialConnection() {
var err error
println("\r\n---------------\r\nDialing TCP connection")
conn, err = net.Dial("tcp", address)
for ; err != nil; conn, err = net.Dial("tcp", address) {
println("Connection failed:", err.Error())
time.Sleep(5 * time.Second)
}
println("Connected!\r")
}
func check(err error) {
if err != nil {
println("Hit an error:", err.Error())
panic("BYE")
}
}
func makeRequest() {
println("Sending HTTP request...")
w := bufio.NewWriter(conn)
fmt.Fprintln(w, "GET / HTTP/1.1")
fmt.Fprintln(w, "Host:", strings.Split(address, ":")[0])
fmt.Fprintln(w, "User-Agent: TinyGo")
fmt.Fprintln(w, "Connection: close")
fmt.Fprintln(w)
check(w.Flush())
println("Sent!\r\n\r")
}
func readResponse() {
r := bufio.NewReader(conn)
resp, err := io.ReadAll(r)
check(err)
println(string(resp))
}
func closeConnection() {
conn.Close()
}
func main() {
waitSerial()
link, _ := probe.Probe()
err := link.NetConnect(&netlink.ConnectParams{
Ssid: ssid,
Passphrase: pass,
})
if err != nil {
log.Fatal(err)
}
for i := 0; ; i++ {
dialConnection()
makeRequest()
readResponse()
closeConnection()
println("--------", i, "--------\r\n")
time.Sleep(10 * time.Second)
}
}
@@ -1,82 +1,52 @@
// This example listens on port :80 serving a web page. Multiple clients
// may connect and be serviced at the same time. IPv4 only. HTTP only.
//
// $ curl http://10.0.0.2
//
// Note: It may be necessary to increase the stack size when using "net/http".
// Use the -stack-size=4KB command line option.
//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal
package main
import (
"fmt"
"log"
"machine"
"net/http"
"strconv"
"time"
"tinygo.org/x/drivers/net/http"
"tinygo.org/x/drivers/wifinina"
"tinygo.org/x/drivers/netlink"
"tinygo.org/x/drivers/netlink/probe"
)
// You can override the settings with the init() in another source code:
//
// func init() {
// ssid = "your-ssid"
// pass = "your-password"
// }
//
// Or use -ldflags option on tinygo command to set at compile-time:
//
// tinygo flash ... -ldflags '-X "main.ssid=xxx" -X "main.pass=xxx"' ...
//
var (
ssid string
pass string
port string = ":80"
)
var led = machine.LED
func main() {
// wait a bit for serial
time.Sleep(2 * time.Second)
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
err := run()
for err != nil {
fmt.Printf("error: %s\r\n", err.Error())
time.Sleep(5 * time.Second)
}
}
link, _ := probe.Probe()
func run() error {
spi := machine.NINA_SPI
spi.Configure(machine.SPIConfig{
Frequency: 8 * 1e6,
SDO: machine.NINA_SDO,
SDI: machine.NINA_SDI,
SCK: machine.NINA_SCK,
err := link.NetConnect(&netlink.ConnectParams{
Ssid: ssid,
Passphrase: pass,
})
adaptor := wifinina.New(spi,
machine.NINA_CS,
machine.NINA_ACK,
machine.NINA_GPIO0,
machine.NINA_RESETN)
adaptor.Configure()
time.Sleep(2 * time.Second)
println("Connecting to " + ssid)
err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second)
if err != nil {
return err
log.Fatal(err)
}
println("Connected.")
time.Sleep(2 * time.Second)
ip, subnet, gateway, err := adaptor.GetIP()
if err != nil {
return err
}
fmt.Printf("IP Address : %s\r\n", ip)
fmt.Printf("Mask : %s\r\n", subnet)
fmt.Printf("Gateway : %s\r\n", gateway)
http.UseDriver(adaptor)
http.HandleFunc("/", root)
http.HandleFunc("/hello", hello)
http.HandleFunc("/cnt", cnt)
@@ -84,7 +54,11 @@ func run() error {
http.HandleFunc("/off", LED_OFF)
http.HandleFunc("/on", LED_ON)
return http.ListenAndServe(":80", nil)
err = http.ListenAndServe(port, nil)
for err != nil {
fmt.Printf("error: %s\r\n", err.Error())
time.Sleep(5 * time.Second)
}
}
func root(w http.ResponseWriter, r *http.Request) {
@@ -159,7 +133,7 @@ func root(w http.ResponseWriter, r *http.Request) {
</p>
</body>
</html>
`, access)
`, access)
}
func sixlines(w http.ResponseWriter, r *http.Request) {
@@ -203,7 +177,3 @@ func cnt(w http.ResponseWriter, r *http.Request) {
w.Header().Set(`Content-Type`, `application/json`)
fmt.Fprintf(w, `{"cnt": %d}`, counter)
}
func message(msg string) {
println(msg, "\r")
}
+64
View File
@@ -0,0 +1,64 @@
// This example is a websocket client. It connects to a websocket server
// which echos messages back to the client. For server, see
//
// https://pkg.go.dev/golang.org/x/net/websocket#example-Handler
//
// Note: It may be necessary to increase the stack size when using
// "golang.org/x/net/websocket". Use the -stack-size=4KB command line option.
//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal
package main
import (
"fmt"
"log"
"machine"
"time"
"golang.org/x/net/websocket"
"tinygo.org/x/drivers/netlink"
"tinygo.org/x/drivers/netlink/probe"
)
var (
ssid string
pass string
url string = "ws://10.0.0.100:8080/echo"
)
// Wait for user to open serial console
func waitSerial() {
for !machine.Serial.DTR() {
time.Sleep(100 * time.Millisecond)
}
}
func main() {
waitSerial()
link, _ := probe.Probe()
err := link.NetConnect(&netlink.ConnectParams{
Ssid: ssid,
Passphrase: pass,
})
if err != nil {
log.Fatal(err)
}
origin := "http://localhost/"
ws, err := websocket.Dial(url, "", origin)
if err != nil {
log.Fatal(err)
}
if _, err := ws.Write([]byte("hello, world!\n")); err != nil {
log.Fatal(err)
}
var msg = make([]byte, 512)
var n int
if n, err = ws.Read(msg); err != nil {
log.Fatal(err)
}
fmt.Printf("Received: %s", msg[:n])
}
+62
View File
@@ -0,0 +1,62 @@
// This example is a websocket server. It listens for websocket clients
// to connect and echos messages back to the client. For client, see
//
// https://pkg.go.dev/golang.org/x/net/websocket#example-Dial
//
// Note: It may be necessary to increase the stack size when using
// "golang.org/x/net/websocket". Use the -stack-size=4KB command line option.
//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal
package main
import (
"io"
"log"
"machine"
"net/http"
"time"
"golang.org/x/net/websocket"
"tinygo.org/x/drivers/netlink"
"tinygo.org/x/drivers/netlink/probe"
)
var (
ssid string
pass string
port string = ":8080"
)
// Echo the data received on the WebSocket.
func EchoServer(ws *websocket.Conn) {
io.Copy(ws, ws)
}
// Wait for user to open serial console
func waitSerial() {
for !machine.Serial.DTR() {
time.Sleep(100 * time.Millisecond)
}
}
// This example demonstrates a trivial echo server.
func main() {
waitSerial()
link, _ := probe.Probe()
err := link.NetConnect(&netlink.ConnectParams{
Ssid: ssid,
Passphrase: pass,
})
if err != nil {
log.Fatal(err)
}
http.Handle("/echo", websocket.Handler(EchoServer))
err = http.ListenAndServe(port, nil)
if err != nil {
panic("ListenAndServe: " + err.Error())
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

+28
View File
@@ -0,0 +1,28 @@
<html>
<head>
<title>TinyGo - A Go Compiler For Small Places</title>
<style>
body {
background-color: rgb(4, 111, 143);
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
}
img {
display: block;
margin: 0 auto;
width: 299px;
height: 255px;
}
h1 {
color: white;
text-align: center;
}
</style>
</head>
<body>
<h1>TinyGo - A Go Compiler For Small Places</h1>
<img src="images/tinygo-logo.png">
</body>
</html>
+49
View File
@@ -0,0 +1,49 @@
// This example is an HTTP server serving up a static file system
//
// Note: It may be necessary to increase the stack size when using "net/http".
// Use the -stack-size=4KB command line option.
//go:build pyportal || nano_rp2040 || metro_m4_airlift || arduino_mkrwifi1010 || matrixportal_m4 || wioterminal
package main
import (
"embed"
"log"
"net/http"
"time"
"tinygo.org/x/drivers/netlink"
"tinygo.org/x/drivers/netlink/probe"
)
var (
ssid string
pass string
port string = ":80"
)
//go:embed index.html main.go images
var fs embed.FS
func main() {
// wait a bit for console
time.Sleep(2 * time.Second)
link, _ := probe.Probe()
err := link.NetConnect(&netlink.ConnectParams{
Ssid: ssid,
Passphrase: pass,
})
if err != nil {
log.Fatal(err)
}
hfs := http.FileServer(http.FS(fs))
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
hfs.ServeHTTP(w, r)
})
log.Fatal(http.ListenAndServe(port, nil))
}
+40
View File
@@ -0,0 +1,40 @@
package main
import (
"encoding/hex"
"time"
"tinygo.org/x/drivers/onewire"
)
func main() {
pin := machine.D2
ow := onewire.New(pin)
for {
time.Sleep(3 * time.Second)
println()
println("Device:", machine.Device)
romIDs, err := ow.Search(onewire.SEARCH)
if err != nil {
println(err)
}
for _, romid := range romIDs {
println(hex.EncodeToString(romid))
}
if len(romIDs) == 1 {
// only 1 device on bus
r, err := ow.ReadAddress()
if err != nil {
println(err)
}
println(hex.EncodeToString(r))
}
}
}
+34
View File
@@ -0,0 +1,34 @@
package main
import (
"machine"
"time"
"tinygo.org/x/drivers/pcf8523"
)
func main() {
machine.I2C0.Configure(machine.I2CConfig{})
dev := pcf8523.New(machine.I2C0)
// make sure the battery takes over if power is lost
err := dev.SetPowerManagement(pcf8523.PowerManagement_SwitchOver_ModeStandard)
if err != nil {
panic(err)
}
// set RTC once, i.e. from `date -u +"%Y-%m-%dT%H:%M:%SZ"`
now, _ := time.Parse(time.RFC3339, "2023-09-18T20:31:38Z")
err = dev.SetTime(now)
if err != nil {
panic(err)
}
for {
ts, err := dev.ReadTime()
if err != nil {
panic(err)
}
println("tick-tock, it's: " + ts.String())
time.Sleep(2 * time.Second)
}
}
-131
View File
@@ -1,131 +0,0 @@
// This is a sensor station that uses a RTL8720DN running on the device UART2.
// It creates an MQTT connection that publishes a message every second
// to an MQTT broker.
//
// In other words:
// Your computer <--> USB-CDC <--> MCU <--> UART2 <--> RTL8720DN <--> Internet <--> MQTT broker.
//
// You must install the Paho MQTT package to build this program:
//
// go get -u github.com/eclipse/paho.mqtt.golang
//
// You can check that mqttpub is running successfully with the following command.
//
// mosquitto_sub -h test.mosquitto.org -t tinygo
package main
import (
"machine"
"fmt"
"math/rand"
"time"
"tinygo.org/x/drivers/net"
"tinygo.org/x/drivers/net/mqtt"
"tinygo.org/x/drivers/rtl8720dn"
)
// You can override the setting with the init() in another source code.
// func init() {
// ssid = "your-ssid"
// pass = "your-password"
// debug = true
// server = "tinygo.org"
// }
var (
ssid string
pass string
server string = "tcp://test.mosquitto.org:1883"
debug = false
)
var buf [0x400]byte
var lastRequestTime time.Time
var conn net.Conn
var adaptor *rtl8720dn.Driver
func main() {
err := run()
for err != nil {
fmt.Printf("error: %s\r\n", err.Error())
time.Sleep(5 * time.Second)
}
}
var (
topic = "tinygo"
)
func run() error {
// change the UART and pins as needed for platforms other than the WioTerminal.
adaptor = rtl8720dn.New(machine.UART3, machine.PB24, machine.PC24, machine.RTL8720D_CHIP_PU)
adaptor.Debug(debug)
adaptor.Configure()
err := adaptor.ConnectToAccessPoint(ssid, pass, 10*time.Second)
if err != nil {
return err
}
ip, subnet, gateway, err := adaptor.GetIP()
if err != nil {
return err
}
fmt.Printf("IP Address : %s\r\n", ip)
fmt.Printf("Mask : %s\r\n", subnet)
fmt.Printf("Gateway : %s\r\n", gateway)
rand.Seed(time.Now().UnixNano())
opts := mqtt.NewClientOptions()
opts.AddBroker(server).SetClientID("tinygo-client-" + randomString(10))
println("Connectng to MQTT...")
cl := mqtt.NewClient(opts)
if token := cl.Connect(); token.Wait() && token.Error() != nil {
failMessage(token.Error().Error())
}
for i := 0; ; i++ {
println("Publishing MQTT message...")
data := []byte(fmt.Sprintf(`{"e":[{"n":"hello %d","v":101}]}`, i))
token := cl.Publish(topic, 0, false, data)
token.Wait()
if err := token.Error(); err != nil {
return err
}
time.Sleep(100 * time.Millisecond)
}
// Right now this code is never reached. Need a way to trigger it...
println("Disconnecting MQTT...")
cl.Disconnect(100)
println("Done.")
return nil
}
// Returns an int >= min, < max
func randomInt(min, max int) int {
return min + rand.Intn(max-min)
}
// Generate a random string of A-Z chars with len = l
func randomString(len int) string {
bytes := make([]byte, len)
for i := 0; i < len; i++ {
bytes[i] = byte(randomInt(65, 90))
}
return string(bytes)
}
func failMessage(msg string) {
for {
println(msg)
time.Sleep(1 * time.Second)
}
}

Some files were not shown because too many files have changed in this diff Show More