├── 3d models ├── tilda6-bot.step └── tilda6-top.step ├── 3d-print-case ├── Chonky Case V1.step ├── Chonky Case V1.stl ├── Tidal Case Lite V6.stl ├── Tidal Case Lite V7.stl ├── Tidal Case Lite v6.step ├── Tidal Case Lite v7.step └── readme.md ├── AppProgrammingQuickest-start.md ├── AppQuickstart.md ├── README.md ├── batterymeasurements.md ├── boarddescription.md ├── gpio.md ├── hardwaredetails.md ├── hatchery.md ├── i2c.md ├── images ├── IMG_1515.JPG ├── IMG_1516.JPG ├── IMG_1517.JPG ├── IMG_1518.JPG ├── IMG_1519.JPG ├── IMG_1522.JPG ├── IMG_1523.JPG ├── image_2022_05_06T06_09_14_036Z.png ├── image_2022_05_06T06_09_29_406Z.png ├── image_2022_05_06T06_10_00_302Z.png ├── image_2022_05_06T06_10_13_339Z.png ├── image_2022_05_06T06_10_28_530Z.png ├── image_2022_05_06T06_19_09_443Z.png ├── tidal-bot-3d-front.png ├── tidal-bot-front.png ├── tidal-bot-reverse.png ├── tidal-detail-27.jpg ├── tidal-detail-28.jpg ├── tidal-detail-29.jpg ├── tidal-detail-30.jpg ├── tidal-detail-31.jpg ├── tidal-detail-32.jpg ├── tidal-detail-33.jpg ├── tidal-detail-34.jpg ├── tidal-edgeview.png ├── tidal-full-annotated.png ├── tidal-full-transparent-annotated.png ├── tidal-full-transparent.kra ├── tidal-full-transparent.png ├── tidal-full.kra ├── tidal-full.png ├── tidal-proto-battery.JPG ├── tidal-proto-handheld.jpg ├── tidal-proto-long.jpg ├── tidal-proto-usbside.jpg ├── tidal-top-3d-front.png ├── tidal-top-3d-reverse.png ├── tidal-top-front.png └── tidal-top-reverse.png ├── pictures.md ├── schematics ├── tidal-bot.pdf └── tidal-top.pdf └── screenreplacementguide.md /3d-print-case/Chonky Case V1.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emfcamp/tidal-docs/c7f852e3dcbfe9aba065a65fc4c3b00ce1114791/3d-print-case/Chonky Case V1.stl -------------------------------------------------------------------------------- /3d-print-case/Tidal Case Lite V6.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emfcamp/tidal-docs/c7f852e3dcbfe9aba065a65fc4c3b00ce1114791/3d-print-case/Tidal Case Lite V6.stl -------------------------------------------------------------------------------- /3d-print-case/Tidal Case Lite V7.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emfcamp/tidal-docs/c7f852e3dcbfe9aba065a65fc4c3b00ce1114791/3d-print-case/Tidal Case Lite V7.stl -------------------------------------------------------------------------------- /3d-print-case/readme.md: -------------------------------------------------------------------------------- 1 | # Files for the creation of a 3d printed case 2 | 3 | ## Tidal Case Lite - By CuppaMatt 4 | 5 | ![IMB_J4qof0](https://user-images.githubusercontent.com/17907465/173020839-02a96ae8-44f4-4840-aecc-075cbc0ee5d7.gif) 6 | 7 | Version History: 8 | - Version 6 published 9th June 2022 9 | - First good fit version 10 | - First public release 11 | - STL & STEP files released 12 | - Version 7 published 10th June 2022 13 | - Fixed clearance issue with LED torch 14 | - Thanks to 'castawayc' for the bug report (you get 1 CuppaMatt Bug Point) 15 | - STL & STEP files released 16 | 17 | This case & all versions thereof are licenced under the [Hippocratic Licence](https://firstdonoharm.dev/version/3/0/license/license.txt) 18 | 19 | ## Tidal Case Chonky - By CuppaMatt 20 | 21 | ![IMB_MyIAFV](https://user-images.githubusercontent.com/17907465/173811769-26d58493-bb32-4884-8b02-d691c8c85582.gif) 22 | 23 | Version History: 24 | - Version 6 published 15th June 2022 25 | - First good fit version 26 | - First public release 27 | - STL & STEP files released 28 | -------------------------------------------------------------------------------- /AppProgrammingQuickest-start.md: -------------------------------------------------------------------------------- 1 | # App Programming Quickest-start 2 | 3 | This shows you how to add code into a file, add it to main menu and run it. 4 | 5 | 1. Plug in your TiDAL in your computer with power off 6 | 2. Turn on your TiDAL 7 | 3. Open https://editor.badge.emfcamp.org/ in Google Chrome 8 | 4. Press "Connect", select your TiDAL (Espressif CDC Device on Windows) and press "Connect" 9 | 5. Press "Programming & Files" 10 | 6. Create a folder for your app in `apps`, e.g. `helloworld` 11 | * If your firmware is below v0.5.1, the `apps` folder does not exist - press "Make folder", second button from the left, and create a folder named `apps` 12 | 8. Select your app folder and press "Make file", call this `__init__.py` 13 | 9. Select `__init__.py` - the filename/path should be `/apps/helloworld/__init__.py` 14 | 10. Paste the following code into the file: 15 | ```Python 16 | from tidal import * 17 | from textwindow import TextWindow 18 | 19 | win = TextWindow(bg=BLACK, fg=WHITE) 20 | win.cls() 21 | win.println("Hello world!") 22 | ``` 23 | 10. Press "Save file", the rightmost button 24 | 11. Reset your TiDAL by pressing the button in the corner and see your app in the list on your TiDAL 25 | 26 | See https://github.com/emfcamp/tidal-docs/blob/main/AppQuickstart.md#structure-of-an-app for how to make your code into an actual App that works with task switching. 27 | -------------------------------------------------------------------------------- /AppQuickstart.md: -------------------------------------------------------------------------------- 1 | # TiDAL badge App Quickstart 2 | 3 | ## Intro 4 | 5 | Apps are written in [MicroPython](https://docs.micropython.org/en/latest/index.html). TiDAL specific definitions are in [`tidal.py`](https://github.com/emfcamp/TiDAL-Firmware/blob/main/modules/tidal.py): 6 | 7 | ```python 8 | from tidal import * 9 | ``` 10 | 11 | The TiDAL CPU is an ESP32S3. There is a useful page on the general ESP32 capabilities here: 12 | 13 | https://docs.micropython.org/en/latest/esp32/quickref.html 14 | 15 | Although it does not cover the specifics of the ESP32S3 or the TiDAL badge. There is more info about the badge hardware in [`boarddescription.md`](boarddescription.md). 16 | 17 | ## Badge hardware 18 | 19 | ### GPIOs and buttons 20 | 21 | `tidal.py` includes definitions for the 4 accessible GPIOs (`G0` to `G3`, which are configured as inputs by default) and the joystick and other buttons (`BUTTON_x` and `JOY_UP`, `JOY_DOWN` etc). These are [`Pin`](https://docs.micropython.org/en/latest/library/machine.Pin.html) objects. The buttons are all active-low, meaning their `value()` is `0` when they are pressed, and `1` when they are unpressed. 22 | 23 | ```python 24 | from tidal import * 25 | if JOY_CENTRE.value() == 0: 26 | print("Joystick is pressed down!") 27 | ``` 28 | 29 | There is a higher-level abstraction in `buttons.py` to allow you to register callbacks for when buttons are pressed: 30 | 31 | ```python 32 | from tidal import * 33 | from buttons import Buttons 34 | but = Buttons() 35 | but.on_press(JOY_CENTRE, lambda: print("Button pressed!")) 36 | ``` 37 | 38 | A `Buttons` instance is automatically created for you by `App` subclasses - call `self.buttons` (or `self.window.buttons`) in your `App` to access it. See [`Torch.on_start()`](https://github.com/emfcamp/TiDAL-Firmware/blob/main/modules/torch/__init__.py) for an example. 39 | 40 | `buttons.py` also exposes the a static API compatible with [BADGE.TEAM](https://badge.team/docs/esp32-platform-firmware/esp32-app-development/api-reference/buttons/), but since this is not app-aware it is recommended to use a `Buttons` instance instead. 41 | 42 | ### I2C 43 | 44 | [I2C Docs here](i2c.md) 45 | 46 | ### Display 47 | 48 | The `display` object is how you write to the screen. It is a [`ST7789`](https://github.com/russhughes/st7789_mpy/blob/master/README.md) object. The display is configured in portrait by default, and is 135x240 pixels supporting 16-bit colour. Several bitmap fonts are included. Using the default 8x8 font means you have room for 16x30 characters (or 16x26 if you leave a 1 pixel gap between lines, which is the default used by `TextWindow`). 49 | 50 | ```python 51 | from tidal import * 52 | import vga2_8x8 as font 53 | display.fill(WHITE) 54 | x = font.WIDTH * 2 # Indent 2 chars 55 | y = (font.HEIGHT+1) * 5 # And 5 lines down 56 | display.text(font, "Hello world!", x, y, RED, WHITE) 57 | ``` 58 | 59 | There is a higher-level abstraction available in `textwindow.py`: 60 | 61 | ```python 62 | from tidal import * 63 | from textwindow import TextWindow 64 | 65 | win = TextWindow(bg=BLACK, fg=WHITE) 66 | win.cls() 67 | win.println("Hello world!") 68 | # use win.display if you need to access the ST7789 directly 69 | ``` 70 | 71 | TextWindow may be instantiated separately, or your `App` subclass may construct one for you (which is what `TextApp` does). 72 | 73 | `ST7789` and `TextWindow` etc define colours in 16-bit 565 format - you can translate from RGB using the `color565(r, g, b)` helper function, which for convenience is imported into the `tidal` module along with some common colours like `BLACK`, `BLUE` etc. 74 | 75 | The native character set used by `ST7789` is [CP437](https://en.wikipedia.org/wiki/Code_page_437) so only a limited set of non-ASCII characters can be displayed. `TextWindow` takes care of converting Python strings to the appropriate format, so you normally shouldn't have to worry about it. 76 | 77 | For a more feature-rich UI API, see the section on [micro-gui](#micro-gui), below. 78 | 79 | ### PNG support 80 | 81 | Use the `lodepng` module to decode PNGs to a buffer compatible with our framebuffer format, then call `blit_buffer` to display. The example below assumes your PNG is saved to flash in the `/apps/MYAPP` directory. You can also pass a raw data buffer to `decode565` to do in-memory decoding, this may be useful when fetching images directly from the internet with eg `urequests`. 82 | 83 | ```python 84 | import tidal 85 | import lodepng 86 | (w, h, buf) = lodepng.decode565("/apps/MYAPP/myimg.png") 87 | 88 | display = tidal.display # Or use self.display if in a TextWindow subclass, or self.window.display in an App 89 | x = 0 90 | y = 0 91 | display.blit_buffer(buf, x, y, w, h) 92 | ``` 93 | 94 | ### Timers 95 | 96 | Use `App.after(time_ms, callback)` to queue a timer callback after a certain amount of milliseconds. The result is an object you can call `cancel()` on if you need to cancel the timer. These timers will wake the badge up from light sleep if necessary. Use `App.periodic()` if you want the timer to fire repeatedly. There are no restrictions on how many timers you can queue, unlike if you use `machine.Timer` which is not recommended because it's a much more complicated and nuanced API to use correctly. 97 | 98 | ```python 99 | self.timer = self.after(1000, do_stuff) 100 | # ... 101 | self.timer.cancel() 102 | ``` 103 | 104 | You can use `Scheduler.after()` instead if you don't have an `App` instance to hand: 105 | 106 | ```python 107 | from scheduler import get_scheduler 108 | 109 | my_timer = get_scheduler().after(1000, do_stuff) 110 | ``` 111 | 112 | ### Wi-Fi 113 | 114 | Wi-Fi connections can be established using the `wifi` module: 115 | 116 | ```python 117 | import wifi 118 | 119 | print(f"Currently-configured SSID is {wifi.get_ssid()}") 120 | # If not already connected... 121 | if not wifi.status(): 122 | # ... connect to the default-configured AP... 123 | wifi.connect() 124 | # ... and synchronously wait for it to connect or time out... 125 | wifi.wait() 126 | if wifi.status(): 127 | print("Connected!") 128 | else: 129 | print("Connection failed!") 130 | ``` 131 | 132 | This module is compatible with the [BADGE.TEAM API](https://badge.team/docs/esp32-platform-firmware/esp32-app-development/api-reference/wifi/) 133 | 134 | Having Wi-Fi connected has a significant impact on battery life. Do not connect unless your app really needs network access, and disconnect as soon as is practical. 135 | 136 | The badge firmware is pre-configured with the correct credentials to use during EMF 2022. The "Wi-Fi Config" app can be used to connect to other networks after the event (or call `wifi.connect(ssid, password)` directly). 137 | 138 | **Note:** Do not configure your badge as an access point during EMF 2022. This is [prohibited by the NOC team](https://wiki.emfcamp.org/wiki/Network/Rogue_Access_Points). 139 | 140 | ### Power management 141 | 142 | The EMF 2022 badge has a small battery and therefore implements aggressive power management. Any time that your app's code isn't running and neither Wi-Fi nor USB are being used, the CPU is in lightsleep. This means (almost) all clocks are suspended and only timers or button presses can wake the CPU. This affects, for example, the LEDC PWM API - and because this is used to dim the standard micropython PWM API is not usable on the TiDAL badge (strictly speaking, this is a hardware limitation combined with micropython not exposing the ability to set the required clock source). 143 | 144 | In addition, the screen will switch off after a configurable time period (by default, 30 seconds). 145 | 146 | ## Structure of an App 147 | 148 | All apps should be placed in the `/apps` directory. You may need to create this first. To allow for apps to include resources or other modules, each app should be in a separate directory with its init code in an `__init__.py`: 149 | 150 | ``` 151 | - myapp/ 152 | |- __init__.py 153 | |- 154 | ``` 155 | 156 | Apps should inherit from `app.App` or one of its subclasses. The example below uses `TextApp` and `BG` and `FG` to specify its default colour scheme. 157 | 158 | If you want the app listed in the menu, you should also add `main = YOURCLASS`. 159 | 160 | ```python 161 | from tidal import * 162 | from app import TextApp 163 | 164 | class MyApp(TextApp): 165 | 166 | BG = BLACK 167 | FG = WHITE 168 | 169 | def on_activate(self): 170 | super().on_activate() 171 | self.window.println("Hello world!") 172 | 173 | # Set the entrypoint for the app launher 174 | main = MyApp 175 | ``` 176 | 177 | 178 | ### App life cycle 179 | 180 | Your `App` object is instantiated by the scheduler when your app is launched (init is called with no arguments). Apps are expected to construct at least one window (generally, a subclass of `TextWindow`) and an associated `Buttons` instance. 181 | 182 | Prior to being displayed for the first time, `on_start()` is called. If overriding, you must call `super().on_start()`. Any initial `window` or `buttons` should normally be constructed by the time of the `App.on_start()` call, therefore a common pattern is to construct the initial window (but not draw to it) in the constructor (for example `TextApp` does this, see [app.py](https://github.com/emfcamp/TiDAL-Firmware/blob/main/modules/app.py)) or immediately prior to the `super().on_start()` call. 183 | 184 | Because `on_start()` is called exactly once, is a good place to declare any buttons callbacks you want to have - the app switching logic takes care of switching the active `Buttons` instance, so you don't have to worry about unregistering callbacks when your app deactivates or exits. 185 | 186 | `on_activate()` is responsible for actually displaying your app on screen. The default implementation activates the `Buttons` associated with `self.window` and calls `self.window.redraw()` to display the initial contents. Override and call `super().on_activate()` to take additional actions when activated (for example, to start any animation timers). Override and don't call super to completely customise how your app displays. 187 | 188 | You should not call anything that attempts to draw to the screen until the `on_activate()` call. Likewise you should not draw anything after `on_deactivate()` has been called - therefore it is a common pattern to cancel any queued timers in `on_deactivate()` if the timer callback results in anything being drawn to the screen. 189 | 190 | **Note:** forgetting to call `super().on_start()` or `super().on_activate()` is likely to cause your app to not display correctly or not react to button presses. 191 | 192 | `on_deactivate()` is called when your app is no longer being displayed on the screen. Usually this will be because the user has pressed the 'back' button to switch away from it, but it can also be because the inactivity timer has expired and switched off the display. 193 | 194 | If/when the firmware supports shutting down apps, `on_stop()` will be called immediately prior. 195 | 196 | ### TextApp 197 | 198 | The simplest way to make an app is to subclass `TextApp`. There are several declarative parameters you can define in your subclass to reduce the amount of code you have to write. Doing so means the only function you have to define is `on_activate()`, to actually determine what to display: 199 | 200 | ```python 201 | from tidal import * 202 | from app import TextApp 203 | 204 | class MyApp(TextApp): 205 | TITLE = "My App" # if defined, adds a title bar to the window. Edit later with self.window.set_title() 206 | BG = GREEN # The background colour 207 | FG = color565(0xF0, 0xF0, 0) # A non-standard colour to use as the default text colour 208 | 209 | def on_activate(self): 210 | super().on_activate() # This will clear the screen by calling TextWindow.redraw() 211 | self.window.println("Hello world!") 212 | ``` 213 | 214 | ### MenuApp 215 | 216 | This is similar to TextApp but instantiates a `Menu` window instead of a plain `TextWindow`. It has one additional declarative parameter you can set, called `CHOICES`, which define the initial items shown in the menu and what should be called when one of them is selected by the user: 217 | 218 | ```python 219 | from tidal import * 220 | from app import MenuApp 221 | 222 | class MyApp(MenuApp): 223 | # TITLE, BG and FG can also be specified 224 | FOCUS_FG = BLACK 225 | FOCUS_BG = CYAN 226 | CHOICES = ( 227 | ("Item 1", lambda: print("Selected item 1!")), 228 | ("Item 2", lambda: print("Selected item 2!")), 229 | ) 230 | 231 | main = MyApp 232 | ``` 233 | 234 | `MenuApp` takes care of registering the appropriate `Button` callbacks such that the joystick can be used to navigate and select the menu items. 235 | 236 | If desired you can omit defining `CHOICES` and instead populate the choices dynamically by calling `self.window.set_choices(...)` in `on_start()` or `on_activate()`. Remember to pass `redraw=False` if calling `set_choices()` from on_start, as drawing should not be done before `on_activate()`. 237 | 238 | ### micro-gui 239 | 240 | In addition to `TextWindow` based Apps, [micropython-micro-gui](https://github.com/peterhinch/micropython-micro-gui) (aka 'ugui') is also available. This is a higher level, widget-based UI framework. To use it, declare a class inheriting from `UguiApp` and set the `ROOT_SCREEN` member to the initial `Screen` to be displayed. `UgiApp` takes care of most of the integration of micro-gui with the TiDAL app framework, including button handling, app switching and power management. See the link above for more info on how to use micro-gui. 241 | 242 | ```python 243 | from uguiapp import UguiApp, Screen, ssd 244 | from gui.widgets import Label 245 | from gui.core.writer import CWriter 246 | import gui.fonts.arial10 as arial10 247 | from gui.core.colors import * 248 | 249 | class MyScreen(Screen): 250 | def __init__(self): 251 | super().__init__() 252 | wri = CWriter(ssd, arial10, GREEN, BLACK, verbose=True) 253 | Label(wri, 0, 0, "Hello world") 254 | 255 | class uGUIDemo(UguiApp): 256 | ROOT_SCREEN = MyScreen 257 | 258 | 259 | main = uGUIDemo 260 | ``` 261 | 262 | Up/down/left/right/select and the back button are automatically configured, although micro-gui has no built-in support for the A or B buttons. Call `App.on_press()` to use them. 263 | 264 | ## Testing on badge 265 | 266 | ### WebSerial 267 | WebSerial Editor can be found at 268 | http://editor.badge.emfcamp.org/ 269 | 270 | 271 | ### REPL 272 | The MicroPython REPL is available by default on the USB port, eg `screen /dev/ttyUSB0 115200` or `minicom -D /dev/tty.usbmodem1234561` (depending on OS etc) 273 | 274 | Prior to deploying apps for real, you can upload files to the board using `pyboard.py` and then test them via the REPL: 275 | 276 | ``` 277 | $ cd TiDAL-Firmware 278 | $ python3 micropython/tools/pyboard.py --no-soft-reset -d /dev/tty.usbmodem1234561 -f cp /path/to/myapp.py :/myapp.py 279 | $ python3 micropython/tools/pyboard.py --no-soft-reset -d /dev/tty.usbmodem1234561 -f ls / 280 | ls :/ 281 | 139 boot.py 282 | 920 myapp.py 283 | 284 | $ minicom -D /dev/tty.usbmodem1234561 285 | 286 | import myapp 287 | >>> myapp.main() 288 | ``` 289 | 290 | ### Adding to the menu 291 | To add the app to the startup menu, make sure you have `main = CLASSNAME` in the `__init__.py` 292 | 293 | You should add the app to the `apps` directory on the badge. 294 | 295 | ``` 296 | $ python3 micropython/tools/pyboard.py --no-soft-reset -d /dev/tty.usbmodem1234561 -f mkdir :/apps 297 | $ python3 micropython/tools/pyboard.py --no-soft-reset -d /dev/tty.usbmodem1234561 -f mkdir :/apps/MYAPP 298 | $ python3 micropython/tools/pyboard.py --no-soft-reset -d /dev/tty.usbmodem1234561 -f cp /path/to/__init__.py :/apps/MYAPP/__init__.py 299 | ``` 300 | Press back button on app launcher to force it to refresh the list (or reset the badge). 301 | 302 | ## Publishing to the Hatchery 303 | 304 | http://2022.badge.emfcamp.org/ 305 | 306 | See [Hatchery](hatchery.md) 307 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # This is the documentation for the TiDAL badge - your electronic companion at EMF2022 2 | 3 | ## Important Note about WiFi 4 | 5 | The WiFi is very limited on the TiDAL - you may need to be within a couple of metres of the access point to use it successfully. Best results are to stand next to an access point that isn't being heavily used. The one in the badge tent itself is good for this! The SSID `emfcamp` is preconfigured, and it won't make any difference switching to another - the problem is with the badge hardware itself. If you have changed the SSID, the credentials for the duration of EMF 2022 are SSID=`emfcamp`, username=`badge`, password=`badge` (WPA2 Enterprise). 6 | 7 | Some people have reported more success by opening "Settings" and changing "WiFi TX power" to "2 dBM". You'll have to be really close to the access point, but that may reduce interference. 8 | 9 | ## Links 10 | 11 | ![The TiDAL device](/images/tidal-edgeview.png) 12 | 13 | [Getting to know TiDAL](boarddescription.md) 14 | 15 | [Photos of TiDAL](pictures.md) 16 | 17 | [App developer guide](AppQuickstart.md) 18 | 19 | [Even quicker quickstart guide](AppProgrammingQuickest-start.md) 20 | 21 | TODO: [Install Apps from the Hatchery](hatchery.md) 22 | 23 | [Some notes on the Accelerometer chip](hardwaredetails.md) 24 | 25 | Schematics: [Bottom board](schematics/tidal-bot.pdf), [Top board](schematics/tidal-top.pdf) 26 | 27 | Some low level details about the [GPIO, display and torch APIs](gpio.md) 28 | 29 | TODO: Expansion developer guide 30 | 31 | [TiDAL firmware repo](https://github.com/emfcamp/TiDAL-Firmware) 32 | 33 | [Hardware schematics and datasheets](https://github.com/emfcamp/TiDAL-Hardware) 34 | 35 | TODO: Badge-compatible installation developer guide 36 | 37 | TODO: Badge firmware developer guide 38 | 39 | ## Sponsors 40 | 41 | ![Sponsored by](https://github.com/emfcamp/TiDAL-Firmware/raw/main/modules/sponsors/sponsored_by.png) 42 | ![Syndicate Systems](https://github.com/emfcamp/TiDAL-Firmware/raw/main/modules/sponsors/syndicate_systems.png) 43 | ![PCBGOGO](https://github.com/emfcamp/TiDAL-Firmware/raw/main/modules/sponsors/pcbgogo.png) 44 | ![Espressif](https://github.com/emfcamp/TiDAL-Firmware/raw/main/modules/sponsors/espressif.png) 45 | ![Google](https://github.com/emfcamp/TiDAL-Firmware/raw/main/modules/sponsors/google.png) 46 | ![Lucid](https://github.com/emfcamp/TiDAL-Firmware/raw/main/modules/sponsors/lucid.png) 47 | ![Twilio](https://github.com/emfcamp/TiDAL-Firmware/raw/main/modules/sponsors/twilio.png) 48 | ![aiven](https://github.com/emfcamp/TiDAL-Firmware/raw/main/modules/sponsors/aiven.png) 49 | ![Amateur Radio Digital Communications](https://github.com/emfcamp/TiDAL-Firmware/raw/main/modules/sponsors/ardc.png) 50 | ![Mathworks](https://github.com/emfcamp/TiDAL-Firmware/raw/main/modules/sponsors/mathworks.png) 51 | ![Sargasso](https://github.com/emfcamp/TiDAL-Firmware/raw/main/modules/sponsors/sargasso.png) 52 | ![SOS Intelligence](https://github.com/emfcamp/TiDAL-Firmware/raw/main/modules/sponsors/sos.png) 53 | ![Huboo](https://github.com/emfcamp/TiDAL-Firmware/raw/main/modules/sponsors/huboo.png) 54 | ![iluli by Mike Lamb](https://github.com/emfcamp/TiDAL-Firmware/raw/main/modules/sponsors/iluli.png) 55 | ![Mullvad VPN](https://github.com/emfcamp/TiDAL-Firmware/raw/main/modules/sponsors/mullvad.png) 56 | ![ONEGA](https://github.com/emfcamp/TiDAL-Firmware/raw/main/modules/sponsors/onega.png) 57 | ![UCL](https://github.com/emfcamp/TiDAL-Firmware/raw/main/modules/sponsors/ucl.png) 58 | ![Codethink](https://github.com/emfcamp/TiDAL-Firmware/raw/main/modules/sponsors/codethink.png) 59 | ![Ingeniunda](https://github.com/emfcamp/TiDAL-Firmware/raw/main/modules/sponsors/ingeniunda.png) 60 | ![Ookla](https://github.com/emfcamp/TiDAL-Firmware/raw/main/modules/sponsors/ookla.png) 61 | ![Pulsant](https://github.com/emfcamp/TiDAL-Firmware/raw/main/modules/sponsors/pulsant.png) 62 | ![Sandcat Consulting](https://github.com/emfcamp/TiDAL-Firmware/raw/main/modules/sponsors/sandcat.png) 63 | ![SecQuest](https://github.com/emfcamp/TiDAL-Firmware/raw/main/modules/sponsors/secquest.png) 64 | ![Suborbital](https://github.com/emfcamp/TiDAL-Firmware/raw/main/modules/sponsors/suborbital.png) 65 | ![The @ Company](https://github.com/emfcamp/TiDAL-Firmware/raw/main/modules/sponsors/the_at_company.png) 66 | ![Uber Space](https://github.com/emfcamp/TiDAL-Firmware/raw/main/modules/sponsors/uberspace.png) 67 | ![Pimoroni](https://github.com/emfcamp/TiDAL-Firmware/raw/main/modules/sponsors/pimoroni.png) 68 | -------------------------------------------------------------------------------- /batterymeasurements.md: -------------------------------------------------------------------------------- 1 | # Battery measurement data 2 | 3 | | Battery V | BTN_A V | Bat % | ADC value | 4 | | -------------- | ------- | ----- | ---- | 5 | | Button pressed | 0.8V | ? | ? | 6 | | 3.10 V | UVLO active | 0 | n/a | 7 | | 3.15 V | 2.27V | 0 | ? | 8 | | 3.20 V | 2.36V | 0 | ? | 9 | | 3.25 V | 2.40V | 1 | ? | 10 | | 3.30 V | 2.45V | 1 | ? | 11 | | 3.35 V | 2.49V | 1 | ? | 12 | | 3.40 V | 2.52V | 1 | ? | 13 | | 3.45 V | 2.57V | 2 | ? | 14 | | 3.50 V | 2.61V | 3 | ? | 15 | | 3.55 V | 2.65V | 4 | ? | 16 | | 3.60 V | 2.69V | 5 | ? | 17 | | 3.65 V | 2.72V | 8 | ? | 18 | | 3.70 V | 2.77V | 12 | ? | 19 | | 3.75 V | 2.80V | 25 | ? | 20 | | 3.80 V | 2.86V | 40 | ? | 21 | | 3.85 V | 2.90V | 55 | ? | 22 | | 3.90 V | 2.94V | 63 | ? | 23 | | 3.95 V | 2.98V | 70 | ? | 24 | | 4.00 V | 3.02V | 78 | ? | 25 | | 4.05 V | 3.06V | 82 | ? | 26 | | 4.10 V | 3.10V | 90 | ? | 27 | | 4.15 V | 3.14V | 95 | ? | 28 | | 4.20 V | 3.18V | 100 | ? | 29 | -------------------------------------------------------------------------------- /boarddescription.md: -------------------------------------------------------------------------------- 1 | # TiDAL description and features 2 | 3 | TiDAL is a handheld device in USB stick form factor. It has a USB-C plug at the front, and consists of a sandwich of two PCBs with a battery in between. 4 | 5 | Features include: 6 | - Extremely tiny - 60x25mm footprint 7 | - 135x240pixel 24-bit colour TFT screen 8 | - Portrait orientation 9 | - ST7789 controller 10 | - FAST! 11 | - 5-directional joystick button 12 | - 1 button on the top 13 | - 2 buttons on the sides 14 | - backlight/bootloader and reset buttons on top 15 | - 2 0.5mm pitch FFC connectors for expansions 16 | - Pinout: 3V3, GND, SCL, SDA, 4xGPIO (all ADC capable) 17 | - Same signals as FFC connectors brought out on solder pads on bottom of board with 1.27mm pitch 18 | - 1 addressable LED with switchable power, programmable colour and unbearable brightness so you can find your tent at night 19 | - 3-axis accelerometer (QMA7981) 20 | - 3-axis magnetometer (QMC7983) 21 | - ATECC108A cryptographic accelerator 22 | - on/off switch 23 | - very small battery 24 | - Native USB with goodies 25 | 26 | # The TiDAL device 27 | ![The TiDAL device](/images/tidal-full-annotated.png) 28 | 29 | When not plugged into a computer, TiDAL is meant to be used in the vertical orientation, with the USB plug pointing away from the user. In this configuration, the on/off switch is to the left of the screen. 30 | Sliding the switch towards the front enables power to the device. 31 | 32 | There are two side buttons either side of the device - the one on the right of the screen is **BUTTON_A** and the one on the left is **BUTTON_B**. BUTTON_A is used to wake the device from sleep mode. 33 | The button next to the corner of the screen is **BUTTON_FRONT**. By default, that's the button used to go back to the previous screen, or back to the launcher from inside an app. 34 | 35 | **To start your badge for the first time, slide the switch to the on (forward) position, and press BUTTON_A.** 36 | 37 | There is a joystick below BUTTON_FRONT. It can register clicks in four directions (JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN) and can also be pressed in the middle (JOY_CENTRE). 38 | 39 | Below that, there are two buttons. The one at the corner of the board is a **RESET** button, which restarts the firmware on the badge. The one next to it is the **BL** button. 40 | The BL button has two functions - it force-activates the backlight, and if held down while the board is reset, it activates the USB bootloader, which is used to replace the entire firmware on the board. 41 | 42 | Above the BL and RESET buttons are two flat flexible cable (FFC) connectors. They let you attach expansion boards to the badge. 43 | You can use the generic expansion board that comes with the device, and attach things to that, or you can design your own board that connects to this one with an FFC cable. 44 | The two connectors are identical and connected to the same signals. The pinout is (looking into the connector, left to right): 45 | 46 | 3V3, GND, SCL, SDA, G0, G1, G2, G3 47 | 48 | The same signals, in the same order, are exposed on solder pads at the bottom of the badge. 49 | 50 | ![The TiDAL device](/images/tidal-edgeview.png) 51 | 52 | -------------------------------------------------------------------------------- /gpio.md: -------------------------------------------------------------------------------- 1 | # Badge API reference 2 | 3 | All these assume the tidal module has already been imported with 4 | ``` 5 | >>> import tidal 6 | ``` 7 | 8 | ## LED/torch 9 | 10 | 11 | To turn LED power on 12 | ``` 13 | >>> tidal.led_power_on() 14 | ``` 15 | 16 | To turn LED power off 17 | 18 | ``` 19 | >>> tidal.led_power_off() 20 | ``` 21 | 22 | To set the LED colour (values are red, green, blue): 23 | 24 | ``` 25 | >>> tidal.led[0]=(0,0,255) #set LED to blue, full brightness 26 | >>> tidal.led.write() 27 | ``` 28 | 29 | Colours are a combination of red, green, and blue. Each colour channel goes from 0 to 255. 30 | 31 | To get white light at half brightness, use (127,127,127). 32 | 33 | To get red light at full brightness, use (255,0,0) 34 | 35 | 36 | ## Display power 37 | 38 | The LCD power enable and backlight enable are controlled separately. 39 | The backlight can be enabled without powering the LCD module on. 40 | 41 | to power on the display (this will also power on the backlight, but you can then turn it off again if you like): 42 | ``` 43 | >>> tidal.lcd_power_on() 44 | ``` 45 | 46 | to power off the display (this will also power off the backlight, but you can then turn it on again if you like): 47 | ``` 48 | >>> tidal.lcd_power_off() 49 | ``` 50 | 51 | When the display is off, it will retain the image stored, so when you switch it on again the same image that was there last time will be displayed. 52 | 53 | to power on the backlight: 54 | ``` 55 | >>> tidal.lcd_backlight_on(); 56 | ``` 57 | 58 | to power off the backlight: 59 | ``` 60 | >>> tidal.lcd_backlight_off(); 61 | ``` 62 | 63 | When the display is powered on but the backlight is powered off, users can press the BL button to view the screen contents. The backlight will 64 | illuminate while the button is pressed. For especially low power operation, you can power off the display, and then power it back on when the BL button is pressed. 65 | 66 | 67 | 68 | ## General GPIO (G0, G1, G2, G3) 69 | 70 | These are micropython Pin objects connected to the pins on the three expansion connectors. By default they are configured as inputs with pullups. 71 | If you reconfigure these pins, make sure that they are not left floating - either configure them as outputs, or inputs with pullup. 72 | 73 | ``` 74 | >>> tidal.G0.value() 75 | 1 76 | ``` 77 | 78 | ## Charge detect 79 | 80 | the charge detect line comes from the battery charger and is 0 for charging and 1 for not. 81 | 82 | ``` 83 | >>> tidal.CHARGE_DET.value() 84 | 0 85 | ``` 86 | 87 | 88 | -------------------------------------------------------------------------------- /hardwaredetails.md: -------------------------------------------------------------------------------- 1 | 2 | Notes on sensors: 3 | 4 | 5 | Step counter will be active all the time so make sure accelerometer clock is slow (and clock divider is low). 6 | For example, with 50kHz clock and divider 1935 there is an output data rate of 26Hz, which should be fine for steps. 7 | This adds some 8uA to power draw, which is fine. 8 | To set this combination, write 0xe2 to register 0x10 and 0xc4 to register 0x11 9 | 10 | To activate step counter, write 0x94 or 0x8c to register 0x12 11 | Read out steps from registers 0x7 and 0x8: steps=(reg_0x8<<8)|reg_0x7; 12 | 13 | Sensor orientation is different for accelerometer and magnetometer. 14 | Accelerometer positive X is to left, positive Y is away from USB. 15 | Magnetometer positive X is away from USB, positive Y is to the right. 16 | 17 | To convert magnetometer coordinates to accelerometer coordinates: 18 | 19 | def convert_to_accel_coords(mag_x, mag_y, mag_z): 20 | return(-mag_y, mag_x, mag_z) 21 | 22 | -------------------------------------------------------------------------------- /hatchery.md: -------------------------------------------------------------------------------- 1 | # Hatchery App store 2 | 3 | Our version of the badge.team hatchery is hosted at 4 | 5 | http://2022.badge.emfcamp.org/ 6 | 7 | TODO: Registration, projects(eggs), code edit, github import, publishing 8 | 9 | 10 | ## API 11 | 12 | Full API documentation and swagger explorer can be found at [https://2022.badge.emfcamp.org/api](https://2022.badge.emfcamp.org/api) 13 | OpenAPI Specification (json) can be found at [https://2022.badge.emfcamp.org/docs](https://2022.badge.emfcamp.org/docs) 14 | 15 | * Basket 16 | * Related to getting Projects for specific Badge models. 17 | * Egg 18 | * Related to getting Eggs / Projects. 19 | * External 20 | * External API proxies for convenience of apps. 21 | 22 | 23 | ## Hatchery source and issues 24 | Code repo for issues https://github.com/emfcamp/TiDAL-Hatchery/ 25 | 26 | -------------------------------------------------------------------------------- /i2c.md: -------------------------------------------------------------------------------- 1 | # I2C examples 2 | 3 | Some examples of reading and writing to devices on the tidal-mk6 badge in micropython. Note, some of these examples may leave the chips in a different state to their original and therefore the chips may need to be reset before any other examples are attempted. 4 | 5 | ## Getting started 6 | 7 | 8 | ``` 9 | import tidal 10 | from tidal import i2c 11 | ``` 12 | 13 | if you want to use the peripheral i2c (for example on the expansion pads on the bottom or on the two ribbon cable connectors) you will need to activate it: 14 | 15 | ``` 16 | tidal.enable_peripheral_I2C() 17 | from tidal import i2cp 18 | ``` 19 | 20 | 21 | scan the i2c bus for devices: 22 | 23 | ``` 24 | >>> i2c.scan() 25 | [18, 44, 54] 26 | ``` 27 | There should be three devices show, 18 is the QMA7981 accelerometer, 44 is the QMC7983 magnetic sensor and 54 is the cryptography chip 28 | 29 | 30 | or, for the peripheral i2c: 31 | 32 | ``` 33 | >>> i2cp.scan() 34 | ``` 35 | 36 | 37 | 38 | ## QMA7981 39 | 40 | Read the identity byte from register 0x00: 41 | 42 | ``` 43 | >>> i2c.readfrom_mem(18,0,1) 44 | b'\xe7' 45 | ``` 46 | 47 | interrupt reading (the values assume the chip has an interrupt asserted, if not the values will be oposite): 48 | ``` 49 | >>> from machine import Pin 50 | >>> irq = Pin(40, Pin.IN, Pin.PULL_UP) 51 | >>> irq.value() 52 | 0 53 | ``` 54 | 55 | set interrupt pin to active high: 56 | ``` 57 | >>> i2c.writeto_mem(18, 0x20, 4); 58 | >>> irq.value(); 59 | 5 60 | ``` 61 | 62 | set interrupt pin back to active low: 63 | ``` 64 | >>> i2c.writeto_mem(18, 0x20, b'5') 65 | >>> irq.value() 66 | 0 67 | ``` 68 | 69 | to do a power-on reset... 70 | ``` 71 | >>> i2c.writeto_mem(18, 0x36, b'\xb6') 72 | ``` 73 | 74 | read the power-state: 75 | >>> bytes = i2c.readfrom_mem(18, 0x11, 1) 76 | >>> print("{}".format(bytes[0])) 77 | 78 | read the data int status: 79 | ``` 80 | >>> bytes = i2c.readfrom_mem(18, 0x0b, 1) 81 | >>> print("{}".format(bytes[0])) 82 | ``` 83 | 84 | initialse settings to 2G, 1024Hz bandwidth 85 | ``` 86 | >>> i2c.writeto_mem(18, 0x11, b'\xc0') 87 | >>> i2c.writeto_mem(18, 0xf, b'\x01') 88 | >>> i2c.writeto_mem(18, 0x10, b'\x05') 89 | ``` 90 | 91 | full example application: 92 | 93 | ``` 94 | ## QMA7981 accelerometer example code 95 | 96 | from tidal import i2c 97 | import time 98 | import ustruct 99 | 100 | # reset 101 | i2c.writeto_mem(18, 0x36, b'\xb6') 102 | time.sleep(0.2) 103 | i2c.writeto_mem(18, 0x36, b'\x00') 104 | 105 | ## todo check post reset state 106 | 107 | # power up, set to 4G, 1024Hz bandwidth 108 | scale = 4 109 | i2c.writeto_mem(18, 0x11, b'\xc1') 110 | i2c.writeto_mem(18, 0xf, b'\x01') 111 | i2c.writeto_mem(18, 0x10, b'\x05') 112 | 113 | def read_val(bytes): 114 | if ((bytes[0] & 1) == 0): 115 | return 0 116 | raw = ustruct.unpack("> 2 118 | return (raw * scale)/ (1 << 13); 119 | 120 | while True: 121 | rawdata = i2c.readfrom_mem(18, 1, 6) 122 | x = read_val(rawdata[0:2]) 123 | y = read_val(rawdata[2:4]) 124 | z = read_val(rawdata[4:6]) 125 | 126 | 127 | print("X {} Y {} Z {} RAW {}".format(x,y,z,rawdata)) 128 | time.sleep_ms(200) 129 | ``` 130 | 131 | ## QMC7983 132 | 133 | 134 | Read the chip identification register: 135 | ``` 136 | >>> i2c.readfrom_mem(44, 0xD, 1) 137 | b'2' 138 | ``` 139 | 140 | do a basic initialisation, as per the datasheet 141 | 142 | ``` 143 | >>> i2c.writeto_mem(44, 0xb, b'\x0f') 144 | >>> i2c.writeto_mem(44, 0x9, b'\x3d') 145 | ``` 146 | 147 | read all the data registers 148 | 149 | ``` 150 | >>> bytes = i2c.readfrom_mem(44, 0x0, 8) 151 | >>> print("{}".format(bytes)) 152 | b'\xd8\x07\x19\n\x8e_\x00\xfe' 153 | ``` 154 | 155 | full example application: 156 | ``` 157 | ## QMA7983 demonstration code 158 | 159 | from tidal import i2c 160 | import time 161 | import ustruct 162 | 163 | i2c.writeto_mem(44, 0xb, b'\x0f') 164 | 165 | # initialise depending on scale 166 | scale = 8 167 | if (scale == 16): 168 | i2c.writeto_mem(44, 0x9, b'\x3d') 169 | if (scale == 12): 170 | i2c.writeto_mem(44, 0x9, b'\x2d') 171 | if (scale == 8): 172 | i2c.writeto_mem(44, 0x9, b'\x1d') 173 | if (scale == 2): 174 | i2c.writeto_mem(44, 0x9, b'\x0d') 175 | 176 | # for some reason from_bytes is ignoring sign 177 | def read_val(bytes): 178 | return (ustruct.unpack("