72 Commits

Author SHA1 Message Date
Nic Limper
9ad847eb53 Update content_cards.json.gz 2023-08-22 13:15:03 +02:00
Nic Limper
f268c48717 add wifi events to log.txt 2023-08-22 13:06:42 +02:00
Nic Limper
a0ebf0d255 weather values in fahrenheit/mph 2023-08-21 01:05:43 +02:00
Nic Limper
6fcfe02b28 updated .gz-files for www folder 2023-08-20 20:18:58 +02:00
Nic Limper
2edbf27033 various fixes / sleep at night / resend image at tag boot
- apconfig: configure night time, where the system is not updating tags, and the tags are sleeping
- fixed bug calculating expected checkin
- throttle down apinfo update to once per minute
- fixed too many concurrent requests getting tagtypes
- resend current image when a tag reboots and the content type is 'static image' (all other content types were already regenerating the content)
- fixed timing of main loop
2023-08-20 20:16:34 +02:00
Nic Limper
46fb23b70f drop json files to a tagcard + small fixes 2023-08-19 20:31:25 +02:00
Moritz Wirger
fce6c16153 Optimize tagDB and reduce RAM usage (#113)
* Reduce code size by removing nullptr assignments

* Optimize tagDB for loops

* More tagDB optimizations

* Remove static from language arrays reducing RAM by 5128b

- Reduces Flash by 13060b

* Add missing extern in tag_db.h

* Fix deprecation warning of sntp.h

* Remove static from contentmanager, reduces RAM by 184b

* Use string reference in prepareDataAvail

- Remove some unneeded buffers
- Remove some gotos
2023-08-17 10:06:21 +02:00
atc1441
a31e1453d0 Now resending the Pending data on Reset of the Connected AP 2023-08-15 13:49:09 +02:00
Nic Limper
d17502cb63 contextmenu: right-click on a tagcard 2023-08-15 12:42:59 +02:00
atc1441
828679b6f3 Update README.md 2023-08-14 23:43:23 +02:00
atc1441
bff6794303 Added Yellow AP Gerber files 2023-08-14 21:40:34 +02:00
atc1441
2f86ea54d0 Precompiled ESP32-C6 AP 2023-08-14 21:28:03 +02:00
atc1441
80e0d9e5dd Added ESP32-C6 AP Release :) 2023-08-14 21:25:38 +02:00
atc1441
4d8cfeae63 Added nRF52811 AP Port 2023-08-14 12:12:44 +02:00
atc1441
3271f399c9 Auto template for AP Display disabled as currently to many refreshes break and reset the AP 2023-08-14 12:02:32 +02:00
atc1441
862c08c00b Set AP Info screen as default template on boot again to know IP of AP 2023-08-13 22:43:59 +02:00
Nic Limper
77cbf92281 bugfix getlocation 2023-08-13 01:48:32 +02:00
Moritz Wirger
c4022e45f9 Fix weather icons exception (#114) 2023-08-13 01:00:47 +02:00
atc1441
a13c220565 Fixed countdown via negative count value 2023-08-12 21:43:37 +02:00
Nic Limper
3989ecc3a3 bugfix date display 2023-08-12 21:17:08 +02:00
Nic Limper
cb0f029900 Update build-test.yml
don't build all small variations. Just the minor platforms, the rest will work.
2023-08-12 21:11:33 +02:00
Nic Limper
443714e9dc build fixes 2023-08-12 20:58:25 +02:00
Nic Limper
f3f163fa00 Revert "Ziped web files"
This reverts commit 8848cfc0f8.
2023-08-12 20:19:01 +02:00
Bot
8848cfc0f8 Ziped web files 2023-08-12 17:16:45 +00:00
Moritz Wirger
6c91c78aa1 Refactor contentmanager (#112)
* Start contentmanager cleanup

- Make destination constant
- Remove unneeded asignments
- Improve loops
- Move printHeap function into util

* Refactor contentmanager::drawNew

* Optimize contentManager::replaceVariables

* Fix missing const in prepareCancelPending

* Refactor drawDate

* Refactor drawWeather, drawForecast & getLocation

- Generalize http get json function

* Add util function for printing largest free block

* Reuse weather icons for both drawWeather & drawForecast

* Make httpGetJson timeout const

* Reafctor more functions

* Add few more const

* Fix spelling mistake

* Add util for debugging streams

- Add util for checking if strings are empty or contain null
- Fix file and string checks

* Remove leftover debug print
2023-08-12 16:57:42 +02:00
Nic Limper
98baa020ff bugfix ap info screen 2023-08-12 16:17:06 +02:00
Nic Limper
56e1e9e6b6 small fixes
- build in led on esp32-s2 active low
- floyd steinberg dithering improved
- add tokyo time zone
2023-08-12 13:18:22 +02:00
jjwbruijn
2817aa7e38 Added 7.5 OTA img 2023-08-12 09:17:55 +02:00
jjwbruijn
2292aeaf7e another beta of the 88mz100 7.5 fw 2023-08-12 02:26:33 +02:00
Nic Limper
3472b5d4b6 drop image files to upload 2023-08-12 01:11:25 +02:00
Moritz Wirger
19f6a093d3 Cleanup ota files (#111)
- Constify everything
- Remove duplicate code
- Fix warnings in js
- Reduces bin size from 1283453 to 1283273 bytes
2023-08-12 00:37:26 +02:00
Nic Limper
d6734a7f79 bugfix database backup button 2023-08-11 22:42:02 +02:00
Nic Limper
da487fbc5f gzipped files in /www are now stored deterministic, without timestamp 2023-08-11 19:46:22 +02:00
Moritz Wirger
3b7fd7d1f6 Fix a few js warnings in main.js (#108)
* Fix indentation of content_cards json
* Fix multiple warnings in main.js
2023-08-11 19:39:20 +02:00
Nic Limper
f4273630ee rework of apinfo screen + variables in jsontemplate
- AP info screen content card. Can run on any tag.
- now, you can use {variables} in the 'text' entries in a json template. You can set variables via a http call. If you update a variable, all tags with a json template containing that variable get updated.
- font name in json template is more flexible. You can just use 'filename.ttf' or 'filename.vlw'. A full path is still possible.
- colors in the json template can now be set using #rrggbb color values, and using 'black', 'white' or 'red'.
- added direct output for the TFT display for the yellow esp32-s3. No file writes needed.
- added POST variable 'ttl' to json template upload and image upload, to set the next checkin time
- added /variables-demo.html to demonstrate the variables.
- json templates received from jsonupload are now saved in /current, and reused.
- known issue: 'backup db' button doesn't work due to some browser policy change. Fixing.
thanks to @steinwedel for the inspiration on the variables and some other fixes.
2023-08-11 18:46:46 +02:00
Jelmer
0cbb9e770e esp32 deep sleep support 2023-08-10 23:22:24 +02:00
jjwbruijn
9dfb37b376 Merge branch 'master' of github.com:jjwbruijn/OpenEPaperLink 2023-08-10 22:38:07 +02:00
jjwbruijn
e457475ea7 zbs243 tag FW 2.0 - added deepsleep 2023-08-10 22:26:50 +02:00
Jonas Niesner
806b2dc9e0 Merge pull request #107 from enwi/symlinks
Add batch for generating symlinks on windows
2023-08-10 22:18:48 +02:00
Jonas Niesner
2b7fc93043 Update release.yml 2023-08-10 22:16:27 +02:00
Moritz Wirger
6a711c99eb Add batch for generating symlinks 2023-08-10 21:49:55 +02:00
Nic Limper
ffd0acff72 tft driver for yellow board 2023-08-09 23:40:39 +02:00
Jelmer
a9c9812525 Merge pull request #104 from AndreKR/3dp-shelf-holder
Add 3D-printed shelf holder
2023-08-09 22:44:12 +02:00
André Hänsel
03ce036083 Accessories: Image of the shelf holder 2023-08-09 22:39:28 +02:00
André Hänsel
a98ad537ea Accessories: Source file for the shelf holder 2023-08-09 22:39:19 +02:00
André Hänsel
2726b4d2fe Accessories: Add a shelf holder
This adds a shelf holder for shelves with a thickness of 2 cm.
2023-08-09 22:38:03 +02:00
Jonas Niesner
fd5adb40f8 Typo fix 2023-08-09 21:32:45 +02:00
Jonas Niesner
89346b5d77 Update and rename esp32-build-test.yml to build-test.yml 2023-08-09 18:03:29 +02:00
Nic Limper
7ac57577df prelimary tagtype def for EL029H3WRA (M3 2.9")
I will finetune the content positions later
2023-08-08 17:31:30 +02:00
Nic Limper
09f7466f6f new flexible tagtype definition
- new: tagtypes are be defined via json in /tagtypes instead of hardcoded
- content template is moved to the tagtype definition
- optimalisation of spr2buffer (also uses psram now, if available)
- bugfixes in spiffseditor
- size fix in painter screen for large screens
2023-08-08 16:31:20 +02:00
atc1441
55e50b1920 Merge branch 'master' of https://github.com/jjwbruijn/OpenEPaperLink 2023-08-08 16:00:25 +02:00
atc1441
7cb5e13a7f Added compiled version of 7.4" 2023-08-08 16:00:05 +02:00
Jonas Niesner
6f08af2c67 Create 32MB_partition table.csv 2023-08-07 17:30:07 +02:00
Jonas Niesner
e185ddd99b Add OutdoorAP 2023-08-07 16:59:47 +02:00
atc1441
3079101c48 Tag DB Size was only 8bit now 32bit, need to be fixed in further UDP usage later as well 2023-08-07 11:53:28 +02:00
atc1441
0b66a23ce4 Added Yellow AP basic parts, for now using Arduino GFX for simplicity 2023-08-06 23:35:30 +02:00
Nic Limper
41c7cb843f cleanup non-.gz files in www folder 2023-08-05 22:51:38 +02:00
Nic Limper
c43e71288d delete files carefully to not brick the thing if update fails 2023-08-05 22:11:45 +02:00
Nic Limper
66cc62339f fix aligning U8G2 fonts / improv protocol fix 2023-08-05 22:05:33 +02:00
atc1441
84bfb18261 Update cc2531_OEPL.c 2023-08-05 15:21:13 +02:00
Nic Limper
ab3cef8dda Update readme.md 2023-08-04 11:30:01 +02:00
Nic Limper
c831f07b6e group/sort/filter tag list 2023-08-03 23:15:05 +02:00
atc1441
ba39f60e69 Added Sonoff ZB Bridge-P as Board 2023-08-03 23:09:21 +02:00
atc1441
f409f493ec Added CC2531 EOPL Port plus the modded python station by foorschtbar 2023-08-03 18:53:30 +02:00
Nic Limper
df783c6f8f ability to mirror tags
mirrored tags don't take file system space (f they are local to the AP), and don't take time to generate. They share their buffer with the source tag.
2023-08-03 12:37:29 +02:00
Nic Limper
f0e4d25d36 use barcode scanner to select a tag 2023-08-02 20:04:55 +02:00
Nic Limper
b4bf060a51 json template is now streaming (both file and url)
'unlimited' size
2023-07-31 22:35:00 +02:00
Nic Limper
e3407468ea weather icons via truetype
Don't forget to update the file system: added .ttf font, deleted some .vlw fonts.
Saves about 144kB.
2023-07-31 20:25:31 +02:00
Nic Limper
8daff632ba truetype: more floats -> better rendering 2023-07-31 20:22:50 +02:00
Nic Limper
21a52e9b17 truetype fixes
- fixed curves using intermediate points
- now uses ascender to find baseline
- now uses unitsPerEm for font size calculations
rendering is not perfect yet, probably mainly due to rounding issues
2023-07-31 19:02:05 +02:00
Nic Limper
f9b4d163bc cleanup and gzip web files
- clean up unnecessary files
- fix ota update, maybe preventing timeouts
- new 'wwwroot' folder for the web source files, and python script to gzip the files to the /data/www folder
- run gzip_wwwfiles.py to compress the files in wwwroot into /data/www
2023-07-29 20:47:12 +02:00
Nic Limper
a78f2e3af5 added truetype fonts 2023-07-29 14:47:01 +02:00
13731 changed files with 7117128 additions and 4037 deletions

81
.github/workflows/build-test.yml vendored Normal file
View File

@@ -0,0 +1,81 @@
name: Firmware build test
on: [push,pull_request]
jobs:
build:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- uses: actions/cache@v3
with:
path: |
~/.cache/pip
~/.platformio/.cache
key: ${{ runner.os }}-pio
- uses: actions/setup-python@v4
with:
python-version: '3.9'
- name: Install PlatformIO Core
run: pip install --upgrade platformio
- name: Build Simple_AP
run: |
cd ESP32_AP-Flasher
pio run --environment Simple_AP
pio run --target buildfs --environment Simple_AP
- name: Build OpenEPaperLink_Mini_AP
run: |
cd ESP32_AP-Flasher
pio run --environment OpenEPaperLink_Mini_AP
pio run --target buildfs --environment OpenEPaperLink_Mini_AP
# - name: Build OpenEPaperLink_Nano_AP
# run: |
# cd ESP32_AP-Flasher
# pio run --environment OpenEPaperLink_Nano_AP
# pio run --target buildfs --environment OpenEPaperLink_Nano_AP
- name: Build OpenEPaperLink_AP_and_Flasher
run: |
cd ESP32_AP-Flasher
pio run --environment OpenEPaperLink_AP_and_Flasher
pio run --target buildfs --environment OpenEPaperLink_AP_and_Flasher
# - name: Build Wemos_d1_mini32_AP
# run: |
# cd ESP32_AP-Flasher
# pio run --environment Wemos_d1_mini32_AP
# pio run --target buildfs --environment Wemos_d1_mini32_AP
# - name: Build M5Stack_Core_ONE_AP
# run: |
# cd ESP32_AP-Flasher
# pio run --environment M5Stack_Core_ONE_AP
# pio run --target buildfs --environment M5Stack_Core_ONE_AP
- name: Build ESP32_S3_16_8_YELLOW_AP]
run: |
cd ESP32_AP-Flasher
pio run --environment ESP32_S3_16_8_YELLOW_AP
pio run --target buildfs --environment ESP32_S3_16_8_YELLOW_AP
# - name: Build Sonoff_zb_bridge_P_AP
# run: |
# cd ESP32_AP-Flasher
# pio run --environment Sonoff_zb_bridge_P_AP
# pio run --target buildfs --environment Sonoff_zb_bridge_P_AP
# - name: Build OpenEPaperLink_CC1352P
# run: |
# cd ESP32_AP-Flasher
# pio run --environment OpenEPaperLink_CC1352P
# pio run --target buildfs --environment OpenEPaperLink_CC1352P
# - name: OutdoorAP
# run: |
# cd ESP32_AP-Flasher
# pio run --environment OutdoorAP
# pio run --target buildfs --environment OutdoorAP

View File

@@ -1,57 +0,0 @@
name: ESP32 firmware builf test
on: [push,pull_request]
jobs:
build:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- uses: actions/cache@v3
with:
path: |
~/.cache/pip
~/.platformio/.cache
key: ${{ runner.os }}-pio
- uses: actions/setup-python@v4
with:
python-version: '3.9'
- name: Install PlatformIO Core
run: pip install --upgrade platformio
- name: Build Simple_AP
run: |
cd ESP32_AP-Flasher
pio run --environment Simple_AP
pio run --target buildfs --environment Simple_AP
- name: Build OpenEPaperLink_Mini_AP
run: |
cd ESP32_AP-Flasher
pio run --environment OpenEPaperLink_Mini_AP
pio run --target buildfs --environment OpenEPaperLink_Mini_AP
- name: Build OpenEPaperLink_Nano_AP
run: |
cd ESP32_AP-Flasher
pio run --environment OpenEPaperLink_Nano_AP
pio run --target buildfs --environment OpenEPaperLink_Nano_AP
- name: Build OpenEPaperLink_AP_and_Flasher
run: |
cd ESP32_AP-Flasher
pio run --environment OpenEPaperLink_AP_and_Flasher
pio run --target buildfs --environment OpenEPaperLink_AP_and_Flasher
- name: Build Wemos_d1_mini32_AP
run: |
cd ESP32_AP-Flasher
pio run --environment Wemos_d1_mini32_AP
pio run --target buildfs --environment Wemos_d1_mini32_AP
- name: Build M5Stack_Core_ONE_AP
run: |
cd ESP32_AP-Flasher
pio run --environment M5Stack_Core_ONE_AP
pio run --target buildfs --environment M5Stack_Core_ONE_AP

View File

@@ -21,6 +21,18 @@ jobs:
with:
python-version: '3.9'
# - name: Zip web files
# run: |
# cd /home/runner/work/OpenEPaperLink/OpenEPaperLink/ESP32_AP-Flasher
# python gzip_wwwfiles.py
# - name: Commit zipped files
# run: |
# git config --global user.name 'Bot'
# git config --global user.email "bot@openepaperlink.de"
# git commit -am "Zipped web files"
# git push origin HEAD:master
- name: Install PlatformIO Core
run: pip install --upgrade platformio
@@ -138,6 +150,24 @@ jobs:
cd /home/runner/work/OpenEPaperLink/OpenEPaperLink
cp OpenEPaperLink_AP_and_Flasher/firmware.bin espbinaries/OpenEPaperLink_AP_and_Flasher.bin
cp OpenEPaperLink_AP_and_Flasher/merged-firmware.bin espbinaries/OpenEPaperLink_AP_and_Flasher_full.bin
- name: Build firmware for ESP32_S3_16_8_YELLOW_AP
run: |
cd ESP32_AP-Flasher
export PLATFORMIO_BUILD_FLAGS="-D BUILD_VERSION=${{ github.ref_name }} -D SHA=$GITHUB_SHA"
pio run --environment ESP32_S3_16_8_YELLOW_AP
pio run --target buildfs --environment ESP32_S3_16_8_YELLOW_AP
mkdir /home/runner/work/OpenEPaperLink/OpenEPaperLink/ESP32_S3_16_8_YELLOW_AP
cp ~/.platformio/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/ESP32_S3_16_8_YELLOW_AP/boot_app0.bin
cp .pio/build/ESP32_S3_16_8_YELLOW_AP/firmware.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/ESP32_S3_16_8_YELLOW_AP/firmware.bin
cp .pio/build/ESP32_S3_16_8_YELLOW_AP/bootloader.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/ESP32_S3_16_8_YELLOW_AP/bootloader.bin
cp .pio/build/ESP32_S3_16_8_YELLOW_AP/partitions.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/ESP32_S3_16_8_YELLOW_AP/partitions.bin
cp .pio/build/ESP32_S3_16_8_YELLOW_AP/littlefs.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/ESP32_S3_16_8_YELLOW_AP/littlefs.bin
cd /home/runner/work/OpenEPaperLink/OpenEPaperLink/ESP32_S3_16_8_YELLOW_AP
esptool.py --chip esp32-s3 merge_bin -o merged-firmware.bin --flash_mode dio --flash_freq 80m --flash_size 16MB 0x0000 bootloader.bin 0x8000 partitions.bin 0xe000 boot_app0.bin 0x10000 firmware.bin 0x00910000 littlefs.bin
cd /home/runner/work/OpenEPaperLink/OpenEPaperLink
cp ESP32_S3_16_8_YELLOW_AP/firmware.bin espbinaries/ESP32_S3_16_8_YELLOW_AP.bin
cp ESP32_S3_16_8_YELLOW_AP/merged-firmware.bin espbinaries/ESP32_S3_16_8_YELLOW_AP_full.bin
- name: generate release json file
run: |

View File

@@ -1,2 +1,4 @@
build
*.axf
# Allow
!*.bin

View File

@@ -0,0 +1,98 @@
const char font[256][40]={
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x20
{0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x21
{0x00,0x00,0xC0,0x18,0xC0,0x18,0xC0,0x18,0xC0,0x18,0xC0,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x22
{0x00,0x00,0x00,0x00,0x00,0x22,0x00,0x22,0x00,0x11,0x00,0x11,0x00,0x11,0xE0,0xFF,0x80,0x08,0x80,0x08,0x80,0x08,0xE0,0x7F,0x40,0x04,0x40,0x04,0x20,0x02,0x20,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x23
{0x00,0x00,0x00,0x04,0x00,0x1F,0x80,0x3F,0xC0,0x24,0xC0,0x04,0xC0,0x04,0x80,0x07,0x00,0x07,0x00,0x1C,0x00,0x1C,0x00,0x34,0x00,0x34,0x40,0x34,0xC0,0x1F,0x80,0x0F,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x24
{0x00,0x00,0x00,0x00,0xE0,0x81,0x30,0x43,0x30,0x23,0x30,0x13,0x30,0x0B,0x30,0x0B,0xE0,0x05,0x00,0x7A,0x00,0xCD,0x00,0xCD,0x80,0xCC,0x40,0xCC,0x20,0xCC,0x10,0x78,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x25
{0x00,0x00,0x00,0x00,0x00,0x0F,0x80,0x1F,0x80,0x19,0x80,0x19,0x80,0x0D,0x00,0x07,0xC0,0x03,0x60,0xC6,0x30,0xCE,0x30,0xCC,0x30,0x78,0x70,0x78,0xE0,0x7F,0xC0,0xEF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x26
{0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x27
{0x00,0x00,0x00,0x30,0x00,0x3C,0x00,0x0E,0x00,0x06,0x00,0x03,0x00,0x03,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x00,0x03,0x00,0x03,0x00,0x06,0x00,0x0E,0x00,0x3C,0x00,0x30,0x00,0x00}, // 0x28
{0x00,0x00,0xC0,0x00,0xC0,0x03,0x00,0x07,0x00,0x06,0x00,0x0C,0x00,0x0C,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x0C,0x00,0x0C,0x00,0x06,0x00,0x07,0xC0,0x03,0xC0,0x00,0x00,0x00}, // 0x29
{0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x03,0x60,0x33,0xE0,0x3C,0x00,0x00,0x80,0x0D,0xC0,0x19,0x80,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x2A
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0xE0,0x7F,0xE0,0x7F,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x2B
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x07,0x00,0x07,0x00,0x06,0x00,0x02,0x00,0x03,0x00,0x00}, // 0x2C
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x3F,0xC0,0x3F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x2D
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x07,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x2E
{0x00,0x00,0x00,0x60,0x00,0x30,0x00,0x30,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x0C,0x00,0x0C,0x00,0x06,0x00,0x06,0x00,0x03,0x00,0x03,0x80,0x01,0x80,0x01,0x80,0x01,0xC0,0x00,0xC0,0x00,0x60,0x00,0x00,0x00}, // 0x2F
{0x00,0x00,0x00,0x00,0x00,0x0F,0x80,0x1F,0xC0,0x30,0xC0,0x30,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0xC0,0x30,0xC0,0x30,0x80,0x1F,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x30
{0x00,0x00,0x00,0x00,0x00,0x06,0xC0,0x07,0x60,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0xE0,0x7F,0xE0,0x7F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x31
{0x00,0x00,0x00,0x00,0x80,0x0F,0xC0,0x1F,0x40,0x38,0x00,0x30,0x00,0x30,0x00,0x30,0x00,0x18,0x00,0x0C,0x00,0x06,0x00,0x03,0x80,0x01,0xC0,0x00,0xC0,0x3F,0xC0,0x3F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x32
{0x00,0x00,0x00,0x00,0x80,0x0F,0xC0,0x3F,0x40,0x30,0x00,0x30,0x00,0x18,0x80,0x0F,0x80,0x0F,0x00,0x18,0x00,0x30,0x00,0x30,0x00,0x30,0x40,0x38,0xC0,0x1F,0xC0,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x33
{0x00,0x00,0x00,0x00,0x00,0x18,0x00,0x1C,0x00,0x1E,0x00,0x1A,0x00,0x19,0x80,0x19,0xC0,0x18,0x40,0x18,0xE0,0x7F,0xE0,0x7F,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x34
{0x00,0x00,0x00,0x00,0x80,0x3F,0x80,0x3F,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x0F,0x80,0x1F,0x00,0x38,0x00,0x30,0x00,0x30,0x00,0x38,0x80,0x1F,0x80,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x35
{0x00,0x00,0x00,0x00,0x00,0x0F,0x80,0x1F,0xC0,0x11,0xC0,0x00,0x60,0x00,0x60,0x0E,0x60,0x1F,0xE0,0x38,0x60,0x30,0x60,0x30,0x60,0x30,0xC0,0x38,0xC0,0x1F,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x36
{0x00,0x00,0x00,0x00,0xC0,0x7F,0xC0,0x7F,0x00,0x60,0x00,0x30,0x00,0x10,0x00,0x18,0x00,0x0C,0x00,0x04,0x00,0x06,0x00,0x02,0x00,0x03,0x00,0x03,0x80,0x01,0x80,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x37
{0x00,0x00,0x00,0x00,0x00,0x0F,0xC0,0x1F,0xC0,0x18,0xC0,0x18,0xC0,0x19,0x80,0x0F,0x00,0x07,0xC0,0x1E,0x60,0x38,0x60,0x30,0x60,0x30,0xE0,0x38,0xC0,0x1F,0x80,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x38
{0x00,0x00,0x00,0x00,0x80,0x07,0xC0,0x1F,0xE0,0x18,0x60,0x30,0x60,0x30,0x60,0x30,0xE0,0x38,0xC0,0x37,0x80,0x33,0x00,0x30,0x00,0x18,0x40,0x1C,0xC0,0x0F,0x80,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x39
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x07,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x07,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x3A
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x07,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x07,0x00,0x07,0x00,0x06,0x00,0x02,0x00,0x03,0x00,0x00}, // 0x3B
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x70,0x00,0x3C,0x00,0x0E,0x80,0x03,0xE0,0x00,0x80,0x03,0x00,0x0E,0x00,0x3C,0x00,0x70,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x3C
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x7F,0xE0,0x7F,0x00,0x00,0x00,0x00,0xE0,0x7F,0xE0,0x7F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x3D
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0xE0,0x00,0xC0,0x03,0x00,0x07,0x00,0x1C,0x00,0x70,0x00,0x1C,0x00,0x07,0xC0,0x03,0xE0,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x3E
{0x00,0x00,0x00,0x00,0xE0,0x0F,0xE0,0x3F,0x20,0x38,0x00,0x30,0x00,0x30,0x00,0x18,0x00,0x0C,0x00,0x06,0x00,0x03,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x3F
{0x00,0x00,0x00,0x00,0x00,0x1F,0x80,0x31,0xC0,0x60,0x60,0x7C,0x30,0x66,0x30,0x63,0x30,0x63,0x30,0x73,0x30,0x73,0x30,0x6F,0x60,0xE6,0x60,0x00,0xC0,0x00,0x00,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x40
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x0F,0x00,0x0F,0x00,0x0D,0x80,0x19,0x80,0x19,0xC0,0x38,0xC0,0x30,0xC0,0x3F,0xE0,0x7F,0x60,0x60,0x60,0x60,0x30,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x41
{0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x0F,0xE0,0x1F,0x60,0x18,0x60,0x18,0x60,0x0C,0xE0,0x07,0xE0,0x0F,0x60,0x18,0x60,0x30,0x60,0x30,0x60,0x30,0xE0,0x1F,0xE0,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x42
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3E,0x80,0x7F,0xC0,0x41,0xC0,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0xC0,0x00,0xC0,0x43,0x80,0x7F,0x00,0x3E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x43
{0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x0F,0xE0,0x1F,0x60,0x38,0x60,0x70,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x30,0x60,0x38,0xE0,0x1F,0xE0,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x44
{0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x7F,0xC0,0x7F,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x3F,0xC0,0x3F,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x7F,0xC0,0x7F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x45
{0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x7F,0xC0,0x7F,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x3F,0xC0,0x3F,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x46
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3E,0x80,0x7F,0xC0,0x41,0xC0,0x00,0x60,0x00,0x60,0x00,0x60,0x78,0x60,0x78,0x60,0x60,0xC0,0x60,0xC0,0x61,0x80,0x7F,0x00,0x3E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x47
{0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0xE0,0x3F,0xE0,0x3F,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x48
{0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x1F,0xE0,0x1F,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0xE0,0x1F,0xE0,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x49
{0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x1F,0x80,0x1F,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x1C,0xC0,0x0F,0xC0,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x4A
{0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x70,0x60,0x38,0x60,0x1C,0x60,0x0E,0x60,0x06,0x60,0x03,0xE0,0x03,0x60,0x07,0x60,0x0E,0x60,0x1C,0x60,0x38,0x60,0x70,0x60,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x4B
{0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x7F,0xC0,0x7F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x4C
{0x00,0x00,0x00,0x00,0x00,0x00,0x70,0x70,0x70,0x70,0xF0,0x78,0xB0,0x68,0xB0,0x68,0xB0,0x6D,0x30,0x65,0x30,0x65,0x30,0x67,0x30,0x62,0x30,0x60,0x30,0x60,0x30,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x4D
{0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x60,0xE0,0x60,0xE0,0x61,0xE0,0x61,0x60,0x63,0x60,0x67,0x60,0x66,0x60,0x6E,0x60,0x6C,0x60,0x78,0x60,0x78,0x60,0x70,0x60,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x4E
{0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x0F,0xC0,0x1F,0xE0,0x38,0x70,0x70,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x70,0x70,0xE0,0x38,0xC0,0x1F,0x80,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x4F
{0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x1F,0xC0,0x3F,0xC0,0x70,0xC0,0x60,0xC0,0x60,0xC0,0x70,0xC0,0x3F,0xC0,0x0F,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x50
{0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x0F,0xC0,0x1F,0xE0,0x38,0x70,0x70,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x60,0x30,0xE0,0x38,0xC0,0x1F,0x80,0x0F,0x00,0x38,0x00,0xF0,0x00,0x40,0x00,0x00}, // 0x51
{0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x07,0xE0,0x1F,0x60,0x18,0x60,0x18,0x60,0x18,0x60,0x1C,0xE0,0x0F,0xE0,0x07,0x60,0x0E,0x60,0x1C,0x60,0x38,0x60,0x70,0x60,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x52
{0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x0F,0xC0,0x1F,0x60,0x10,0x60,0x00,0xE0,0x00,0xC0,0x03,0x80,0x0F,0x00,0x3C,0x00,0x30,0x00,0x30,0x60,0x38,0xE0,0x1F,0xC0,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x53
{0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0xFF,0xF0,0xFF,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x54
{0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0xE0,0x38,0xC0,0x1F,0x80,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x55
{0x00,0x00,0x00,0x00,0x00,0x00,0x70,0xC0,0x60,0x60,0x60,0x60,0xE0,0x60,0xC0,0x30,0xC0,0x30,0xC0,0x31,0x80,0x19,0x80,0x1B,0x00,0x0B,0x00,0x0F,0x00,0x0F,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x56
{0x00,0x00,0x00,0x00,0x00,0x00,0x30,0xC0,0x30,0xC0,0x30,0xC0,0x20,0x46,0x20,0x46,0x20,0x6E,0x60,0x6F,0x60,0x69,0x60,0x69,0x60,0x39,0xC0,0x39,0xC0,0x39,0xC0,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x57
{0x00,0x00,0x00,0x00,0x00,0x00,0x70,0xE0,0xE0,0x60,0xC0,0x30,0x80,0x19,0x80,0x0F,0x00,0x0F,0x00,0x06,0x00,0x0F,0x80,0x1D,0x80,0x19,0xC0,0x30,0x60,0x70,0x30,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x58
{0x00,0x00,0x00,0x00,0x00,0x00,0x70,0xC0,0x60,0x60,0xC0,0x30,0xC0,0x31,0x80,0x19,0x00,0x0F,0x00,0x0F,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x59
{0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x7F,0xE0,0x7F,0x00,0x60,0x00,0x30,0x00,0x18,0x00,0x0C,0x00,0x06,0x00,0x03,0x80,0x01,0xC0,0x00,0x60,0x00,0xE0,0x7F,0xE0,0x7F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x5A
{0x00,0x00,0x00,0x3F,0x00,0x3F,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x3F,0x00,0x3F,0x00,0x00}, // 0x5B
{0x00,0x00,0x60,0x00,0xC0,0x00,0xC0,0x00,0x80,0x01,0x80,0x01,0x80,0x01,0x00,0x03,0x00,0x03,0x00,0x06,0x00,0x06,0x00,0x0C,0x00,0x0C,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x30,0x00,0x30,0x00,0x60,0x00,0x00}, // 0x5C
{0x00,0x00,0xC0,0x0F,0xC0,0x0F,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0xC0,0x0F,0xC0,0x0F,0x00,0x00}, // 0x5D
{0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x04,0x00,0x0E,0x00,0x0A,0x00,0x0B,0x00,0x1B,0x80,0x11,0x80,0x31,0xC0,0x30,0xC0,0x20,0x60,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x5E
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0xFF,0xF0,0xFF,0x00,0x00,0x00,0x00}, // 0x5F
{0x00,0x06,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x60
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x1F,0xC0,0x3F,0x40,0x30,0x00,0x30,0x00,0x30,0x80,0x3F,0xC0,0x30,0x60,0x30,0x60,0x38,0xE0,0xFF,0xC0,0xE7,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x61
{0x00,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x0E,0x60,0x1F,0xE0,0x39,0xE0,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0xE0,0x18,0xE0,0x1F,0x60,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x62
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0xC0,0x3F,0xC0,0x21,0xE0,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0xE0,0x00,0xC0,0x01,0xC0,0x3F,0x00,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x63
{0x00,0x00,0x00,0x30,0x00,0x30,0x00,0x30,0x00,0x30,0x80,0x37,0xC0,0x3F,0xC0,0x38,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0xE0,0x38,0xC0,0x3F,0x80,0x33,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x64
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xC0,0x1F,0xC0,0x38,0x60,0x30,0xE0,0x3F,0xE0,0x3F,0x60,0x00,0x60,0x00,0xC0,0x20,0xC0,0x3F,0x00,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x65
{0x00,0x00,0x00,0x7E,0x00,0x7F,0x00,0x03,0x00,0x03,0xE0,0x7F,0xE0,0x7F,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x66
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x37,0xC0,0x3F,0xC0,0x38,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0xE0,0x38,0xC0,0x3F,0x80,0x37,0x00,0x30,0x40,0x38,0xC0,0x1F,0x80,0x0F}, // 0x67
{0x00,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x1E,0x60,0x3F,0xE0,0x31,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x68
{0x00,0x00,0x00,0x0C,0x00,0x0C,0x00,0x00,0x00,0x00,0xC0,0x0F,0xC0,0x0F,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x69
{0x00,0x00,0x00,0x18,0x00,0x18,0x00,0x00,0x00,0x00,0x80,0x1F,0x80,0x1F,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x1C,0xC0,0x0F,0xC0,0x07}, // 0x6A
{0x00,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x70,0xC0,0x38,0xC0,0x1C,0xC0,0x0E,0xC0,0x06,0xC0,0x07,0xC0,0x0E,0xC0,0x1C,0xC0,0x38,0xC0,0x70,0xC0,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x6B
{0x00,0x00,0xC0,0x0F,0xC0,0x0F,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x6C
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xB0,0x39,0xF0,0x7F,0x70,0x67,0x30,0x63,0x30,0x63,0x30,0x63,0x30,0x63,0x30,0x63,0x30,0x63,0x30,0x63,0x30,0x63,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x6D
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x1E,0x60,0x3F,0xE0,0x31,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x6E
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xC0,0x3F,0xC0,0x30,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0xC0,0x30,0xC0,0x3F,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x6F
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x0F,0xE0,0x1F,0xE0,0x38,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0xE0,0x18,0xE0,0x1F,0x60,0x0F,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00}, // 0x70
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x37,0xC0,0x3F,0xC0,0x38,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0xE0,0x38,0xC0,0x37,0x80,0x33,0x00,0x30,0x00,0x30,0x00,0x30,0x00,0x30}, // 0x71
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x3C,0xC0,0x3E,0xC0,0x23,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x72
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x1F,0xC0,0x1F,0xC0,0x00,0xC0,0x00,0xC0,0x03,0x00,0x1F,0x00,0x38,0x00,0x30,0x40,0x30,0xC0,0x1F,0x80,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x73
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x03,0xE0,0x7F,0xE0,0x7F,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x7F,0x00,0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x74
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x3C,0xE0,0x37,0xC0,0x33,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x75
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x60,0xC0,0x20,0xC0,0x30,0xC0,0x30,0x80,0x11,0x80,0x19,0x80,0x19,0x00,0x0B,0x00,0x0F,0x00,0x0F,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x76
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0xC0,0x30,0xC6,0x30,0xC6,0x20,0x4E,0x60,0x4F,0x60,0x49,0x60,0x69,0x60,0x79,0xC0,0x39,0xC0,0x30,0xC0,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x77
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x70,0xC0,0x30,0x80,0x19,0x80,0x0B,0x00,0x0F,0x00,0x06,0x00,0x0F,0x80,0x1D,0x80,0x19,0xC0,0x30,0x60,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x78
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x60,0xC0,0x20,0xC0,0x30,0xC0,0x31,0x80,0x19,0x80,0x19,0x00,0x0B,0x00,0x0F,0x00,0x0F,0x00,0x06,0x00,0x06,0x00,0x02,0x00,0x03,0xC0,0x03,0xC0,0x01}, // 0x79
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x3F,0xE0,0x3F,0x00,0x30,0x00,0x18,0x00,0x0C,0x00,0x06,0x00,0x03,0x80,0x01,0xC0,0x00,0xE0,0x3F,0xE0,0x3F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x7A
{0x00,0x00,0x00,0x3C,0x00,0x3E,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0xC0,0x03,0xC0,0x03,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x3E,0x00,0x3C,0x00,0x00}, // 0x7B
{0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x00}, // 0x7C
{0x00,0x00,0xC0,0x03,0xC0,0x07,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x3C,0x00,0x3C,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0xC0,0x07,0xC0,0x03,0x00,0x00}, // 0x7D
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x43,0xE0,0x7F,0x20,0x3C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 0x7E
{0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x3F,0x40,0x20,0x40,0x20,0x40,0x20,0x40,0x20,0x40,0x20,0x40,0x20,0x40,0x20,0x40,0x20,0x40,0x20,0x40,0x20,0x40,0x20,0xC0,0x3F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00} // 0x7F
};

View File

@@ -1,10 +1,11 @@
MZ_FLASHER=python ../88MZ100_Flasher/88MZ100_Uart_flasher.py
ARMGCC=armgcc/bin/
ARMGCC=/usr/bin/
CC=$(ARMGCC)arm-none-eabi-gcc
AS=$(ARMGCC)arm-none-eabi-as
OBJCOPY=$(ARMGCC)arm-none-eabi-objcopy
#-Wall
CC_WARNING_FLAGS=-Wall -Wformat=0 -Wattributes -Wstrict-aliasing=0
CC_FlAGS=-mcpu=cortex-m3 -g -O0 -mthumb -fdata-sections -ffunction-sections -std=c99
@@ -16,10 +17,15 @@ C_EXECUTABLE :=$(C_SOURCES:.c=)
COMPORT = COM12
build: clean compile create_ota_img flash_uart_flash
build: compile
only: clean compile create_ota_img
uart: clean compile flash_uart
#build: clean compile create_ota_img flash_uart_flash
#only: clean compile create_ota_img
#uart: clean compile flash_uart
compile:
@mkdir -p build
@$(AS) -mcpu=cortex-m3 --gdwarf-2 -mthumb-interwork -o build/startup.o startup.S
@@ -40,16 +46,8 @@ compile:
@$(CC) $(CC_FlAGS) -c mz100_gpt.c -o build/mz100_gpt.o
@$(CC) $(CC_FlAGS) -c mz100_sleep.c -o build/mz100_sleep.o
@$(CC) $(CC_FlAGS) -c mz100_uart.c -o build/mz100_uart.o
# UZLIB
@$(CC) $(CC_FlAGS) $(CC_WARNING_FLAGS) -c uzlib/src/adler32.c -o build/adler32.o
@$(CC) $(CC_FlAGS) $(CC_WARNING_FLAGS) -c uzlib/src/crc32.c -o build/crc32.o
@$(CC) $(CC_FlAGS) $(CC_WARNING_FLAGS) -c uzlib/src/defl_static.c -o build/defl_static.o
@$(CC) $(CC_FlAGS) $(CC_WARNING_FLAGS) -c uzlib/src/genlz77.c -o build/genlz77.o
@$(CC) $(CC_FlAGS) $(CC_WARNING_FLAGS) -c uzlib/src/tinfgzip.c -o build/tinfgzip.o
@$(CC) $(CC_FlAGS) $(CC_WARNING_FLAGS) -c uzlib/src/tinflate.c -o build/tinflate.o
@$(CC) $(CC_FlAGS) $(CC_WARNING_FLAGS) -c uzlib/src/tinfzlib.c -o build/tinfzlib.o
# UZLIB END
@$(CC) $(CC_FlAGS) $(CC_WARNING_FLAGS) -c compression.c -o build/compression.o
@$(CC) $(CC_FlAGS) -c mz100_aon_ram.c -o build/mz100_aon_ram.o
@$(CC) $(CC_FlAGS) -c printf.c -o build/printf.o
@$(CC) $(CC_FlAGS) $(CC_WARNING_FLAGS) -c zigbee.c -o build/zigbee.o
@$(CC) $(CC_FlAGS) $(CC_WARNING_FLAGS) -c ccm.c -o build/ccm.o
@$(CC) $(CC_FlAGS) $(CC_WARNING_FLAGS) -c chars.c -o build/chars.o
@@ -61,10 +59,10 @@ compile:
@$(CC) $(CC_FlAGS) $(CC_WARNING_FLAGS) -c timer.c -o build/timer.o
@$(CC) $(CC_FlAGS) $(CC_WARNING_FLAGS) -c util.c -o build/util.o
@$(CC) $(CC_FlAGS) $(CC_WARNING_FLAGS) -c gpio.c -o build/gpio.o
@$(CC) $(CC_FlAGS) $(CC_WARNING_FLAGS) -c nfc.c -o build/nfc.o
@$(CC) $(CC_FlAGS) $(CC_WARNING_FLAGS) -c epd.c -o build/epd.o
@$(CC) $(CC_FlAGS) $(CC_WARNING_FLAGS) -c userinterface.c -o build/userinterface.o
@$(CC) $(CC_FlAGS) $(CC_WARNING_FLAGS) -c main.c -o build/main.o
@$(CC) $(CC_END_FLAGS) $(CC_WARNING_FLAGS) build/main.o build/adler32.o build/crc32.o build/defl_static.o build/genlz77.o build/tinfgzip.o build/tinflate.o build/tinfzlib.o build/compression.o build/zigbee.o build/ccm.o build/chars.o build/drawing.o build/powermgt.o build/syncedproto.o build/comms.o build/settings.o build/timer.o build/util.o build/gpio.o build/nfc.o build/epd.o build/mz100_sleep.o build/core_cm3.o build/mz100_ssp.o build/mz100_wdt.o build/mz100_gpio.o build/mz100_driver.o build/mz100_adc.o build/mz100_flash.o build/mz100_clock.o build/mz100_rtc.o build/mz100_pinmux.o build/mz100_pmu.o build/mz100_qspi.o build/mz100_aes.o build/mz100_gpt.o build/mz100_uart.o build/startup.o -o main.axf
@$(CC) $(CC_END_FLAGS) $(CC_WARNING_FLAGS) build/main.o build/userinterface.o build/printf.o build/mz100_aon_ram.o build/zigbee.o build/chars.o build/drawing.o build/powermgt.o build/syncedproto.o build/comms.o build/settings.o build/timer.o build/util.o build/gpio.o build/epd.o build/mz100_sleep.o build/core_cm3.o build/mz100_ssp.o build/mz100_wdt.o build/mz100_gpio.o build/mz100_driver.o build/mz100_adc.o build/mz100_flash.o build/mz100_clock.o build/mz100_rtc.o build/mz100_pinmux.o build/mz100_pmu.o build/mz100_qspi.o build/mz100_aes.o build/mz100_gpt.o build/mz100_uart.o build/startup.o -o main.axf
@$(OBJCOPY) -v -O binary main.axf main.bin
clean:
@@ -83,4 +81,4 @@ flash_dump:
@$(MZ_FLASHER) $(COMPORT) read dump.bin
create_ota_img:
@$(MZ_FLASHER) img main.bin UPDT0028.BIN
@$(MZ_FLASHER) img main.bin UPDT0028.BIN

View File

@@ -1,96 +1,32 @@
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "comms.h"
#include "proto.h"
#include <stdint.h>
//#include <stdio.h>
#include <string.h>
#include "ccm.h"
#include "proto.h"
extern uint8_t Zigbee_tx_buffer(uint8_t tx_buffer[], int len);
static uint8_t packet[128];
static uint8_t mSeq = 0;
uint8_t mLastLqi = 0;
int8_t mLastRSSI = 0;
uint8_t commsGetLastPacketLQI(void)
{
return mLastLqi;
uint8_t commsGetLastPacketLQI(void) {
return mLastLqi;
}
int8_t commsGetLastPacketRSSI(void)
{
return mLastRSSI;
int8_t commsGetLastPacketRSSI(void) {
return mLastRSSI;
}
static inline void __attribute__((always_inline)) macCopy(uint8_t *restrict dst, const uint8_t *restrict src)
{
((uint32_t *)dst)[0] = ((const uint32_t *)src)[0];
((uint32_t *)dst)[1] = ((const uint32_t *)src)[1];
static inline void __attribute__((always_inline)) macCopy(uint8_t *restrict dst, const uint8_t *restrict src) {
((uint32_t *)dst)[0] = ((const uint32_t *)src)[0];
((uint32_t *)dst)[1] = ((const uint32_t *)src)[1];
}
static inline bool __attribute__((always_inline)) macIsEq(const uint8_t *restrict dst, const uint8_t *restrict src)
{
return ((uint32_t *)dst)[0] == ((const uint32_t *)src)[0] && ((uint32_t *)dst)[1] == ((const uint32_t *)src)[1];
}
bool commsTx(struct CommsInfo *info, bool bcast, const void *packet_in, uint32_t len)
{
uint8_t nonce[AES_CCM_NONCE_SIZE] = {};
struct MacFrameNormal *mfn;
struct MacFrameBcast *mfb;
uint32_t hdrSz;
char *payload;
static const struct MacFcs normalFcs = {
.frameType = FRAME_TYPE_DATA,
.panIdCompressed = 1,
.destAddrType = ADDR_MODE_LONG,
.srcAddrType = ADDR_MODE_LONG,
};
static const struct MacFcs broadcastFcs = {
.frameType = FRAME_TYPE_DATA,
.destAddrType = ADDR_MODE_SHORT,
.srcAddrType = ADDR_MODE_LONG,
};
if (len > COMMS_MAX_PACKET_SZ)
return false;
if (bcast)
{
mfb = (struct MacFrameBcast *)packet;
hdrSz = sizeof(struct MacFrameBcast);
payload = (char *)(mfb + 1);
mfb->fcs = broadcastFcs;
mfb->seq = mSeq++;
mfb->dstPan = 0xffff;
mfb->dstAddr = 0xffff;
mfb->srcPan = PROTO_PAN_ID;
macCopy(mfb->src, info->myMac);
}
else
{
mfn = (struct MacFrameNormal *)packet;
hdrSz = sizeof(struct MacFrameNormal);
payload = (char *)(mfn + 1);
mfn->fcs = normalFcs;
mfn->seq = mSeq++;
mfn->pan = PROTO_PAN_ID;
macCopy(mfn->dst, info->masterMac);
macCopy(mfn->src, info->myMac);
}
*(uint32_t *)nonce = (*info->nextIV)++;
macCopy(nonce + sizeof(uint32_t), info->myMac);
memcpy(payload, packet_in, len);
aesCcmEnc((void *)packet, (void *)packet, hdrSz, len, info->encrKey, nonce);
*(uint32_t *)(payload + len + AES_CCM_MIC_SIZE) = *(uint32_t *)nonce; // send nonce
len += hdrSz;
len += AES_CCM_MIC_SIZE;
len += sizeof(uint32_t);
return !Zigbee_tx_buffer((uint8_t *)&packet, len);
static inline bool __attribute__((always_inline)) macIsEq(const uint8_t *restrict dst, const uint8_t *restrict src) {
return ((uint32_t *)dst)[0] == ((const uint32_t *)src)[0] && ((uint32_t *)dst)[1] == ((const uint32_t *)src)[1];
}
extern volatile uint8_t rx_buffer[0x400];
@@ -98,94 +34,16 @@ extern volatile uint8_t new_rx;
extern volatile uint8_t new_rssi;
extern volatile int rx_len;
int32_t __attribute__((noinline)) commsRx(struct CommsInfo *info, void *data, uint8_t *fromMacP)
{
uint8_t *buf = packet, nonce[13] = {}, fromMac[8] = {0, 0, 0, 0, 0, 0, 0, 0};
uint32_t len, minNeedLen, hdrLen = 0;
struct MacFrameFromMaster *mfm;
struct MacFrameNormal *mfn;
// sort out how many bytes minimum are a valid packet
minNeedLen = sizeof(struct MacFrameFromMaster); // mac header
minNeedLen += sizeof(uint8_t); // packet type
minNeedLen += AES_CCM_MIC_SIZE; // MIC
minNeedLen += sizeof(uint32_t); // nonce counter
minNeedLen += 2 * sizeof(uint8_t); // RSSI/LQI
if (!new_rx)
return COMMS_RX_ERR_NO_PACKETS;
// some basic checks
mfm = (struct MacFrameFromMaster *)rx_buffer;
if (rx_len >= sizeof(packet) || rx_len < minNeedLen || mfm->fcs.frameType != FRAME_TYPE_DATA ||
mfm->fcs.secure || mfm->fcs.frameVer || mfm->fcs.destAddrType != ADDR_MODE_LONG || !mfm->fcs.panIdCompressed ||
(mfm->fcs.srcAddrType != ADDR_MODE_LONG && mfm->fcs.srcAddrType != ADDR_MODE_SHORT) ||
mfm->pan != PROTO_PAN_ID || !macIsEq(mfm->dst, info->myMac))
{
new_rx = 0;
return COMMS_RX_ERR_INVALID_PACKET;
}
// copy out and release buffer
memcpy(buf, &rx_buffer, len = rx_len - 2 * sizeof(uint8_t));
mLastLqi = rx_buffer[len + 0];
mLastRSSI = rx_buffer[len + 1];
mfm = (struct MacFrameFromMaster *)buf;
mfn = (struct MacFrameNormal *)buf;
new_rx = 0;
// sort out header len, copy mac into nonce
if (mfm->fcs.srcAddrType == ADDR_MODE_LONG)
{
macCopy(fromMac, mfn->src);
hdrLen = sizeof(struct MacFrameNormal);
// re-verify needed length
minNeedLen -= sizeof(struct MacFrameFromMaster);
minNeedLen += sizeof(struct MacFrameNormal);
if (rx_len < minNeedLen)
return COMMS_RX_ERR_INVALID_PACKET;
}
else if (mfm->fcs.srcAddrType == ADDR_MODE_SHORT)
{
macCopy(fromMac, info->masterMac);
hdrLen = sizeof(struct MacFrameFromMaster);
}
// sort out the nonce
macCopy(nonce + sizeof(uint32_t), fromMac);
*(uint32_t *)nonce = *(uint32_t *)(buf + len - sizeof(uint32_t));
// decrypt and auth
len -= hdrLen + AES_CCM_MIC_SIZE + sizeof(uint32_t);
if (!aesCcmDec(buf, buf, hdrLen, len, info->encrKey, nonce))
return COMMS_RX_ERR_MIC_FAIL;
if (fromMacP)
macCopy(fromMacP, fromMac);
memcpy(data, buf + hdrLen, len);
return len;
int32_t __attribute__((noinline)) commsRxUnenc(void *data) {
if (!new_rx)
return COMMS_RX_ERR_NO_PACKETS;
memcpy(data, (uint8_t*)&rx_buffer, rx_len);
mLastLqi = 255 - new_rssi;
mLastRSSI = new_rssi;
new_rx = 0;
return rx_len;
}
int32_t __attribute__((noinline)) commsRxUnenc(void *data)
{
if (!new_rx)
return COMMS_RX_ERR_NO_PACKETS;
memcpy(data, &rx_buffer, rx_len);
mLastLqi = 255 - new_rssi;
mLastRSSI = new_rssi;
new_rx = 0;
return rx_len;
}
void commsTxNoCpy(uint8_t *packetp)
{
Zigbee_tx_buffer((uint8_t *)&packetp[1], (packetp[0] - 2));
void commsTxNoCpy(uint8_t *packetp) {
Zigbee_tx_buffer((uint8_t *)&packetp[1], (packetp[0] - 2));
}

View File

@@ -4,12 +4,6 @@
#include <stdint.h>
#include "ccm.h"
struct CommsInfo {
const uint8_t *myMac;
const uint8_t *masterMac;
const void *encrKey;
uint32_t *nextIV;
};
extern uint8_t mLastLqi;
extern int8_t mLastRSSI;
@@ -23,8 +17,6 @@ extern int8_t mLastRSSI;
#define COMMS_MAX_PACKET_SZ (127 /* max phy len */ - 21 /* max mac frame with panID compression */ - 2 /* FCS len */ - AES_CCM_MIC_SIZE - COMMS_IV_SIZE)
bool commsTx(struct CommsInfo *info, bool bcast, const void *packet, uint32_t len);
int32_t commsRx(struct CommsInfo *info, void *data, uint8_t *fromMac); //returns length or COMMS_RX_ERR_*
uint8_t commsGetLastPacketLQI(void);
int8_t commsGetLastPacketRSSI(void);

View File

@@ -1,7 +1,7 @@
#include "compression.h"
#include "uzlib/src/uzlib.h"
#include <stdio.h>
//#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>

View File

@@ -1,7 +1,7 @@
#pragma once
#include "uzlib/src/uzlib.h"
#include <stdio.h>
//#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>

View File

@@ -2,6 +2,7 @@
#include <stdbool.h>
#include <stdbool.h>
#include "printf.h"
#include "board.h"
#include "eeprom.h"
@@ -11,366 +12,22 @@
#include "util.h"
#include "epd.h"
#define COMPRESSION_BITPACKED_3x5_to_7 0x62700357 // 3 pixels of 5 possible colors in 7 bits
#define COMPRESSION_BITPACKED_5x3_to_8 0x62700538 // 5 pixels of 3 possible colors in 8 bits
#define COMPRESSION_BITPACKED_3x6_to_8 0x62700368 // 3 pixels of 6 possible colors in 8 bits
struct BitmapFileHeader
{
uint8_t sig[2];
uint32_t fileSz;
uint8_t rfu[4];
uint32_t dataOfst;
uint32_t headerSz; // 40
int32_t width;
int32_t height;
uint16_t colorplanes; // must be one
uint16_t bpp;
uint32_t compression;
uint32_t dataLen; // may be 0
uint32_t pixelsPerMeterX;
uint32_t pixelsPerMeterY;
uint32_t numColors; // if zero, assume 2^bpp
uint32_t numImportantColors;
};
struct BitmapClutEntry
{
uint8_t b, g, r, x;
};
struct BitmapDrawInfo
{
// dimensions
uint16_t w, h, effectiveW, effectiveH, stride /* 0 -> 1, 5 - >7, 255 -> 256 */;
uint8_t numColorsM1;
// data start
uint32_t dataAddr;
// compression state
uint8_t packetPixelDivVal;
uint8_t packetNumPixels;
uint8_t packetBitSz;
uint8_t packetBitMask; // derived from the above
// flags
uint8_t bpp : 4;
uint8_t bottomUp : 1;
};
uint8_t mPassNo = 0;
static const uint8_t mColorMap[][6] = {
// colors are: B, DG, G, LG, W, R
// phase 0 (LUTS: B:W:R:G, purpose: BWR, prepare greys)
{1, 1, 1, 1, 0, 0}, // lo plane (B)
{0, 0, 0, 0, 0, 1} // hi plane (R)
};
static uint8_t mClutMap[256];
static uint8_t mClutMapRed[256];
static struct BitmapDrawInfo mDrawInfo;
static uint32_t drawPrvParseHeader(uint32_t addr) // return clut addr or zero on error
{
/*struct BitmapFileHeader bmph;
uint16_t packetsPerRow;
addr += sizeof(struct EepromImageHeader);
eepromRead(addr, &bmph, sizeof(bmph));
if (bmph.sig[0] != 'B' || bmph.sig[1] != 'M')
goto fail;
if (bmph.colorplanes != 1)
goto fail;
if ((&bmph.headerSz - 40)) // < 40
goto fail;
if (bmph.bpp > 8)
goto fail;
mDrawInfo.bpp = bmph.bpp;
if (!(&bmph.headerSz - 257)) // >= 257
goto fail;
if ((&bmph.numColors))
mDrawInfo.numColorsM1 = (uint8_t)bmph.numColors - (uint8_t)1;
else
mDrawInfo.numColorsM1 = (uint8_t)((uint8_t)1 << (uint8_t)mDrawInfo.bpp) - (uint8_t)1;
if (!(&bmph.height))
goto fail;
if ((&bmph.width - 1) || !(&bmph.width - 0xffff))
goto fail;
mDrawInfo.w = bmph.width;
if ((&bmph.height) < 0)
{
if ((&bmph.height + 0xffff)) // carries if val too negative
goto fail;
mDrawInfo.h = -bmph.height;
mDrawInfo.bottomUp = false;
}
else
{
if (!(&bmph.headerSz - 0xffff)) // no carry if val too big
goto fail;
mDrawInfo.h = bmph.height;
mDrawInfo.bottomUp = true;
}
if (bmph.compression)
{
printf("compression is not supported ;(");
goto fail;
}
mDrawInfo.packetPixelDivVal = 0;
mDrawInfo.packetNumPixels = 1;
if (mDrawInfo.bpp > 1)
{
mDrawInfo.packetBitSz = 2;
}
else
{
mDrawInfo.packetBitSz = 1; // mDrawInfo.bpp;
}
// mDrawInfo.stride = mathPrvDiv32x8(mathPrvMul16x8((mDrawInfo.w + mDrawInfo.packetNumPixels - 1), mDrawInfo.packetBitSz) + 31, 32) * 4UL;
// mDrawInfo.packetBitMask = (uint8_t)(((uint8_t)1) << (uint8_t)mDrawInfo.packetBitSz) - (uint8_t)1;
packetsPerRow = (mDrawInfo.w + mDrawInfo.packetNumPixels - 1) / (mDrawInfo.packetNumPixels);
mDrawInfo.stride = (((packetsPerRow * mDrawInfo.packetBitSz) + 31) / 32) * 4UL;
mDrawInfo.packetBitMask = (uint8_t)(((uint8_t)1) << (uint8_t)mDrawInfo.packetBitSz) - (uint8_t)1;
// calc effective size
mDrawInfo.effectiveH = (mDrawInfo.h > SCREEN_HEIGHT) ? SCREEN_HEIGHT : mDrawInfo.h;
mDrawInfo.effectiveW = (mDrawInfo.w > SCREEN_WIDTH) ? SCREEN_WIDTH : mDrawInfo.w;
// calc addrs
mDrawInfo.dataAddr = addr + bmph.dataOfst;
return addr + bmph.dataOfst - sizeof(struct BitmapClutEntry) * (1 + mDrawInfo.numColorsM1);
fail:
printf("Tried to parse the bmp header, didn't work...");*/
return 0;
}
static void drawPrvLoadAndMapClut(uint32_t clutAddr)
{
/*struct BitmapClutEntry clut;
uint8_t i;
// convert clut to our understanding of color
i = 0;
do
{
uint8_t entry;
eepromRead(clutAddr, &clut, sizeof(clut));
clutAddr += sizeof(struct BitmapClutEntry);
if (SCREEN_EXTRA_COLOR_INDEX >= 0 && clut.r == 0xff && (clut.g == 0xff || clut.g == 0) && clut.b == 0) // yellow/red
entry = SCREEN_EXTRA_COLOR_INDEX;
else
{
uint16_t intensity = 0;
intensity += (0x37 * clut.r);
intensity += (0xB7 * clut.g);
intensity += (0x12 * clut.b);
// adds up to 0xff00 -> fix it
intensity += (uint8_t)(intensity >> 8);
entry = (intensity * SCREEN_NUM_GREYS) >> 16;
entry += SCREEN_FIRST_GREY_IDX;
}
// printf("mapped clut %u (%d %d %d) -> %d\n", i, clut.r, clut.g, clut.b, entry);
mClutMap[i] = entry;
} while (i++ != mDrawInfo.numColorsM1);
// replicate clut down if not a full 256-entry clut
if (mDrawInfo.bpp != 8)
{
uint8_t num = (uint8_t)((uint8_t)1 << (uint8_t)mDrawInfo.bpp);
// we can use the fact that our memcpy always copies forward
memcpy(mClutMap + num, mClutMap, (uint8_t)256 - (uint8_t)num);
}*/
}
static void drawPrvDecodeImageOnce(void)
{
/*uint8_t rowBuf[SCREEN_WIDTH];
uint16_t er, c;
if (mDrawInfo.bottomUp)
er = mDrawInfo.effectiveH - 1;
else
er = 0;
while (1)
{ // we account differently for loop gets compiled worse
uint8_t inIdx = 0, bitpoolInUsed = 0, bitpoolIn = 0;
uint16_t nBytesOut = 0;
#if SCREEN_TX_BPP == 4
uint8_t txPrev = 0;
bool emit = false;
#else
uint8_t bitpoolOutUsedUsed = 0;
uint16_t bitpoolOut = 0;
#endif
// get a row
epdDeselect();
eepromRead((er * mDrawInfo.stride) + mDrawInfo.dataAddr, rowBuf, mDrawInfo.stride);
epdSelect();
// convert to our format
c = mDrawInfo.effectiveW;
do
{
// uartTx('.');
uint8_t packet, packetIdx, packetMembers = mDrawInfo.packetNumPixels;
if (bitpoolInUsed >= mDrawInfo.packetBitSz)
{
bitpoolInUsed -= mDrawInfo.packetBitSz;
packet = bitpoolIn >> bitpoolInUsed;
}
else
{
uint8_t packetBitSz = mDrawInfo.packetBitSz;
uint8_t t = rowBuf[inIdx++];
packet = (bitpoolIn << (packetBitSz - bitpoolInUsed)) | (t >> (8 - (packetBitSz - bitpoolInUsed)));
bitpoolInUsed += 8 - packetBitSz;
bitpoolIn = t;
}
packet &= mDrawInfo.packetBitMask;
// val is now a packet - unpack it
if (packetMembers > c)
packetMembers = c;
for (packetIdx = 0; packetIdx < packetMembers; packetIdx++)
{
uint8_t val;
// extract
if (mDrawInfo.packetPixelDivVal)
{
val = packet % mDrawInfo.packetPixelDivVal;
packet /= mDrawInfo.packetPixelDivVal;
}
else
val = packet;
// map
val = mClutMap[val];
// get bits out
#if SCREEN_TX_BPP == 4
if (emit)
{
emit = false;
ByteDecode(txPrev | val);
nBytesOut++;
txPrev = 0;
}
else
{
emit = true;
txPrev = val << 4;
}
#else
bitpoolOut <<= SCREEN_TX_BPP;
bitpoolOut |= val;
bitpoolOutUsedUsed += SCREEN_TX_BPP;
if (bitpoolOutUsedUsed >= 8)
{
ByteDecode(bitpoolOut >> (bitpoolOutUsedUsed -= 8));
bitpoolOut &= (1 << bitpoolOutUsedUsed) - 1;
nBytesOut++;
}
#endif
}
c -= packetMembers;
} while (c);
#if SCREEN_TX_BPP == 4
if (emit)
{
ByteDecode(txPrev);
nBytesOut++;
}
#else
if (bitpoolOutUsedUsed)
{
ByteDecode(bitpoolOut);
nBytesOut++;
}
#endif
// if we did not produce enough bytes, do so
nBytesOut = ((long)SCREEN_WIDTH * SCREEN_TX_BPP + 7) / 8 - nBytesOut;
while (nBytesOut--)
ByteDecode(SCREEN_BYTE_FILL);
// update row
if (mDrawInfo.bottomUp)
{
if (er)
er--;
else
break;
}
else
{
er++;
if (er == mDrawInfo.effectiveH)
break;
}
}
// fill the rest of the screen
for (er = mDrawInfo.effectiveH - SCREEN_HEIGHT; er; er--)
{
for (c = ((long)SCREEN_WIDTH * SCREEN_TX_BPP + 7) / 8; c; c--)
{
ByteDecode(SCREEN_BYTE_FILL);
}
}*/
}
static uint8_t prev, step = 0;
void ByteDecode(uint8_t byte)
{
/*prev <<= 2;
prev |= (mColorMap[mPassNo][byte >> 4] << 1) | mColorMap[mPassNo][byte & 0x0f];
if (++step == 4)
{
step = 0;
Display_Write_byte(prev);
}*/
}
void drawImageAtAddress(uint32_t addr, uint8_t lut)
{
struct EepromImageHeader *eih = (struct EepromImageHeader *)mClutMap;
eepromRead(addr, mClutMap, sizeof(struct EepromImageHeader));
uint8_t prevVal = 0;
switch (eih->dataType)
{
case DATATYPE_IMG_RAW_1BPP:
@@ -430,34 +87,6 @@ void drawImageAtAddress(uint32_t addr, uint8_t lut)
display_send_stop();
epd_refresh_and_sleep();
break;
case DATATYPE_IMG_BMP:;
uint32_t clutAddr;
printf("sending BMP to EPD - ");
/*clutAddr = drawPrvParseHeader(addr);
if (!clutAddr)
return;
drawPrvLoadAndMapClut(clutAddr);
epdSetup();
if (lut)
selectLUT(lut);
mPassNo = 0;
beginFullscreenImage();
beginWriteFramebuffer(EPD_COLOR_BLACK);
prev = 0;
step = 0;
drawPrvDecodeImageOnce();
endWriteFramebuffer();
mPassNo++;
beginFullscreenImage();
beginWriteFramebuffer(EPD_COLOR_RED);
prev = 0;
step = 0;
drawPrvDecodeImageOnce();
endWriteFramebuffer();*/
printf(" complete.\n");
break;
default: // prevent drawing from an unknown file image type
printf("Image with type 0x%02X was requested, but we don't know what to do with that currently...\n", eih->dataType);
return;

1110
ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/epd.c Normal file → Executable file

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,8 @@
#pragma once
#include <stdio.h>
//#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#define DISPLAY_WIDTH (640)
#define DISPLAY_HEIGHT (384)
@@ -16,6 +17,18 @@
#define FORE_COLOR_2 4
#define FORE_COLOR_3 0
#define EPD_LUT_DEFAULT 0
#define EPD_LUT_NO_REPEATS 1
#define EPD_LUT_FAST_NO_REDS 2
#define EPD_LUT_FAST 3
#define EPD_DIRECTION_X false
#define EPD_DIRECTION_Y true
#define EPD_SIZE_SINGLE false
#define EPD_SIZE_DOUBLE true
#define EPD_COLOR_RED true
#define EPD_COLOR_BLACK false
void init_GPIO_EPD();
void display_send_buffer();
@@ -25,5 +38,16 @@ void display_tx_byte(uint8_t data);
void display_send_start(uint8_t inverted);
void display_send_stop();
void setDisplayWindow(uint16_t x, uint16_t y, uint16_t xe, uint16_t ye);
void init_epd();
void refresh_epd();
void refresh_epd();
void lutBeginTX(uint8_t reg);
void lutEndTX();
void epd_pin_enable(int a1);
void fillWindow(uint16_t x, uint16_t y, uint16_t xe, uint16_t ye, uint8_t color);
void epdPrintf(uint16_t x, uint16_t y, bool color, const char* c, ...);

View File

@@ -1,6 +1,6 @@
#include "nfc.h"
#include <stdio.h>
//#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
@@ -12,6 +12,8 @@
#include "mz100_pinmux.h"
#include "mz100_gpio.h"
#include "util.h"
#include "printf.h"
void NVIC_some_IRQ1(unsigned int a1)
{

898
ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/main.c Normal file → Executable file

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
#pragma once
#include <stdio.h>
//#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>

View File

@@ -4,8 +4,9 @@ GROUP(-lgcc -lc -lnosys)
MEMORY
{
FLASH (rx) : ORIGIN = 0x100000, LENGTH = 80k
RAM (rwx) : ORIGIN = 0x20100000 + 80k, LENGTH = 160k - 80k - 16k
RAM1 (rwx) : ORIGIN = 0x20124000 , LENGTH = 16k
RAM (rwx) : ORIGIN = 0x20100000 + 80k, LENGTH = 160k - 80k - 2k
AONSHADOW (rwx) : ORIGIN = 0x20128000 - 2k, LENGTH = 2k
AON (rwx) : ORIGIN = 0x20130000 , LENGTH = 4k
}
ENTRY(Reset_Handler)
@@ -123,6 +124,27 @@ SECTIONS
/* Check if data + heap + stack exceeds RAM limit */
ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed with stack")
.aon (NOLOAD):
{
. = ALIGN(4);
_start_of_aon = .;
*(.aon)
*(.aon.*)
. = ALIGN(4);
_end_of_aon = .;
} > AON
.aonshadow (NOLOAD):
{
. = ALIGN(4);
_start_of_aonshadow = .;
*(.aonshadow)
*(.aonshadow.*)
. = ALIGN(4);
_end_of_aonshadow = .;
} > AONSHADOW
}

View File

@@ -0,0 +1,43 @@
#include "mz100_aon_ram.h"
#include <string.h>
#include "stdint.h"
__attribute__((section(".aon"))) volatile uint32_t aonChecksum;
__attribute__((section(".aon"))) volatile uint8_t aonShadow[AONSHADOW_SIZE];
bool aonRamValid = false;
void clearAonRam() {
memset((void *)0x130000, 0, 4096);
}
bool loadValidateAonRam() {
uint32_t testchecksum = aonChecksum;
aonChecksum = 0x00000000;
uint32_t checksum = 0xABBA5FF5;
for (uint32_t c = 0x130000; c < 0x131000; c += 4) {
checksum += *(uint32_t *)c;
}
if (checksum == testchecksum) {
// immediately invalidate the checksum; if we reboot, we want a clean reboot
aonChecksum = 0x5445A00A;
memcpy((void *)(0x128000 - AONSHADOW_SIZE), (uint8_t*)aonShadow, AONSHADOW_SIZE);
return true;
} else {
clearAonRam();
memset((void *)(0x128000 - AONSHADOW_SIZE), 0, AONSHADOW_SIZE);
return false;
}
}
void saveAonRam() {
memcpy((uint8_t*)aonShadow, (void *)(0x128000 - AONSHADOW_SIZE), AONSHADOW_SIZE);
aonChecksum = 0x00000000;
uint32_t checksum = 0xABBA5FF5;
for (uint32_t c = 0x130000; c < 0x131000; c += 4) {
checksum += *(uint32_t *)c;
}
aonChecksum = checksum;
}

View File

@@ -0,0 +1,11 @@
#include "stdint.h"
#include "stdbool.h"
#define AONSHADOW_SIZE 2048
extern bool aonRamValid;
bool loadValidateAonRam();
void saveAonRam();
void clearAonRam();

View File

@@ -1,4 +1,4 @@
#include <stdio.h>
//#include <stdio.h>
#include <stdint.h>
#include "core_cm3.h"
#include "mz100_gpio.h"
@@ -14,6 +14,9 @@
#include "gpio.h"
#include "main.h"
#include "proto.h"
#include "printf.h"
extern void saveAonRam();
void AON_level_VDD(int state)
{
@@ -90,9 +93,11 @@ extern struct AvailDataInfo curDataInfo; // last 'AvailDataInfo' we received fro
extern bool requestPartialBlock; // if we should ask the AP to get this block from the host or not
void sleep_with_with_wakeup(uint32_t sleep_time_ms)
{
memcpy((uint8_t *)&(*(volatile unsigned int *)0x130500), (uint8_t *)&curBlock, sizeof(struct blockRequest));
memcpy((uint8_t *)&(*(volatile unsigned int *)0x130600), (uint8_t *)&curDataInfo, sizeof(struct AvailDataInfo));
printf("sleep: %u\r\n", sleep_time_ms);
saveAonRam();
//memcpy((uint8_t *)&(*(volatile unsigned int *)0x130500), (uint8_t *)&curBlock, sizeof(struct blockRequest));
//memcpy((uint8_t *)&(*(volatile unsigned int *)0x130600), (uint8_t *)&curDataInfo, sizeof(struct AvailDataInfo));
//sleep_time_ms = 10000;
printf("sleep! %u\n", sleep_time_ms);
uint32_t sleep_time_ms_1;
AON_level_VDD(7);
AON_level_VAA(0);

View File

@@ -1,6 +1,6 @@
#pragma once
#include <stdio.h>
//#include <stdio.h>
#include <stdint.h>
#include "core_cm3.h"
#include "mz100_gpio.h"

View File

@@ -1,5 +1,5 @@
#include "nfc.h"
#include <stdio.h>
//#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
@@ -17,6 +17,8 @@
#include "timer.h"
#include "epd.h"
#include "proto.h"
#include "printf.h"
#define WHO_AM_I 0x04
uint8_t i2c_address = 0xAA;

View File

@@ -1,5 +1,5 @@
#pragma once
#include <stdio.h>
//#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>

143
ARM_Tag_FW/88MZ100_OpenEpaperLink_7.4/powermgt.c Normal file → Executable file
View File

@@ -3,64 +3,69 @@
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
// #include <stdio.h>
#include <string.h>
#include "mz100_sleep.h"
#include "zigbee.h"
#include "eeprom.h"
#include "board.h"
#include "screen.h"
#include "eeprom.h"
#include "main.h"
#include "util.h"
#include "mz100_sleep.h"
#include "printf.h"
#include "screen.h"
#include "syncedproto.h"
#include <stdbool.h>
#include "util.h"
#include "zigbee.h"
uint16_t dataReqAttemptArr[POWER_SAVING_SMOOTHING] = {0}; // Holds the amount of attempts required per data_req/check-in
uint8_t dataReqAttemptArrayIndex = 0;
uint8_t dataReqLastAttempt = 0;
uint16_t nextCheckInFromAP = 0;
uint8_t wakeUpReason = 0;
uint8_t scanAttempts = 0;
__attribute__((section(".aonshadow"))) uint16_t dataReqAttemptArr[POWER_SAVING_SMOOTHING] = {0}; // Holds the amount of attempts required per data_req/check-in
__attribute__((section(".aonshadow"))) uint8_t dataReqAttemptArrayIndex = 0;
__attribute__((section(".aonshadow"))) uint8_t dataReqLastAttempt = 0;
__attribute__((section(".aonshadow"))) uint16_t nextCheckInFromAP = 0;
__attribute__((section(".aonshadow"))) uint8_t wakeUpReason = 0;
__attribute__((section(".aonshadow"))) uint8_t scanAttempts = 0;
int8_t temperature = 0;
uint16_t batteryVoltage = 0;
bool lowBattery = false;
uint16_t longDataReqCounter = 0;
uint16_t voltageCheckCounter = 0;
__attribute__((section(".aonshadow"))) int8_t temperature = 0;
__attribute__((section(".aonshadow"))) uint16_t batteryVoltage = 0;
__attribute__((section(".aonshadow"))) bool lowBattery = false;
__attribute__((section(".aonshadow"))) uint16_t longDataReqCounter = 0;
__attribute__((section(".aonshadow"))) uint16_t voltageCheckCounter = 0;
uint8_t capabilities = 0;
__attribute__((section(".aonshadow"))) uint8_t capabilities = 0;
bool spiActive = false;
bool uartActive = false;
bool eepromActive = false;
bool i2cActive = false;
extern int8_t adcSampleTemperature(void); // in degrees C
uint8_t checkButtonOrJig()
{
extern int8_t adcSampleTemperature(void); // in degrees C
uint8_t checkButtonOrJig() {
return DETECT_P1_0_NOTHING;
}
void setupPortsInitial()
{
void setupPortsInitial() {
}
void initPowerSaving(const uint16_t initialValue)
{
for (uint8_t c = 0; c < POWER_SAVING_SMOOTHING; c++)
{
uint16_t doVoltageReading() {
batteryVoltage = (uint16_t)measureBattery();
if (batteryVoltage < BATTERY_VOLTAGE_MINIMUM) {
lowBattery = true;
} else {
lowBattery = false;
}
return batteryVoltage;
}
void initPowerSaving(const uint16_t initialValue) {
for (uint8_t c = 0; c < POWER_SAVING_SMOOTHING; c++) {
dataReqAttemptArr[c] = initialValue;
}
}
static void configSPI(const bool setup)
{
static void configSPI(const bool setup) {
spiActive = setup;
}
static void configUART(const bool setup)
{
static void configUART(const bool setup) {
/* if (setup == uartActive)
return;
uartActive = setup;
@@ -70,69 +75,64 @@ static void configUART(const bool setup)
Serial.end();*/
}
static void configEEPROM(const bool setup)
{
static void configEEPROM(const bool setup) {
}
static void configI2C(const bool setup)
{
static void configI2C(const bool setup) {
}
void powerUp(const uint8_t parts)
{
printf("Power up: %d\r\n", parts);
if (parts & INIT_RADIO)
{
void powerUp(const uint8_t parts) {
// printf("Power up: %d\r\n", parts);
if (parts & INIT_RADIO) {
radioInit();
// radioRxFilterCfg(mSelfMac, 0x10000, PROTO_PAN_ID);
// radioSetTxPower(10);
if (currentChannel >= 11 && currentChannel <= 27)
{
if (currentChannel >= 11 && currentChannel <= 27) {
radioSetChannel(currentChannel);
}
else
{
} else {
radioSetChannel(RADIO_FIRST_CHANNEL);
}
}
if (parts & INIT_UART) {
configUART(true);
}
if (parts & INIT_EPD) {
configSPI(true);
}
if (parts & INIT_EEPROM) {
configEEPROM(true);
}
if (parts & INIT_I2C) {
configI2C(true);
}
}
void powerDown(const uint8_t parts)
{
printf("Power down: %d\r\n", parts);
void powerDown(const uint8_t parts) {
// printf("Power down: %d\r\n", parts);
}
void doSleep(const uint32_t t)
{
void doSleep(const uint32_t t) {
printf("Sleeping for: %d ms\r\n", t);
// sleepForMs(t);
delay(t);
}
uint32_t getNextScanSleep(const bool increment)
{
if (increment)
{
uint32_t getNextScanSleep(const bool increment) {
if (increment) {
if (scanAttempts < 255)
scanAttempts++;
}
if (scanAttempts < INTERVAL_1_ATTEMPTS)
{
if (scanAttempts < INTERVAL_1_ATTEMPTS) {
return INTERVAL_1_TIME;
}
else if (scanAttempts < (INTERVAL_1_ATTEMPTS + INTERVAL_2_ATTEMPTS))
{
} else if (scanAttempts < (INTERVAL_1_ATTEMPTS + INTERVAL_2_ATTEMPTS)) {
return INTERVAL_2_TIME;
}
else
{
} else {
return INTERVAL_3_TIME;
}
}
void addAverageValue()
{
void addAverageValue() {
uint16_t curval = INTERVAL_AT_MAX_ATTEMPTS - INTERVAL_BASE;
curval *= dataReqLastAttempt;
curval /= DATA_REQ_MAX_ATTEMPTS;
@@ -141,14 +141,11 @@ void addAverageValue()
dataReqAttemptArrayIndex++;
}
uint16_t getNextSleep()
{
/*uint16_t avg = 0;
for (uint8_t c = 0; c < POWER_SAVING_SMOOTHING; c++)
{
uint16_t getNextSleep() {
uint16_t avg = 0;
for (uint8_t c = 0; c < POWER_SAVING_SMOOTHING; c++) {
avg += dataReqAttemptArr[c];
}
avg /= POWER_SAVING_SMOOTHING;
return avg;*/
return 30;
return avg;
}

View File

@@ -60,20 +60,22 @@ extern void doSleep(const uint32_t t);
extern void addAverageValue();
extern uint16_t getNextSleep();
extern uint16_t doVoltageReading();
extern uint32_t getNextScanSleep(const bool increment);
extern void initPowerSaving(const uint16_t initialValue);
extern uint8_t wakeUpReason;
extern uint8_t capabilities;
extern __attribute__((section(".aonshadow"))) uint8_t capabilities;
extern uint16_t nextCheckInFromAP;
extern uint8_t dataReqLastAttempt;
extern int8_t temperature;
extern uint16_t batteryVoltage;
extern bool lowBattery;
extern uint8_t scanAttempts;
extern uint16_t longDataReqCounter;
extern uint16_t voltageCheckCounter;
extern __attribute__((section(".aonshadow"))) uint8_t dataReqLastAttempt;
extern __attribute__((section(".aonshadow"))) int8_t temperature;
extern __attribute__((section(".aonshadow"))) uint16_t batteryVoltage;
extern __attribute__((section(".aonshadow"))) bool lowBattery;
extern __attribute__((section(".aonshadow"))) uint8_t scanAttempts;
extern __attribute__((section(".aonshadow"))) uint16_t longDataReqCounter;
extern __attribute__((section(".aonshadow"))) uint16_t voltageCheckCounter;
#endif

View File

@@ -0,0 +1,914 @@
///////////////////////////////////////////////////////////////////////////////
// \author (c) Marco Paland (info@paland.com)
// 2014-2019, PALANDesign Hannover, Germany
//
// \license The MIT License (MIT)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// \brief Tiny printf, sprintf and (v)snprintf implementation, optimized for speed on
// embedded systems with a very limited resources. These routines are thread
// safe and reentrant!
// Use this instead of the bloated standard/newlib printf cause these use
// malloc for printf (and may not be thread safe).
//
///////////////////////////////////////////////////////////////////////////////
#include <stdbool.h>
#include <stdint.h>
#include "printf.h"
// define this globally (e.g. gcc -DPRINTF_INCLUDE_CONFIG_H ...) to include the
// printf_config.h header file
// default: undefined
#ifdef PRINTF_INCLUDE_CONFIG_H
#include "printf_config.h"
#endif
// 'ntoa' conversion buffer size, this must be big enough to hold one converted
// numeric number including padded zeros (dynamically created on stack)
// default: 32 byte
#ifndef PRINTF_NTOA_BUFFER_SIZE
#define PRINTF_NTOA_BUFFER_SIZE 32U
#endif
// 'ftoa' conversion buffer size, this must be big enough to hold one converted
// float number including padded zeros (dynamically created on stack)
// default: 32 byte
#ifndef PRINTF_FTOA_BUFFER_SIZE
#define PRINTF_FTOA_BUFFER_SIZE 32U
#endif
// support for the floating point type (%f)
// default: activated
#ifndef PRINTF_DISABLE_SUPPORT_FLOAT
#define PRINTF_SUPPORT_FLOAT
#endif
// support for exponential floating point notation (%e/%g)
// default: activated
#ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL
#define PRINTF_SUPPORT_EXPONENTIAL
#endif
// define the default floating point precision
// default: 6 digits
#ifndef PRINTF_DEFAULT_FLOAT_PRECISION
#define PRINTF_DEFAULT_FLOAT_PRECISION 6U
#endif
// define the largest float suitable to print with %f
// default: 1e9
#ifndef PRINTF_MAX_FLOAT
#define PRINTF_MAX_FLOAT 1e9
#endif
// support for the long long types (%llu or %p)
// default: activated
#ifndef PRINTF_DISABLE_SUPPORT_LONG_LONG
#define PRINTF_SUPPORT_LONG_LONG
#endif
// support for the ptrdiff_t type (%t)
// ptrdiff_t is normally defined in <stddef.h> as long or long long type
// default: activated
#ifndef PRINTF_DISABLE_SUPPORT_PTRDIFF_T
#define PRINTF_SUPPORT_PTRDIFF_T
#endif
///////////////////////////////////////////////////////////////////////////////
// internal flag definitions
#define FLAGS_ZEROPAD (1U << 0U)
#define FLAGS_LEFT (1U << 1U)
#define FLAGS_PLUS (1U << 2U)
#define FLAGS_SPACE (1U << 3U)
#define FLAGS_HASH (1U << 4U)
#define FLAGS_UPPERCASE (1U << 5U)
#define FLAGS_CHAR (1U << 6U)
#define FLAGS_SHORT (1U << 7U)
#define FLAGS_LONG (1U << 8U)
#define FLAGS_LONG_LONG (1U << 9U)
#define FLAGS_PRECISION (1U << 10U)
#define FLAGS_ADAPT_EXP (1U << 11U)
// import float.h for DBL_MAX
#if defined(PRINTF_SUPPORT_FLOAT)
#include <float.h>
#endif
// output function type
typedef void (*out_fct_type)(char character, void* buffer, size_t idx, size_t maxlen);
// wrapper (used as buffer) for output function type
typedef struct {
void (*fct)(char character, void* arg);
void* arg;
} out_fct_wrap_type;
// internal buffer output
static inline void _out_buffer(char character, void* buffer, size_t idx, size_t maxlen)
{
if (idx < maxlen) {
((char*)buffer)[idx] = character;
}
}
// internal null output
static inline void _out_null(char character, void* buffer, size_t idx, size_t maxlen)
{
(void)character; (void)buffer; (void)idx; (void)maxlen;
}
// internal _putchar wrapper
static inline void _out_char(char character, void* buffer, size_t idx, size_t maxlen)
{
(void)buffer; (void)idx; (void)maxlen;
if (character) {
_putchar(character);
}
}
// internal output function wrapper
static inline void _out_fct(char character, void* buffer, size_t idx, size_t maxlen)
{
(void)idx; (void)maxlen;
if (character) {
// buffer is the output fct pointer
((out_fct_wrap_type*)buffer)->fct(character, ((out_fct_wrap_type*)buffer)->arg);
}
}
// internal secure strlen
// \return The length of the string (excluding the terminating 0) limited by 'maxsize'
static inline unsigned int _strnlen_s(const char* str, size_t maxsize)
{
const char* s;
for (s = str; *s && maxsize--; ++s);
return (unsigned int)(s - str);
}
// internal test if char is a digit (0-9)
// \return true if char is a digit
static inline bool _is_digit(char ch)
{
return (ch >= '0') && (ch <= '9');
}
// internal ASCII string to unsigned int conversion
static unsigned int _atoi(const char** str)
{
unsigned int i = 0U;
while (_is_digit(**str)) {
i = i * 10U + (unsigned int)(*((*str)++) - '0');
}
return i;
}
// output the specified string in reverse, taking care of any zero-padding
static size_t _out_rev(out_fct_type out, char* buffer, size_t idx, size_t maxlen, const char* buf, size_t len, unsigned int width, unsigned int flags)
{
const size_t start_idx = idx;
// pad spaces up to given width
if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) {
for (size_t i = len; i < width; i++) {
out(' ', buffer, idx++, maxlen);
}
}
// reverse string
while (len) {
out(buf[--len], buffer, idx++, maxlen);
}
// append pad spaces up to given width
if (flags & FLAGS_LEFT) {
while (idx - start_idx < width) {
out(' ', buffer, idx++, maxlen);
}
}
return idx;
}
// internal itoa format
static size_t _ntoa_format(out_fct_type out, char* buffer, size_t idx, size_t maxlen, char* buf, size_t len, bool negative, unsigned int base, unsigned int prec, unsigned int width, unsigned int flags)
{
// pad leading zeros
if (!(flags & FLAGS_LEFT)) {
if (width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) {
width--;
}
while ((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
buf[len++] = '0';
}
while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
buf[len++] = '0';
}
}
// handle hash
if (flags & FLAGS_HASH) {
if (!(flags & FLAGS_PRECISION) && len && ((len == prec) || (len == width))) {
len--;
if (len && (base == 16U)) {
len--;
}
}
if ((base == 16U) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
buf[len++] = 'x';
}
else if ((base == 16U) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
buf[len++] = 'X';
}
else if ((base == 2U) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
buf[len++] = 'b';
}
if (len < PRINTF_NTOA_BUFFER_SIZE) {
buf[len++] = '0';
}
}
if (len < PRINTF_NTOA_BUFFER_SIZE) {
if (negative) {
buf[len++] = '-';
}
else if (flags & FLAGS_PLUS) {
buf[len++] = '+'; // ignore the space if the '+' exists
}
else if (flags & FLAGS_SPACE) {
buf[len++] = ' ';
}
}
return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags);
}
// internal itoa for 'long' type
static size_t _ntoa_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long value, bool negative, unsigned long base, unsigned int prec, unsigned int width, unsigned int flags)
{
char buf[PRINTF_NTOA_BUFFER_SIZE];
size_t len = 0U;
// no hash for 0 values
if (!value) {
flags &= ~FLAGS_HASH;
}
// write if precision != 0 and value is != 0
if (!(flags & FLAGS_PRECISION) || value) {
do {
const char digit = (char)(value % base);
buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10;
value /= base;
} while (value && (len < PRINTF_NTOA_BUFFER_SIZE));
}
return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags);
}
// internal itoa for 'long long' type
#if defined(PRINTF_SUPPORT_LONG_LONG)
static size_t _ntoa_long_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long long value, bool negative, unsigned long long base, unsigned int prec, unsigned int width, unsigned int flags)
{
char buf[PRINTF_NTOA_BUFFER_SIZE];
size_t len = 0U;
// no hash for 0 values
if (!value) {
flags &= ~FLAGS_HASH;
}
// write if precision != 0 and value is != 0
if (!(flags & FLAGS_PRECISION) || value) {
do {
const char digit = (char)(value % base);
buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10;
value /= base;
} while (value && (len < PRINTF_NTOA_BUFFER_SIZE));
}
return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags);
}
#endif // PRINTF_SUPPORT_LONG_LONG
#if defined(PRINTF_SUPPORT_FLOAT)
#if defined(PRINTF_SUPPORT_EXPONENTIAL)
// forward declaration so that _ftoa can switch to exp notation for values > PRINTF_MAX_FLOAT
static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags);
#endif
// internal ftoa for fixed decimal floating point
static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags)
{
char buf[PRINTF_FTOA_BUFFER_SIZE];
size_t len = 0U;
double diff = 0.0;
// powers of 10
static const double pow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };
// test for special values
if (value != value)
return _out_rev(out, buffer, idx, maxlen, "nan", 3, width, flags);
if (value < -DBL_MAX)
return _out_rev(out, buffer, idx, maxlen, "fni-", 4, width, flags);
if (value > DBL_MAX)
return _out_rev(out, buffer, idx, maxlen, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U, width, flags);
// test for very large values
// standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad
if ((value > PRINTF_MAX_FLOAT) || (value < -PRINTF_MAX_FLOAT)) {
#if defined(PRINTF_SUPPORT_EXPONENTIAL)
return _etoa(out, buffer, idx, maxlen, value, prec, width, flags);
#else
return 0U;
#endif
}
// test for negative
bool negative = false;
if (value < 0) {
negative = true;
value = 0 - value;
}
// set default precision, if not set explicitly
if (!(flags & FLAGS_PRECISION)) {
prec = PRINTF_DEFAULT_FLOAT_PRECISION;
}
// limit precision to 9, cause a prec >= 10 can lead to overflow errors
while ((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) {
buf[len++] = '0';
prec--;
}
int whole = (int)value;
double tmp = (value - whole) * pow10[prec];
unsigned long frac = (unsigned long)tmp;
diff = tmp - frac;
if (diff > 0.5) {
++frac;
// handle rollover, e.g. case 0.99 with prec 1 is 1.0
if (frac >= pow10[prec]) {
frac = 0;
++whole;
}
}
else if (diff < 0.5) {
}
else if ((frac == 0U) || (frac & 1U)) {
// if halfway, round up if odd OR if last digit is 0
++frac;
}
if (prec == 0U) {
diff = value - (double)whole;
if ((!(diff < 0.5) || (diff > 0.5)) && (whole & 1)) {
// exactly 0.5 and ODD, then round up
// 1.5 -> 2, but 2.5 -> 2
++whole;
}
}
else {
unsigned int count = prec;
// now do fractional part, as an unsigned number
while (len < PRINTF_FTOA_BUFFER_SIZE) {
--count;
buf[len++] = (char)(48U + (frac % 10U));
if (!(frac /= 10U)) {
break;
}
}
// add extra 0s
while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) {
buf[len++] = '0';
}
if (len < PRINTF_FTOA_BUFFER_SIZE) {
// add decimal
buf[len++] = '.';
}
}
// do whole part, number is reversed
while (len < PRINTF_FTOA_BUFFER_SIZE) {
buf[len++] = (char)(48 + (whole % 10));
if (!(whole /= 10)) {
break;
}
}
// pad leading zeros
if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) {
if (width && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) {
width--;
}
while ((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) {
buf[len++] = '0';
}
}
if (len < PRINTF_FTOA_BUFFER_SIZE) {
if (negative) {
buf[len++] = '-';
}
else if (flags & FLAGS_PLUS) {
buf[len++] = '+'; // ignore the space if the '+' exists
}
else if (flags & FLAGS_SPACE) {
buf[len++] = ' ';
}
}
return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags);
}
#if defined(PRINTF_SUPPORT_EXPONENTIAL)
// internal ftoa variant for exponential floating-point type, contributed by Martijn Jasperse <m.jasperse@gmail.com>
static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags)
{
// check for NaN and special values
if ((value != value) || (value > DBL_MAX) || (value < -DBL_MAX)) {
return _ftoa(out, buffer, idx, maxlen, value, prec, width, flags);
}
// determine the sign
const bool negative = value < 0;
if (negative) {
value = -value;
}
// default precision
if (!(flags & FLAGS_PRECISION)) {
prec = PRINTF_DEFAULT_FLOAT_PRECISION;
}
// determine the decimal exponent
// based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c)
union {
uint64_t U;
double F;
} conv;
conv.F = value;
int exp2 = (int)((conv.U >> 52U) & 0x07FFU) - 1023; // effectively log2
conv.U = (conv.U & ((1ULL << 52U) - 1U)) | (1023ULL << 52U); // drop the exponent so conv.F is now in [1,2)
// now approximate log10 from the log2 integer part and an expansion of ln around 1.5
int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168);
// now we want to compute 10^expval but we want to be sure it won't overflow
exp2 = (int)(expval * 3.321928094887362 + 0.5);
const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453;
const double z2 = z * z;
conv.U = (uint64_t)(exp2 + 1023) << 52U;
// compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex
conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14)))));
// correct for rounding errors
if (value < conv.F) {
expval--;
conv.F /= 10;
}
// the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters
unsigned int minwidth = ((expval < 100) && (expval > -100)) ? 4U : 5U;
// in "%g" mode, "prec" is the number of *significant figures* not decimals
if (flags & FLAGS_ADAPT_EXP) {
// do we want to fall-back to "%f" mode?
if ((value >= 1e-4) && (value < 1e6)) {
if ((int)prec > expval) {
prec = (unsigned)((int)prec - expval - 1);
}
else {
prec = 0;
}
flags |= FLAGS_PRECISION; // make sure _ftoa respects precision
// no characters in exponent
minwidth = 0U;
expval = 0;
}
else {
// we use one sigfig for the whole part
if ((prec > 0) && (flags & FLAGS_PRECISION)) {
--prec;
}
}
}
// will everything fit?
unsigned int fwidth = width;
if (width > minwidth) {
// we didn't fall-back so subtract the characters required for the exponent
fwidth -= minwidth;
} else {
// not enough characters, so go back to default sizing
fwidth = 0U;
}
if ((flags & FLAGS_LEFT) && minwidth) {
// if we're padding on the right, DON'T pad the floating part
fwidth = 0U;
}
// rescale the float value
if (expval) {
value /= conv.F;
}
// output the floating part
const size_t start_idx = idx;
idx = _ftoa(out, buffer, idx, maxlen, negative ? -value : value, prec, fwidth, flags & ~FLAGS_ADAPT_EXP);
// output the exponent part
if (minwidth) {
// output the exponential symbol
out((flags & FLAGS_UPPERCASE) ? 'E' : 'e', buffer, idx++, maxlen);
// output the exponent value
idx = _ntoa_long(out, buffer, idx, maxlen, (expval < 0) ? -expval : expval, expval < 0, 10, 0, minwidth-1, FLAGS_ZEROPAD | FLAGS_PLUS);
// might need to right-pad spaces
if (flags & FLAGS_LEFT) {
while (idx - start_idx < width) out(' ', buffer, idx++, maxlen);
}
}
return idx;
}
#endif // PRINTF_SUPPORT_EXPONENTIAL
#endif // PRINTF_SUPPORT_FLOAT
// internal vsnprintf
static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const char* format, va_list va)
{
unsigned int flags, width, precision, n;
size_t idx = 0U;
if (!buffer) {
// use null output function
out = _out_null;
}
while (*format)
{
// format specifier? %[flags][width][.precision][length]
if (*format != '%') {
// no
out(*format, buffer, idx++, maxlen);
format++;
continue;
}
else {
// yes, evaluate it
format++;
}
// evaluate flags
flags = 0U;
do {
switch (*format) {
case '0': flags |= FLAGS_ZEROPAD; format++; n = 1U; break;
case '-': flags |= FLAGS_LEFT; format++; n = 1U; break;
case '+': flags |= FLAGS_PLUS; format++; n = 1U; break;
case ' ': flags |= FLAGS_SPACE; format++; n = 1U; break;
case '#': flags |= FLAGS_HASH; format++; n = 1U; break;
default : n = 0U; break;
}
} while (n);
// evaluate width field
width = 0U;
if (_is_digit(*format)) {
width = _atoi(&format);
}
else if (*format == '*') {
const int w = va_arg(va, int);
if (w < 0) {
flags |= FLAGS_LEFT; // reverse padding
width = (unsigned int)-w;
}
else {
width = (unsigned int)w;
}
format++;
}
// evaluate precision field
precision = 0U;
if (*format == '.') {
flags |= FLAGS_PRECISION;
format++;
if (_is_digit(*format)) {
precision = _atoi(&format);
}
else if (*format == '*') {
const int prec = (int)va_arg(va, int);
precision = prec > 0 ? (unsigned int)prec : 0U;
format++;
}
}
// evaluate length field
switch (*format) {
case 'l' :
flags |= FLAGS_LONG;
format++;
if (*format == 'l') {
flags |= FLAGS_LONG_LONG;
format++;
}
break;
case 'h' :
flags |= FLAGS_SHORT;
format++;
if (*format == 'h') {
flags |= FLAGS_CHAR;
format++;
}
break;
#if defined(PRINTF_SUPPORT_PTRDIFF_T)
case 't' :
flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
format++;
break;
#endif
case 'j' :
flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
format++;
break;
case 'z' :
flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
format++;
break;
default :
break;
}
// evaluate specifier
switch (*format) {
case 'd' :
case 'i' :
case 'u' :
case 'x' :
case 'X' :
case 'o' :
case 'b' : {
// set the base
unsigned int base;
if (*format == 'x' || *format == 'X') {
base = 16U;
}
else if (*format == 'o') {
base = 8U;
}
else if (*format == 'b') {
base = 2U;
}
else {
base = 10U;
flags &= ~FLAGS_HASH; // no hash for dec format
}
// uppercase
if (*format == 'X') {
flags |= FLAGS_UPPERCASE;
}
// no plus or space flag for u, x, X, o, b
if ((*format != 'i') && (*format != 'd')) {
flags &= ~(FLAGS_PLUS | FLAGS_SPACE);
}
// ignore '0' flag when precision is given
if (flags & FLAGS_PRECISION) {
flags &= ~FLAGS_ZEROPAD;
}
// convert the integer
if ((*format == 'i') || (*format == 'd')) {
// signed
if (flags & FLAGS_LONG_LONG) {
#if defined(PRINTF_SUPPORT_LONG_LONG)
const long long value = va_arg(va, long long);
idx = _ntoa_long_long(out, buffer, idx, maxlen, (unsigned long long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags);
#endif
}
else if (flags & FLAGS_LONG) {
const long value = va_arg(va, long);
idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags);
}
else {
const int value = (flags & FLAGS_CHAR) ? (char)va_arg(va, int) : (flags & FLAGS_SHORT) ? (short int)va_arg(va, int) : va_arg(va, int);
idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned int)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags);
}
}
else {
// unsigned
if (flags & FLAGS_LONG_LONG) {
#if defined(PRINTF_SUPPORT_LONG_LONG)
idx = _ntoa_long_long(out, buffer, idx, maxlen, va_arg(va, unsigned long long), false, base, precision, width, flags);
#endif
}
else if (flags & FLAGS_LONG) {
idx = _ntoa_long(out, buffer, idx, maxlen, va_arg(va, unsigned long), false, base, precision, width, flags);
}
else {
const unsigned int value = (flags & FLAGS_CHAR) ? (unsigned char)va_arg(va, unsigned int) : (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(va, unsigned int) : va_arg(va, unsigned int);
idx = _ntoa_long(out, buffer, idx, maxlen, value, false, base, precision, width, flags);
}
}
format++;
break;
}
#if defined(PRINTF_SUPPORT_FLOAT)
case 'f' :
case 'F' :
if (*format == 'F') flags |= FLAGS_UPPERCASE;
idx = _ftoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags);
format++;
break;
#if defined(PRINTF_SUPPORT_EXPONENTIAL)
case 'e':
case 'E':
case 'g':
case 'G':
if ((*format == 'g')||(*format == 'G')) flags |= FLAGS_ADAPT_EXP;
if ((*format == 'E')||(*format == 'G')) flags |= FLAGS_UPPERCASE;
idx = _etoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags);
format++;
break;
#endif // PRINTF_SUPPORT_EXPONENTIAL
#endif // PRINTF_SUPPORT_FLOAT
case 'c' : {
unsigned int l = 1U;
// pre padding
if (!(flags & FLAGS_LEFT)) {
while (l++ < width) {
out(' ', buffer, idx++, maxlen);
}
}
// char output
out((char)va_arg(va, int), buffer, idx++, maxlen);
// post padding
if (flags & FLAGS_LEFT) {
while (l++ < width) {
out(' ', buffer, idx++, maxlen);
}
}
format++;
break;
}
case 's' : {
const char* p = va_arg(va, char*);
unsigned int l = _strnlen_s(p, precision ? precision : (size_t)-1);
// pre padding
if (flags & FLAGS_PRECISION) {
l = (l < precision ? l : precision);
}
if (!(flags & FLAGS_LEFT)) {
while (l++ < width) {
out(' ', buffer, idx++, maxlen);
}
}
// string output
while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) {
out(*(p++), buffer, idx++, maxlen);
}
// post padding
if (flags & FLAGS_LEFT) {
while (l++ < width) {
out(' ', buffer, idx++, maxlen);
}
}
format++;
break;
}
case 'p' : {
width = sizeof(void*) * 2U;
flags |= FLAGS_ZEROPAD | FLAGS_UPPERCASE;
#if defined(PRINTF_SUPPORT_LONG_LONG)
const bool is_ll = sizeof(uintptr_t) == sizeof(long long);
if (is_ll) {
idx = _ntoa_long_long(out, buffer, idx, maxlen, (uintptr_t)va_arg(va, void*), false, 16U, precision, width, flags);
}
else {
#endif
idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)((uintptr_t)va_arg(va, void*)), false, 16U, precision, width, flags);
#if defined(PRINTF_SUPPORT_LONG_LONG)
}
#endif
format++;
break;
}
case '%' :
out('%', buffer, idx++, maxlen);
format++;
break;
default :
out(*format, buffer, idx++, maxlen);
format++;
break;
}
}
// termination
out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen);
// return written chars without terminating \0
return (int)idx;
}
///////////////////////////////////////////////////////////////////////////////
int printf_(const char* format, ...)
{
va_list va;
va_start(va, format);
char buffer[1];
const int ret = _vsnprintf(_out_char, buffer, (size_t)-1, format, va);
va_end(va);
return ret;
}
int sprintf_(char* buffer, const char* format, ...)
{
va_list va;
va_start(va, format);
const int ret = _vsnprintf(_out_buffer, buffer, (size_t)-1, format, va);
va_end(va);
return ret;
}
int snprintf_(char* buffer, size_t count, const char* format, ...)
{
va_list va;
va_start(va, format);
const int ret = _vsnprintf(_out_buffer, buffer, count, format, va);
va_end(va);
return ret;
}
int vprintf_(const char* format, va_list va)
{
char buffer[1];
return _vsnprintf(_out_char, buffer, (size_t)-1, format, va);
}
int vsnprintf_(char* buffer, size_t count, const char* format, va_list va)
{
return _vsnprintf(_out_buffer, buffer, count, format, va);
}
int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...)
{
va_list va;
va_start(va, format);
const out_fct_wrap_type out_fct_wrap = { out, arg };
const int ret = _vsnprintf(_out_fct, (char*)(uintptr_t)&out_fct_wrap, (size_t)-1, format, va);
va_end(va);
return ret;
}

View File

@@ -0,0 +1,116 @@
///////////////////////////////////////////////////////////////////////////////
// \author (c) Marco Paland (info@paland.com)
// 2014-2019, PALANDesign Hannover, Germany
//
// \license The MIT License (MIT)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// \brief Tiny printf, sprintf and snprintf implementation, optimized for speed on
// embedded systems with a very limited resources.
// Use this instead of bloated standard/newlib printf.
// These routines are thread safe and reentrant.
//
///////////////////////////////////////////////////////////////////////////////
#ifndef _PRINTF_H_
#define _PRINTF_H_
#include <stdarg.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* Output a character to a custom device like UART, used by the printf() function
* This function is declared here only. You have to write your custom implementation somewhere
* \param character Character to output
*/
void _putchar(char character);
/**
* Tiny printf implementation
* You have to implement _putchar if you use printf()
* To avoid conflicts with the regular printf() API it is overridden by macro defines
* and internal underscore-appended functions like printf_() are used
* \param format A string that specifies the format of the output
* \return The number of characters that are written into the array, not counting the terminating null character
*/
#define printf printf_
int printf_(const char* format, ...);
/**
* Tiny sprintf implementation
* Due to security reasons (buffer overflow) YOU SHOULD CONSIDER USING (V)SNPRINTF INSTEAD!
* \param buffer A pointer to the buffer where to store the formatted string. MUST be big enough to store the output!
* \param format A string that specifies the format of the output
* \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character
*/
#define sprintf sprintf_
int sprintf_(char* buffer, const char* format, ...);
/**
* Tiny snprintf/vsnprintf implementation
* \param buffer A pointer to the buffer where to store the formatted string
* \param count The maximum number of characters to store in the buffer, including a terminating null character
* \param format A string that specifies the format of the output
* \param va A value identifying a variable arguments list
* \return The number of characters that COULD have been written into the buffer, not counting the terminating
* null character. A value equal or larger than count indicates truncation. Only when the returned value
* is non-negative and less than count, the string has been completely written.
*/
#define snprintf snprintf_
#define vsnprintf vsnprintf_
int snprintf_(char* buffer, size_t count, const char* format, ...);
int vsnprintf_(char* buffer, size_t count, const char* format, va_list va);
/**
* Tiny vprintf implementation
* \param format A string that specifies the format of the output
* \param va A value identifying a variable arguments list
* \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character
*/
#define vprintf vprintf_
int vprintf_(const char* format, va_list va);
/**
* printf with output function
* You may use this as dynamic alternative to printf() with its fixed _putchar() output
* \param out An output function which takes one character and an argument pointer
* \param arg An argument pointer for user data passed to output function
* \param format A string that specifies the format of the output
* \return The number of characters that are sent to the output function, not counting the terminating null character
*/
int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...);
#ifdef __cplusplus
}
#endif
#endif // _PRINTF_H_

View File

@@ -4,49 +4,49 @@
#include <stdint.h>
/*
All communications are direct from tag to station, EXCEPT association (tag will broadcast).
All comms shall be encrypted and authenticated with AES-CCM. Shared key shall be burned into the firmware.
Master shall provision new key at association. All non-bcast packets shall have pan id compression.
Master may skip "from" field. Tag checking in confirms it got the master's provisioning reply.
Sadly filtering on MZ100 fails for long addr with no src addr. so short addr for src is used
T = tag, S = station
PACKET TYPE USE PAYLOAD STRUCT NOTES
ASSOC_REQ T2bcast TagInfo tag's info and assoc request (encrypted with shared key)
ASSOC_RESP S2T AssocInfo tag's association info (encrypted with shared key)
CHECKIN T2S CheckinInfo tag checking in occasionally
CHECKOUT S2T PendingInfo station's checkin reply telling tag what we have for it
CHUNK_REQ T2S ChunkReqInfo tag requesting a piece of data
CHUNK_RESP S2T ChunkInfo station provides chunk
All communications are direct from tag to station, EXCEPT association (tag will broadcast).
All comms shall be encrypted and authenticated with AES-CCM. Shared key shall be burned into the firmware.
Master shall provision new key at association. All non-bcast packets shall have pan id compression.
Master may skip "from" field. Tag checking in confirms it got the master's provisioning reply.
Sadly filtering on MZ100 fails for long addr with no src addr. so short addr for src is used
T = tag, S = station
PACKET TYPE USE PAYLOAD STRUCT NOTES
ASSOC_REQ T2bcast TagInfo tag's info and assoc request (encrypted with shared key)
ASSOC_RESP S2T AssocInfo tag's association info (encrypted with shared key)
CHECKIN T2S CheckinInfo tag checking in occasionally
CHECKOUT S2T PendingInfo station's checkin reply telling tag what we have for it
CHUNK_REQ T2S ChunkReqInfo tag requesting a piece of data
CHUNK_RESP S2T ChunkInfo station provides chunk
*/
#define PROTO_PRESHARED_KEY {0x34D906D3, 0xE3E5298E, 0x3429BF58, 0xC1022081}
#define PROTO_PRESHARED_KEY \
{ 0x34D906D3, 0xE3E5298E, 0x3429BF58, 0xC1022081 }
#define PROTO_PAN_ID (0x4447) //PAN ID compression shall be used
#define PROTO_PAN_ID (0x4447) // PAN ID compression shall be used
#define PKT_ASSOC_REQ (0xF0)
#define PKT_ASSOC_RESP (0xF1)
#define PKT_CHECKIN (0xF2)
#define PKT_CHECKOUT (0xF3)
#define PKT_CHUNK_REQ (0xF4)
#define PKT_CHUNK_RESP (0xF5)
#define PKT_ASSOC_REQ (0xF0)
#define PKT_ASSOC_RESP (0xF1)
#define PKT_CHECKIN (0xF2)
#define PKT_CHECKOUT (0xF3)
#define PKT_CHUNK_REQ (0xF4)
#define PKT_CHUNK_RESP (0xF5)
#define PROTO_VER_0 (0)
#define PROTO_VER_CURRENT (PROTO_VER_0)
#define PROTO_VER_0 (0)
#define PROTO_VER_CURRENT (PROTO_VER_0)
#define PROTO_COMPR_TYPE_LZ (0x0001)
#define PROTO_COMPR_TYPE_BITPACK (0x0002)
#define PROTO_MAX_DL_LEN (88)
#define PROTO_COMPR_TYPE_LZ (0x0001)
#define PROTO_COMPR_TYPE_BITPACK (0x0002)
#define PROTO_MAX_DL_LEN (88)
//////////////// NEW
#include "tag_types.h"
/*
// power saving algorithm
#define INTERVAL_BASE 40 // interval (in seconds) (when 1 packet is sent/received) for target current (7.2µA)
#define INTERVAL_AT_MAX_ATTEMPTS 600 // interval (in seconds) (at max attempts) for target average current
@@ -68,7 +68,7 @@
#define INTERVAL_2_TIME 7200UL // Try every 2 hours
#define INTERVAL_2_ATTEMPTS 12 // for 12 attempts (an additional day)
#define INTERVAL_3_TIME 86400UL // Finally, try every day
*/
#pragma pack(1)
enum TagScreenType {
TagScreenEink_BW_1bpp,
@@ -96,8 +96,6 @@ enum TagScreenType {
TagScreenTypeOther = 0x7f,
};
#define RADIO_MAX_PACKET_LEN (125) // useful payload, not including the crc
#define ADDR_MODE_NONE (0)
@@ -122,7 +120,7 @@ struct MacFcs {
uint8_t destAddrType : 2;
uint8_t frameVer : 2;
uint8_t srcAddrType : 2;
} ;
};
struct MacFrameFromMaster {
struct MacFcs fcs;
@@ -130,7 +128,7 @@ struct MacFrameFromMaster {
uint16_t pan;
uint8_t dst[8];
uint16_t from;
} ;
};
struct MacFrameNormal {
struct MacFcs fcs;
@@ -138,7 +136,7 @@ struct MacFrameNormal {
uint16_t pan;
uint8_t dst[8];
uint8_t src[8];
} ;
};
struct MacFrameBcast {
struct MacFcs fcs;
@@ -147,7 +145,7 @@ struct MacFrameBcast {
uint16_t dstAddr;
uint16_t srcPan;
uint8_t src[8];
} ;
};
#define PKT_AVAIL_DATA_SHORTREQ 0xE3
#define PKT_AVAIL_DATA_REQ 0xE5
@@ -175,7 +173,7 @@ struct AvailDataReq {
uint8_t currentChannel;
uint8_t customMode;
uint8_t reserved[8];
} ;
};
struct oldAvailDataReq {
uint8_t checksum;
@@ -186,7 +184,7 @@ struct oldAvailDataReq {
uint8_t hwType;
uint8_t wakeupReason;
uint8_t capabilities;
} ;
};
struct AvailDataInfo {
uint8_t checksum;
@@ -195,31 +193,31 @@ struct AvailDataInfo {
uint8_t dataType;
uint8_t dataTypeArgument; // extra specification or instruction for the tag (LUT to be used for drawing image)
uint16_t nextCheckIn; // when should the tag check-in again? Measured in minutes
} ;
} __attribute__((packed)) ;
struct pendingData {
struct AvailDataInfo availdatainfo;
uint16_t attemptsLeft;
uint8_t targetMac[8];
} ;
};
struct blockPart {
uint8_t checksum;
uint8_t blockId;
uint8_t blockPart;
uint8_t data[];
} ;
};
struct blockData {
uint16_t size;
uint16_t checksum;
uint8_t data[];
} ;
};
struct burstMacData {
uint16_t offset;
uint8_t targetMac[8];
} ;
};
#define BLOCK_PART_DATA_SIZE 99
#define BLOCK_MAX_PARTS 42
@@ -238,140 +236,131 @@ struct blockRequest {
struct blockRequestAck {
uint8_t checksum;
uint16_t pleaseWaitMs;
} ;
};
struct espBlockRequest {
uint8_t checksum;
uint64_t ver;
uint8_t blockId;
uint8_t src[8];
} ;
};
struct espXferComplete {
uint8_t checksum;
uint8_t src[8];
} ;
};
struct espAvailDataReq {
uint8_t checksum;
uint8_t src[8];
struct AvailDataReq adr;
} ;
};
struct espSetChannelPower {
uint8_t checksum;
uint8_t channel;
uint8_t power;
} ;
};
#pragma pack(0)
///////////////// NEW END
#ifndef __packed
#define __packed __attribute__((packed))
#define __packed __attribute__((packed))
#endif
struct TagState {
uint64_t swVer;
uint16_t hwType;
uint16_t batteryMv;
uint64_t swVer;
uint16_t hwType;
uint16_t batteryMv;
} __packed;
struct TagInfo {
uint8_t protoVer; //PROTO_VER_*
struct TagState state;
uint8_t rfu1[1]; //shall be ignored for now
uint16_t screenPixWidth;
uint16_t screenPixHeight;
uint16_t screenMmWidth;
uint16_t screenMmHeight;
uint16_t compressionsSupported; //COMPR_TYPE_* bitfield
uint16_t maxWaitMsec; //how long tag will wait for packets before going to sleep
uint8_t screenType; //enum TagScreenType
uint8_t rfu[11]; //shall be zero for now
uint8_t protoVer; // PROTO_VER_*
struct TagState state;
uint8_t rfu1[1]; // shall be ignored for now
uint16_t screenPixWidth;
uint16_t screenPixHeight;
uint16_t screenMmWidth;
uint16_t screenMmHeight;
uint16_t compressionsSupported; // COMPR_TYPE_* bitfield
uint16_t maxWaitMsec; // how long tag will wait for packets before going to sleep
uint8_t screenType; // enum TagScreenType
uint8_t rfu[11]; // shall be zero for now
} __packed;
struct AssocInfo {
uint32_t checkinDelay; //space between checkins, in msec
uint32_t retryDelay; //if download fails mid-way wait thi smany msec to retry (IFF progress was made)
uint16_t failedCheckinsTillBlank; //how many fails till we go blank
uint16_t failedCheckinsTillDissoc; //how many fails till we dissociate
uint32_t newKey[4];
uint8_t rfu[8]; //shall be zero for now
uint32_t checkinDelay; // space between checkins, in msec
uint32_t retryDelay; // if download fails mid-way wait thi smany msec to retry (IFF progress was made)
uint16_t failedCheckinsTillBlank; // how many fails till we go blank
uint16_t failedCheckinsTillDissoc; // how many fails till we dissociate
uint32_t newKey[4];
uint8_t rfu[8]; // shall be zero for now
} __packed;
#define CHECKIN_TEMP_OFFSET 0x7f
#define CHECKIN_TEMP_OFFSET 0x7f
struct CheckinInfo {
struct TagState state;
uint8_t lastPacketLQI; //zero if not reported/not supported to be reported
int8_t lastPacketRSSI; //zero if not reported/not supported to be reported
uint8_t temperature; //zero if not reported/not supported to be reported. else, this minus CHECKIN_TEMP_OFFSET is temp in degrees C
uint8_t rfu[6]; //shall be zero for now
struct TagState state;
uint8_t lastPacketLQI; // zero if not reported/not supported to be reported
int8_t lastPacketRSSI; // zero if not reported/not supported to be reported
uint8_t temperature; // zero if not reported/not supported to be reported. else, this minus CHECKIN_TEMP_OFFSET is temp in degrees C
uint8_t rfu[6]; // shall be zero for now
} __packed;
struct PendingInfo {
uint64_t imgUpdateVer;
uint32_t imgUpdateSize;
uint64_t osUpdateVer; //version of OS update avail
uint32_t osUpdateSize;
uint8_t rfu[8]; //shall be zero for now
uint64_t imgUpdateVer;
uint32_t imgUpdateSize;
uint64_t osUpdateVer; // version of OS update avail
uint32_t osUpdateSize;
uint8_t rfu[8]; // shall be zero for now
} __packed;
struct ChunkReqInfo {
uint64_t versionRequested;
uint32_t offset;
uint8_t len;
uint8_t osUpdatePlz : 1;
uint8_t rfu[6]; //shall be zero for now
uint64_t versionRequested;
uint32_t offset;
uint8_t len;
uint8_t osUpdatePlz : 1;
uint8_t rfu[6]; // shall be zero for now
} __packed;
struct ChunkInfo {
uint32_t offset;
uint8_t osUpdatePlz : 1;
uint8_t rfu; //shall be zero for now
uint8_t data[]; //no data means request is out of bounds of this version no longer exists
uint32_t offset;
uint8_t osUpdatePlz : 1;
uint8_t rfu; // shall be zero for now
uint8_t data[]; // no data means request is out of bounds of this version no longer exists
} __packed;
#define MACFMT "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x"
#define MACCVT(x) ((const uint8_t*)(x))[7], ((const uint8_t*)(x))[6], ((const uint8_t*)(x))[5], ((const uint8_t*)(x))[4], ((const uint8_t*)(x))[3], ((const uint8_t*)(x))[2], ((const uint8_t*)(x))[1], ((const uint8_t*)(x))[0]
#define VERSION_SIGNIFICANT_MASK (0x0000ffffffffffffull)
#define MACFMT "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x"
#define MACCVT(x) ((const uint8_t*)(x))[7], ((const uint8_t*)(x))[6], ((const uint8_t*)(x))[5], ((const uint8_t*)(x))[4], ((const uint8_t*)(x))[3], ((const uint8_t*)(x))[2], ((const uint8_t*)(x))[1], ((const uint8_t*)(x))[0]
#define HW_TYPE_42_INCH_SAMSUNG (1)
#define HW_TYPE_42_INCH_SAMSUNG_ROM_VER_OFST (0xEFF8)
#define HW_TYPE_74_INCH_DISPDATA (2)
#define HW_TYPE_74_INCH_DISPDATA_FRAME_MODE (3)
#define HW_TYPE_74_INCH_DISPDATA_ROM_VER_OFST (0x008b)
#define VERSION_SIGNIFICANT_MASK (0x0000ffffffffffffull)
#define HW_TYPE_ZBD_EPOP50 (4)
#define HW_TYPE_ZBD_EPOP50_ROM_VER_OFST (0x008b)
#define HW_TYPE_42_INCH_SAMSUNG (1)
#define HW_TYPE_42_INCH_SAMSUNG_ROM_VER_OFST (0xEFF8)
#define HW_TYPE_ZBD_EPOP900 (5)
#define HW_TYPE_ZBD_EPOP900_ROM_VER_OFST (0x008b)
#define HW_TYPE_74_INCH_DISPDATA (2)
#define HW_TYPE_74_INCH_DISPDATA_FRAME_MODE (3)
#define HW_TYPE_74_INCH_DISPDATA_ROM_VER_OFST (0x008b)
#define HW_TYPE_ZBD_EPOP50 (4)
#define HW_TYPE_ZBD_EPOP50_ROM_VER_OFST (0x008b)
#define HW_TYPE_ZBD_EPOP900 (5)
#define HW_TYPE_ZBD_EPOP900_ROM_VER_OFST (0x008b)
#define HW_TYPE_29_INCH_DISPDATA (6)
#define HW_TYPE_29_INCH_DISPDATA_FRAME_MODE (7)
#define HW_TYPE_29_INCH_DISPDATA_ROM_VER_OFST (0x008b)
#define HW_TYPE_29_INCH_ZBS_026 (8)
#define HW_TYPE_29_INCH_ZBS_026_FRAME_MODE (9)
#define HW_TYPE_29_INCH_ZBS_025 (10)
#define HW_TYPE_29_INCH_ZBS_025_FRAME_MODE (11)
#define HW_TYPE_29_INCH_ZBS_ROM_VER_OFST (0x008b)
#define HW_TYPE_74_INCH_BWR (40)
#define HW_TYPE_74_INCH_BWR_ROM_VER_OFST (0x0160)
#define HW_TYPE_58_INCH_BWR (41)
#define HW_TYPE_58_INCH_BWR_ROM_VER_OFST (0x0160)
#define HW_TYPE_42_INCH_BWR (42)
#define HW_TYPE_42_INCH_BWR_ROM_VER_OFST (0x0160)
#define HW_TYPE_29_INCH_DISPDATA (6)
#define HW_TYPE_29_INCH_DISPDATA_FRAME_MODE (7)
#define HW_TYPE_29_INCH_DISPDATA_ROM_VER_OFST (0x008b)
#define HW_TYPE_29_INCH_ZBS_026 (8)
#define HW_TYPE_29_INCH_ZBS_026_FRAME_MODE (9)
#define HW_TYPE_29_INCH_ZBS_025 (10)
#define HW_TYPE_29_INCH_ZBS_025_FRAME_MODE (11)
#define HW_TYPE_29_INCH_ZBS_ROM_VER_OFST (0x008b)
#define HW_TYPE_74_INCH_BWR (40)
#define HW_TYPE_74_INCH_BWR_ROM_VER_OFST (0x0160)
#define HW_TYPE_58_INCH_BWR (41)
#define HW_TYPE_58_INCH_BWR_ROM_VER_OFST (0x0160)
#define HW_TYPE_42_INCH_BWR (42)
#define HW_TYPE_42_INCH_BWR_ROM_VER_OFST (0x0160)
#endif

View File

@@ -3,143 +3,24 @@
#include "eeprom.h"
#include "util.h"
#include "mz100_flash.h"
#include "powermgt.h"
__attribute__((section(".aonshadow"))) struct tagsettings tagSettings = {0};
extern uint8_t blockXferBuffer[];
uint8_t* infopageTempBuffer = 1024 + blockXferBuffer;
#define EEPROM_NUM_SETTINGS_PAGES (EEPROM_SETTINGS_AREA_LEN / EEPROM_PAGE_SIZE)
#define SETTINGS_MAGIC (0x31415926)
static uint32_t mCurSettingsAddr;
static void settingsPrvDoWriteAtLocation(uint32_t addr, struct Settings* settings)
{
settings->hdr.revision++;
mCurSettingsAddr = addr;
FLASH_Write(false, addr, (uint8_t*)settings, sizeof(struct Settings));
}
//this is impossible to call before calling read. thus mCurSettingsAddr will be set
void settingsPrvDoWrite(struct Settings* settings)
{
struct SettingsHeader sh;
uint32_t i, addr;
uint8_t byte;
//first we try to fit in the current page, after current (latest) settings
if (mCurSettingsAddr) {
FLASH_Read(0, mCurSettingsAddr, (uint8_t*)&sh, sizeof(struct SettingsHeader));
addr = mCurSettingsAddr + sh.structSize;
//is there space?
if (addr % EEPROM_PAGE_SIZE != 0 && addr % EEPROM_PAGE_SIZE + sizeof(struct Settings) <= EEPROM_PAGE_SIZE) {
//is it erased
for (i = 0; i < sizeof(struct Settings); i++) {
FLASH_Read(0, addr, &byte, 1);
if (byte != 0xff)
break;
}
if (i == sizeof(struct Settings)) {
settingsPrvDoWriteAtLocation(addr, settings);
return;
}
}
}
//we need to erase - use next page (or 0th page if no current page at all)
if (mCurSettingsAddr) {
addr = (mCurSettingsAddr + EEPROM_PAGE_SIZE - 1) / EEPROM_PAGE_SIZE * EEPROM_PAGE_SIZE;
if (addr == EEPROM_SETTINGS_AREA_START + EEPROM_SETTINGS_AREA_LEN)
addr = EEPROM_SETTINGS_AREA_START;
}
else
addr = EEPROM_SETTINGS_AREA_START;
qspiEraseRange(addr, EEPROM_PAGE_SIZE);
settingsPrvDoWriteAtLocation(addr, settings);
}
void settingsRead(struct Settings* settings)
{
uint32_t bestAddr = 0, page, ofst;
uint64_t bestRevision = 0;
struct SettingsHeader sh;
bool doWrite = true;
for (page = 0; page < EEPROM_NUM_SETTINGS_PAGES; page++) {
for (ofst = 0; ofst < EEPROM_PAGE_SIZE - sizeof(struct SettingsHeader); ofst += sh.structSize) {
uint32_t addr = EEPROM_SETTINGS_AREA_START + page * EEPROM_PAGE_SIZE + ofst;
FLASH_Read(0, addr, (uint8_t*)&sh, sizeof(struct SettingsHeader));
//sanity checks. struct is only allowed to grow in size...
if (sh.magic != SETTINGS_MAGIC || ofst + sh.structSize > EEPROM_PAGE_SIZE || sh.structSize > sizeof(struct Settings))
break;
if (sh.revision > bestRevision) {
bestRevision = sh.revision;
bestAddr = addr;
}
}
}
if (bestAddr) {
FLASH_Read(0, bestAddr, (uint8_t*)&sh, sizeof(struct SettingsHeader)); //to get size
FLASH_Read(0, bestAddr, (uint8_t*)settings, sh.structSize);
mCurSettingsAddr = bestAddr;
}
else {
settings->hdr.structVersion = SETTINGS_VER_NONE;
settings->hdr.revision = 1;
mCurSettingsAddr = 0;
}
//migrate
switch (settings->hdr.structVersion) {
//current version here - mark as such
case SETTINGS_CURRENT:
doWrite = false;
break;
case SETTINGS_VER_NONE: //migrate to v1
memset(settings, 0, sizeof(*settings));
settings->hdr.magic = SETTINGS_MAGIC;
//fallthrough
case SETTINGS_VER_1: //migrate to v2
settings->prevDlProgress = 0xffff;
//fallthrough
case SETTINGS_VER_2: //migrate to v3
settings->lastRxedRSSI = 0;
//fallthrough
//new migrations here in order from lower vers to higher vers
settings->hdr.structVersion = SETTINGS_CURRENT;
settings->hdr.structSize = sizeof(struct Settings);
break;
}
if (doWrite)
settingsPrvDoWrite(settings);
}
void settingsWrite(struct Settings* settings)
{
struct Settings s;
settingsRead(&s);
if (memcmp(&s, settings, sizeof(struct Settings)))
settingsPrvDoWrite(settings);
void loadDefaultSettings() {
tagSettings.settingsVer = SETTINGS_STRUCT_VERSION;
tagSettings.enableFastBoot = DEFAULT_SETTING_FASTBOOT;
tagSettings.enableRFWake = DEFAULT_SETTING_RFWAKE;
tagSettings.enableTagRoaming = DEFAULT_SETTING_TAGROAMING;
tagSettings.enableScanForAPAfterTimeout = DEFAULT_SETTING_SCANFORAP;
tagSettings.enableLowBatSymbol = DEFAULT_SETTING_LOWBATSYMBOL;
tagSettings.enableNoRFSymbol = DEFAULT_SETTING_NORFSYMBOL;
tagSettings.customMode = 0;
tagSettings.fastBootCapabilities = 0;
tagSettings.minimumCheckInTime = INTERVAL_BASE;
tagSettings.fixedChannel = 0;
tagSettings.batLowVoltage = BATTERY_VOLTAGE_MINIMUM;
}

View File

@@ -1,65 +1,41 @@
#ifndef _SETTINGS_H_
#define _SETTINGS_H_
#ifndef SETTINGS_H
#define SETTINGS_H
#include <stdint.h>
#define SETTINGS_VER_NONE (0x00000000)
#define SETTINGS_VER_1 (0x00000001)
#define SETTINGS_VER_2 (0x00000002)
#define SETTINGS_VER_3 (0x00000003)
#define FW_VERSION 20 // version number (max 2.5.5 :) )
#define FW_VERSION_SUFFIX "-75" // suffix, like -RC1 or whatever.
// #define DEBUGBLOCKS // uncomment to enable extra debug information on the block transfers
// #define PRINT_LUT // uncomment if you want the tag to print the LUT for the current temperature bracket
struct SettingsHeader {
uint32_t magic;
uint64_t revision;
uint8_t structVersion;
uint8_t structSize; //incl this header
#define SETTINGS_STRUCT_VERSION 0x01
#define DEFAULT_SETTING_FASTBOOT 0
#define DEFAULT_SETTING_RFWAKE 0
#define DEFAULT_SETTING_TAGROAMING 0
#define DEFAULT_SETTING_SCANFORAP 1
#define DEFAULT_SETTING_LOWBATSYMBOL 1
#define DEFAULT_SETTING_NORFSYMBOL 1
struct tagsettings {
uint8_t settingsVer; // the version of the struct as written to the infopage
uint8_t enableFastBoot; // default 0; if set, it will skip splashscreen
uint8_t enableRFWake; // default 0; if set, it will enable RF wake. This will add about ~0.9µA idle power consumption
uint8_t enableTagRoaming; // default 0; if set, the tag will scan for an accesspoint every few check-ins. This will increase power consumption quite a bit
uint8_t enableScanForAPAfterTimeout; // default 1; if a the tag failed to check in, after a few attempts it will try to find a an AP on other channels
uint8_t enableLowBatSymbol; // default 1; tag will show 'low battery' icon on screen if the battery is depleted
uint8_t enableNoRFSymbol; // default 1; tag will show 'no signal' icon on screen if it failed to check in for a longer period of time
uint8_t fastBootCapabilities; // holds the byte with 'capabilities' as detected during a normal tag boot; allows the tag to skip detecting buttons and NFC chip
uint8_t customMode; // default 0; if anything else, tag will bootup in a different 'mode'
uint16_t batLowVoltage; // Low battery threshold voltage (2450 for 2.45v). defaults to BATTERY_VOLTAGE_MINIMUM from powermgt.h
uint16_t minimumCheckInTime; // defaults to BASE_INTERVAL from powermgt.h
uint8_t fixedChannel; // default 0; if set to a valid channel number, the tag will stick to that channel
} __attribute__((packed));
enum SettingsThingType {
SettingsThingTypeNone,
SettingsThingTypeImage,
SettingsThingTypeUpdate,
};
extern __attribute__((section(".aonshadow")))struct tagsettings tagSettings;
#define SETTING_CHANNEL_OFFSET 11
struct Settings { //V1
struct SettingsHeader hdr;
//master address
uint8_t masterMac[8];
//encryption things
uint32_t encrKey[4];
uint32_t nextIV;
//checkin tracking
uint32_t checkinDelay; //space between checkins, in msec
uint32_t retryDelay;
uint16_t failedCheckinsTillBlank; //how many fails till we go blank
uint16_t failedCheckinsTillDissoc; //how many fails till we dissociate
uint16_t numFailedCheckins;
//state
uint8_t lastRxedLQI;
uint8_t isPaired : 1;
uint8_t channel : 4; //minus SETTING_CHANNEL_OFFSET
uint8_t reserved : 3;
uint16_t prevDlProgress;
int8_t lastRxedRSSI;
uint32_t helperInit;
} __attribute__((packed));
#define SETTINGS_CURRENT SETTINGS_VER_3
void settingsRead(struct Settings* settings);
void settingsWrite(struct Settings* settings);
#endif
void loadDefaultSettings();
void writeSettings();
void loadSettings();
void loadSettingsFromBuffer(uint8_t* p);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,14 +1,19 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <stdint.h>
#include "settings.h"
extern uint8_t mSelfMac[];
extern uint8_t currentChannel;
extern __attribute__((section(".aonshadow"))) uint8_t mSelfMac[];
extern __attribute__((section(".aonshadow"))) volatile uint8_t currentChannel;
extern __attribute__((section(".aonshadow"))) struct blockRequest curBlock; // used by the block-requester, contains the next request that we'll send
extern __attribute__((section(".aonshadow"))) struct AvailDataInfo curDataInfo; // last 'AvailDataInfo' we received from the AP // __attribute__((section(".aon")))
extern uint8_t APmac[];
extern uint8_t curImgSlot;
extern __attribute__((section(".aonshadow"))) uint8_t curImgSlot;
extern void setupRadio(void);
extern void killRadio(void);

View File

@@ -0,0 +1,181 @@
#include "userinterface.h"
#include <stdbool.h>
#include <string.h>
#include "bitmaps.h"
#include "board.h"
#include "comms.h"
#include "epd.h"
#include "font.h"
#include "powermgt.h"
#include "printf.h"
#include "proto.h"
#include "screen.h"
#include "settings.h"
#include "syncedproto.h" // for APmac / Channel
#include "timer.h"
const uint16_t fwVersion = FW_VERSION;
const char fwVersionSuffix[] = FW_VERSION_SUFFIX;
extern uint8_t capabilities;
bool __attribute__((section(".aonshadow"))) lowBatteryShown = false;
bool __attribute__((section(".aonshadow"))) noAPShown = false;
void addCapabilities() {
// if (capabilities) epdpr("Options: ");
if (capabilities & CAPABILITY_HAS_NFC) {
// epdpr("-NFC");
if (capabilities & CAPABILITY_NFC_WAKE) {
// epdpr("+WAKE");
} else {
// epdpr(" ");
}
}
if (capabilities & CAPABILITY_HAS_WAKE_BUTTON) {
// epdpr("-WAKE BUTTON");
}
}
void addOverlay() {
if ((currentChannel == 0) && (tagSettings.enableNoRFSymbol)) {
// loadRawBitmap(ant, SCREEN_WIDTH - 24, 6, EPD_COLOR_BLACK);
// loadRawBitmap(cross, SCREEN_WIDTH - 16, 13, EPD_COLOR_RED);
noAPShown = true;
} else {
noAPShown = false;
}
if ((batteryVoltage < tagSettings.batLowVoltage) && (tagSettings.enableLowBatSymbol)) {
// loadRawBitmap(battery, SCREEN_WIDTH - 16, SCREEN_HEIGHT - 10, EPD_COLOR_BLACK);
lowBatteryShown = true;
} else {
lowBatteryShown = false;
}
}
void afterFlashScreenSaver() {
// selectLUT(EPD_LUT_DEFAULT);
// clearScreen();
#if (SCREEN_WIDTH == 400) // 4.2"
epdPrintBegin(3, 3, EPD_DIRECTION_X, EPD_SIZE_DOUBLE, EPD_COLOR_BLACK);
epdpr("OpenEPaperLink");
epdPrintEnd();
#endif
// drawWithSleep();
}
void showSplashScreen() {
// selectLUT(EPD_LUT_NO_REPEATS);
// clearScreen();
#if (SCREEN_WIDTH == 400) // 4.2"
epdPrintBegin(3, 3, EPD_DIRECTION_X, EPD_SIZE_DOUBLE, EPD_COLOR_BLACK);
epdpr("Starting");
epdPrintEnd();
epdPrintBegin(2, 252, EPD_DIRECTION_X, EPD_SIZE_SINGLE, EPD_COLOR_BLACK);
addCapabilities();
epdPrintEnd();
epdPrintBegin(3, 268, EPD_DIRECTION_X, EPD_SIZE_SINGLE, EPD_COLOR_BLACK);
epdpr("zbs42v033 %d.%d.%d%s", fwVersion / 100, (fwVersion % 100) / 10, (fwVersion % 10), fwVersionSuffix);
epdPrintEnd();
epdPrintBegin(3, 284, EPD_DIRECTION_X, EPD_SIZE_SINGLE, EPD_COLOR_RED);
epdpr("MAC: %02X:%02X", mSelfMac[7], mSelfMac[6]);
epdpr(":%02X:%02X", mSelfMac[5], mSelfMac[4]);
epdpr(":%02X:%02X", mSelfMac[3], mSelfMac[2]);
epdpr(":%02X:%02X", mSelfMac[1], mSelfMac[0]);
epdPrintEnd();
loadRawBitmap(oepli, 136, 22, EPD_COLOR_BLACK);
loadRawBitmap(cloud, 136, 10, EPD_COLOR_RED);
uint8_t __xdata buffer[17];
spr(buffer, "%02X%02X", mSelfMac[7], mSelfMac[6]);
spr(buffer + 4, "%02X%02X", mSelfMac[5], mSelfMac[4]);
spr(buffer + 8, "%02X%02X", mSelfMac[3], mSelfMac[2]);
spr(buffer + 12, "%02X%02X", mSelfMac[1], mSelfMac[0]);
printBarcode(buffer, 392, 264);
printBarcode(buffer, 384, 264);
#endif
// drawWithSleep();
}
void showApplyUpdate() {
// selectLUT(1);
// clearScreen();
#if (SCREEN_WIDTH == 400)
epdPrintBegin(136, 134, EPD_DIRECTION_X, EPD_SIZE_DOUBLE, EPD_COLOR_BLACK);
#endif
// epdpr("Updating!");
// epdPrintEnd();
// drawNoWait();
}
void showAPFound() {
init_epd();
fillWindow(0, 0, 640, 384, 1);
epdPrintf(10, 10, 1, "OpenEPaperLink");
epdPrintf(10, 40, 1, "AP Found at channel %d", currentChannel);
epdPrintf(10, 60, 1, "AP MAC: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X", APmac[7], APmac[6], APmac[5], APmac[4], APmac[3], APmac[2], APmac[1], APmac[0]);
epdPrintf(10, 330, 1, "Battery: %d.%dV", batteryVoltage / 1000, batteryVoltage % 1000);
epdPrintf(10, 350, 1, "Tag MAC: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X", mSelfMac[7], mSelfMac[6], mSelfMac[5], mSelfMac[4], mSelfMac[3], mSelfMac[2], mSelfMac[1], mSelfMac[0]);
epd_refresh_and_sleep();
}
void showNoAP() {
init_epd();
fillWindow(0, 0, 640, 384, 1);
epdPrintf(10, 10, 1, "OpenEPaperLink ");
epdPrintf(10, 40, 1, "No AP found... We'll try again in a little while though!");
epdPrintf(10, 350, 1, "Tag MAC: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X", mSelfMac[7], mSelfMac[6], mSelfMac[5], mSelfMac[4], mSelfMac[3], mSelfMac[2], mSelfMac[1], mSelfMac[0]);
epd_refresh_and_sleep();
}
void showLongTermSleep() {
// selectLUT(EPD_LUT_NO_REPEATS);
// clearScreen();
// epdPrintBegin(2, SCREEN_HEIGHT - 16, EPD_DIRECTION_X, EPD_SIZE_SINGLE, EPD_COLOR_BLACK);
// epdpr("zZ");
// epdPrintEnd();
addOverlay();
// drawWithSleep();
}
void showNoEEPROM() {
// selectLUT(EPD_LUT_NO_REPEATS);
// clearScreen();
#if (SCREEN_WIDTH == 400) // 4.2"
epdPrintBegin(50, 3, EPD_DIRECTION_X, EPD_SIZE_DOUBLE, EPD_COLOR_BLACK);
epdpr("EEPROM FAILED :(");
epdPrintEnd();
loadRawBitmap(failed, 176, 126, EPD_COLOR_RED);
epdPrintBegin(100, 284, EPD_DIRECTION_X, EPD_SIZE_SINGLE, EPD_COLOR_BLACK);
epdpr("Sleeping forever :'(");
epdPrintEnd();
#endif
// drawWithSleep();
}
void showNoMAC() {
// selectLUT(EPD_LUT_NO_REPEATS);
// clearScreen();
#if (SCREEN_WIDTH == 400) // 4.2"
epdPrintBegin(100, 3, EPD_DIRECTION_X, EPD_SIZE_DOUBLE, EPD_COLOR_BLACK);
epdpr("NO MAC SET :(");
epdPrintEnd();
loadRawBitmap(failed, 176, 126, EPD_COLOR_RED);
epdPrintBegin(100, 284, EPD_DIRECTION_X, EPD_SIZE_SINGLE, EPD_COLOR_BLACK);
epdpr("Sleeping forever :'(");
epdPrintEnd();
#endif
// drawWithSleep();
}

View File

@@ -0,0 +1,22 @@
#ifndef _UI_H_
#define _UI_H_
#include <stdint.h>
#include <stdbool.h>
void addOverlay();
void afterFlashScreenSaver();
void showSplashScreen();
void showApplyUpdate();
void showAPFound();
void showNoAP();
void showLongTermSleep();
void showNoEEPROM();
void showNoMAC();
extern const uint16_t fwVersion;
extern const char fwVersionSuffix[];
extern __attribute__((section(".aon"))) bool lowBatteryShown;
extern __attribute__((section(".aon"))) bool noAPShown;
#endif

View File

@@ -1,213 +1,185 @@
#include <stdarg.h>
#include <stdio.h>
#include "eeprom.h"
#include "timer.h"
#include "mz100.h"
#include "util.h"
#include "mz100_flash.h"
#include <stdarg.h>
//#include <stdio.h>
#include "printf.h"
#include "eeprom.h"
#include "mz100.h"
#include "mz100_adc.h"
#include "mz100_flash.h"
#include "mz100_wdt.h"
#include "timer.h"
void wdt10s()
{
void wdt10s() {
WDT_RestartCounter();
}
void wdt30s()
{
void wdt30s() {
WDT_RestartCounter();
}
void wdt60s()
{
void wdt60s() {
WDT_RestartCounter();
}
void delay(int cnt)
{
volatile unsigned int i;
for (i = 107 * cnt; i; --i)
;
void delay(int cnt) {
volatile unsigned int i;
for (i = 107 * cnt; i; --i)
;
}
void delay_us(unsigned int result)
{
volatile unsigned int i;
void delay_us(unsigned int result) {
volatile unsigned int i;
for (i = 0; i < result; ++i)
;
for (i = 0; i < result; ++i)
;
}
uint16_t crc16(uint16_t cur_crc, uint8_t data)
{
cur_crc ^= data;
for (uint8_t i = 8; i > 0; i--)
{
if ((cur_crc & 0x001) != 0)
{
cur_crc >>= 1;
cur_crc ^= 0x8005; // poly
}
else
{
cur_crc >>= 1;
}
}
return cur_crc;
uint16_t crc16(uint16_t cur_crc, uint8_t data) {
cur_crc ^= data;
for (uint8_t i = 8; i > 0; i--) {
if ((cur_crc & 0x001) != 0) {
cur_crc >>= 1;
cur_crc ^= 0x8005; // poly
} else {
cur_crc >>= 1;
}
}
return cur_crc;
}
uint32_t measureTemp(void)
{
uint32_t result = 0;
ADC_CFG_Type adc_config;
adc_config.adcResolution = ADC_RESOLUTION_14BIT;
adc_config.adcVrefSource = ADC_VREF_INTERNAL; // 1.2V
adc_config.adcGainSel = ADC_GAIN_1;
adc_config.adcClockDivider = ADC_CLOCK_DIVIDER_4;
adc_config.adcBiasMode = ADC_BIAS_FULL;
uint32_t measureTemp(void) {
uint32_t result = 0;
ADC_CFG_Type adc_config;
adc_config.adcResolution = ADC_RESOLUTION_14BIT;
adc_config.adcVrefSource = ADC_VREF_INTERNAL; // 1.2V
adc_config.adcGainSel = ADC_GAIN_1;
adc_config.adcClockDivider = ADC_CLOCK_DIVIDER_4;
adc_config.adcBiasMode = ADC_BIAS_FULL;
ADC_Reset();
ADC_ModeSelect(ADC_MODE_TSENSOR);
ADC_TSensorConfig(ADC_TEMPP, ADC_SENSOR_INTERNAL);
ADC_Init(&adc_config);
ADC_Enable();
for (int i = 0; i < 32; i++)
{
ADC_ConversionStart();
ADC_IntClr(ADC_RDY);
while (!ADC_GetStatus(ADC_STATUS_RDY))
;
ADC_ConversionStop();
}
for (int i = 0; i < 128; i++)
{
ADC_ConversionStart();
ADC_IntClr(ADC_RDY);
while (!ADC_GetStatus(ADC_STATUS_RDY))
;
ADC_ConversionStop();
result += (ADC_GetConversionResult() - 458) / 1.7;
}
result /= 128;
printf("Temp: %iC\r\n", result);
return result;
ADC_Reset();
ADC_ModeSelect(ADC_MODE_TSENSOR);
ADC_TSensorConfig(ADC_TEMPP, ADC_SENSOR_INTERNAL);
ADC_Init(&adc_config);
ADC_Enable();
for (int i = 0; i < 32; i++) {
ADC_ConversionStart();
ADC_IntClr(ADC_RDY);
while (!ADC_GetStatus(ADC_STATUS_RDY))
;
ADC_ConversionStop();
}
for (int i = 0; i < 128; i++) {
ADC_ConversionStart();
ADC_IntClr(ADC_RDY);
while (!ADC_GetStatus(ADC_STATUS_RDY))
;
ADC_ConversionStop();
result += (ADC_GetConversionResult() - 458) / 1.7;
}
result /= 128;
printf("Temp: %iC\r\n", result);
return result;
}
uint32_t measureBattery(void)
{
uint32_t result = 0;
ADC_CFG_Type adc_config;
adc_config.adcResolution = ADC_RESOLUTION_16BIT;
adc_config.adcVrefSource = ADC_VREF_VCAU; // 1.8V
adc_config.adcGainSel = ADC_GAIN_1; // range 0 - 1.8V
adc_config.adcClockDivider = ADC_CLOCK_DIVIDER_4;
adc_config.adcBiasMode = ADC_BIAS_FULL;
uint32_t measureBattery(void) {
uint32_t result = 0;
ADC_CFG_Type adc_config;
adc_config.adcResolution = ADC_RESOLUTION_16BIT;
adc_config.adcVrefSource = ADC_VREF_VCAU; // 1.8V
adc_config.adcGainSel = ADC_GAIN_1; // range 0 - 1.8V
adc_config.adcClockDivider = ADC_CLOCK_DIVIDER_4;
adc_config.adcBiasMode = ADC_BIAS_FULL;
ADC_Reset();
ADC_ModeSelect(ADC_MODE_ADC);
ADC_ChannelConfig(ADC_VBATS); // 0.33 of Actual Voltage
ADC_Init(&adc_config);
ADC_Enable();
ADC_ConversionStart();
ADC_IntClr(ADC_RDY);
for (int i = 0; i < 32; i++)
{
ADC_ConversionStart();
ADC_IntClr(ADC_RDY);
while (!ADC_GetStatus(ADC_STATUS_RDY))
;
ADC_ConversionStop();
}
for (int i = 0; i < 128; i++)
{
ADC_ConversionStart();
ADC_IntClr(ADC_RDY);
while (!ADC_GetStatus(ADC_STATUS_RDY))
;
ADC_ConversionStop();
result += ADC_GetConversionResult() * 5940 / 32768;
}
result /= 128;
printf("Voltage: %imV\r\n", result);
return result;
ADC_Reset();
ADC_ModeSelect(ADC_MODE_ADC);
ADC_ChannelConfig(ADC_VBATS); // 0.33 of Actual Voltage
ADC_Init(&adc_config);
ADC_Enable();
ADC_ConversionStart();
ADC_IntClr(ADC_RDY);
for (int i = 0; i < 32; i++) {
ADC_ConversionStart();
ADC_IntClr(ADC_RDY);
while (!ADC_GetStatus(ADC_STATUS_RDY))
;
ADC_ConversionStop();
}
for (int i = 0; i < 128; i++) {
ADC_ConversionStart();
ADC_IntClr(ADC_RDY);
while (!ADC_GetStatus(ADC_STATUS_RDY))
;
ADC_ConversionStop();
result += ADC_GetConversionResult() * 5940 / 32768;
}
result /= 128;
printf("Voltage: %imV\r\n", result);
return result;
}
void qspiEraseRange(uint32_t addr, uint32_t len)
{
uint64_t time;
// round starting address down
if (addr % EEPROM_PAGE_SIZE)
{
len += addr % EEPROM_PAGE_SIZE;
addr = addr / EEPROM_PAGE_SIZE * EEPROM_PAGE_SIZE;
}
void qspiEraseRange(uint32_t addr, uint32_t len) {
uint64_t time;
// round starting address down
if (addr % EEPROM_PAGE_SIZE) {
len += addr % EEPROM_PAGE_SIZE;
addr = addr / EEPROM_PAGE_SIZE * EEPROM_PAGE_SIZE;
}
// round length up
len = (len + EEPROM_PAGE_SIZE - 1) / EEPROM_PAGE_SIZE * EEPROM_PAGE_SIZE;
// round length up
len = (len + EEPROM_PAGE_SIZE - 1) / EEPROM_PAGE_SIZE * EEPROM_PAGE_SIZE;
while (len)
{
while (len) {
uint32_t now;
bool ok;
uint32_t now;
bool ok;
WDT_RestartCounter();
if (!(addr % 0x10000) && len >= 0x10000) {
ok = FLASH_Block64KErase(addr / 0x10000);
now = 0x10000;
} else if (!(addr % 0x8000) && len >= 0x8000) {
ok = FLASH_Block32KErase(addr / 0x8000);
now = 0x8000;
} else {
ok = FLASH_SectorErase(addr / 0x1000);
now = 0x1000;
}
WDT_RestartCounter();
if (!(addr % 0x10000) && len >= 0x10000)
{
ok = FLASH_Block64KErase(addr / 0x10000);
now = 0x10000;
}
else if (!(addr % 0x8000) && len >= 0x8000)
{
ok = FLASH_Block32KErase(addr / 0x8000);
now = 0x8000;
}
else
{
ok = FLASH_SectorErase(addr / 0x1000);
now = 0x1000;
}
if (!ok)
printf("ERZ fail at 0x%08x + %u\r\n", addr, now);
if (!ok)
printf("ERZ fail at 0x%08x + %u\r\n", addr, now);
addr += now;
len -= now;
if (len)
{
// let the caps recharge
time = timerGet();
while (timerGet() - time < TIMER_TICKS_PER_SEC / 10)
;
}
}
WDT_RestartCounter();
addr += now;
len -= now;
if (len) {
// let the caps recharge
time = timerGet();
while (timerGet() - time < TIMER_TICKS_PER_SEC / 10)
;
}
}
WDT_RestartCounter();
}
bool eepromWrite(uint32_t addr, const void *srcP, uint16_t len)
{
FLASH_Write(false, addr, srcP, len);
return true;
bool eepromWrite(uint32_t addr, const void *srcP, uint16_t len) {
FLASH_Write(false, addr, (void*)srcP, len);
return true;
}
bool eepromErase(uint32_t addr, uint16_t nSec)
{
qspiEraseRange(addr, nSec);
return true;
bool eepromErase(uint32_t addr, uint16_t nSec) {
qspiEraseRange(addr, nSec);
return true;
}
void eepromRead(uint32_t addr, void *dstP, uint16_t len)
{
uint8_t *dst = (uint8_t *)dstP;
FLASH_Read(0, addr, dst, len);
void eepromRead(uint32_t addr, void *dstP, uint16_t len) {
uint8_t *dst = (uint8_t *)dstP;
FLASH_Read(0, addr, dst, len);
}
uint32_t eepromGetSize(void)
{
return EEPROM_IMG_LEN;
uint32_t eepromGetSize(void) {
return EEPROM_IMG_LEN;
}
void radioShutdown(void)
{
// i have no idea what these do, determined by writing random registers and watching the current drawn
*(volatile uint32_t *)0x4C000000 = 0;
*(volatile uint32_t *)0x4C010000 = 0;
*(volatile uint32_t *)0x4C010004 = 0x10000000;
void radioShutdown(void) {
// i have no idea what these do, determined by writing random registers and watching the current drawn
*(volatile uint32_t *)0x4C000000 = 0;
*(volatile uint32_t *)0x4C010000 = 0;
*(volatile uint32_t *)0x4C010004 = 0x10000000;
}

View File

@@ -1,7 +1,8 @@
#include <stdio.h>
//#include <stdio.h>
#include <stdint.h>
#include "util.h"
#include "zigbee.h"
#include "printf.h"
volatile uint8_t calibration_irq_ocoured = 0;
volatile uint8_t zigbee_tx_done = 0;
@@ -145,24 +146,29 @@ void fill_rx_regs()
;
}
void sub_1021E6()
void load_calib()
{
int v0;
unsigned int v1;
unsigned int i;
v0 = (*(volatile unsigned int *)0x4C01000C);
v1 = get_register(0x130004);
v1 = zigbeeCalibData.len;
(*(volatile unsigned int *)0x4C010000) |= 4u;
while (((*(volatile unsigned int *)0x4C010008) & 0x1000000) == 0)
;
for (i = 0; i < v1; ++i)
set_register(v0 + 4 * i + 0x4C014000, *(uint32_t *)(4 * i + 0x130008));
set_register(v0 + 4 * i + 0x4C014000, zigbeeCalibData.data[i]);
(*(volatile unsigned int *)0x4C010000) &= ~4u;
while (((*(volatile unsigned int *)0x4C010008) & 0x1000000) != 0)
;
}
// It is from 0x130000 up to 0x1301000 But you can only use 0x130404 up to the 0x1301000
void save_calib_in_ram()
{
int v0;
@@ -171,14 +177,14 @@ void save_calib_in_ram()
v0 = (*(volatile unsigned int *)0x4C01000C) + 0x4C014000;
v1 = ((unsigned int)(uint8_t)((*(volatile unsigned int *)0x4C010008) >> 2) + 3) >> 2;
set_register(0x130000u, 0x464C4147);
set_register(0x130004u, v1);
zigbeeCalibData.isValid = true;
(*(volatile unsigned int *)0x4C01001C) = -5;
(*(volatile unsigned int *)0x4C010000) |= 2u;
while (((*(volatile unsigned int *)0x4C010008) & 0x1000000) == 0)
;
for (i = 0; i < v1; ++i)
set_register(4 * i + 0x130008, *(uint32_t *)(v0 + 4 * i));
for (i = 0; i < v1; ++i){
zigbeeCalibData.data[i] = *(uint32_t *)(v0 + 4 * i);
}
(*(volatile unsigned int *)0x4C010000) &= ~2u;
while (((*(volatile unsigned int *)0x4C010008) & 0x1000000) != 0)
;
@@ -189,12 +195,12 @@ int inner_calibration()
int is_in_ram;
(*(volatile unsigned int *)0x4C010000) |= 0x20u;
if (get_register(0x130000) == 0x464C4147)
if(zigbeeCalibData.isValid)
{
is_in_ram = 1;
(*(volatile unsigned int *)0x4C010000) |= 8u;
(*(volatile unsigned int *)0x4C010000) &= ~0x10u;
sub_1021E6();
load_calib();
}
else
{

View File

@@ -1,4 +1,5 @@
#pragma once
#include <stdlib.h>
extern uint8_t channelList[6];
@@ -10,4 +11,11 @@ uint8_t Zigbee_tx_buffer(uint8_t *tx_buffer, int len);
void radioInit();
void radioSetChannel(uint8_t channel);
void radioRxEnable(uint8_t channel);
void radioRxFlush();
void radioRxFlush();
struct zigbeeCalibDataStruct {
uint16_t len;
bool isValid;
uint32_t data[30];
};
extern __attribute__((section(".aon"))) volatile struct zigbeeCalibDataStruct zigbeeCalibData;

View File

@@ -0,0 +1,4 @@
build
*.axf
# Allow
!*.bin

View File

@@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(OpenEPaperLink_esp32_C6)

View File

@@ -0,0 +1 @@
idf.py -p COM21 flash monitor

View File

@@ -0,0 +1,7 @@
idf_component_register( SRCS
SRCS "utils.c"
SRCS "second_uart.c"
SRCS "radio.c"
SRCS "led.c"
SRCS "main.c"
INCLUDE_DIRS ".")

View File

@@ -0,0 +1,26 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <inttypes.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"
#include "led.h"
#include "proto.h"
void init_led()
{
gpio_config_t led1 = {};
led1.intr_type = GPIO_INTR_DISABLE;
led1.mode = GPIO_MODE_OUTPUT;
led1.pin_bit_mask = ((1ULL<<LED1) | (1ULL<<LED2));
led1.pull_down_en = 0;
led1.pull_up_en = 0;
gpio_config(&led1);
}
void led_set(int nr, bool state)
{
gpio_set_level(nr, state);
}

View File

@@ -0,0 +1,4 @@
#pragma once
void init_led();
void led_set(int nr, bool state);

View File

@@ -0,0 +1,715 @@
// Ported to ESP32-C6 By ATC1441(ATCnetz.de) for OpenEPaperLink at ~08.2023
#include "main.h"
#include <esp_mac.h>
#include <math.h>
#include <stdarg.h>
#include <string.h>
#include "driver/gpio.h"
#include "driver/uart.h"
#include "esp_err.h"
#include "esp_event.h"
#include "esp_ieee802154.h"
#include "esp_log.h"
#include "esp_phy_init.h"
#include "esp_timer.h"
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/task.h"
#include "proto.h"
#include "radio.h"
#include "sdkconfig.h"
#include "second_uart.h"
#include "soc/uart_struct.h"
#include "soc\lp_uart_reg.h"
#include "utils.h"
#include "led.h"
static const char *TAG = "MAIN";
const uint8_t channelList[6] = {11, 15, 20, 25, 26, 27};
#define DATATYPE_NOUPDATE 0
#define HW_TYPE 0xFF
#define MAX_PENDING_MACS 250
#define HOUSEKEEPING_INTERVAL 60UL
struct pendingData pendingDataArr[MAX_PENDING_MACS];
// VERSION GOES HERE!
uint16_t version = 0x0017;
#define RAW_PKT_PADDING 2
uint8_t radiotxbuffer[128];
uint8_t radiorxbuffer[128];
static uint32_t housekeepingTimer;
struct blockRequest requestedData = {0}; // holds which data was requested by the tag
uint8_t dstMac[8]; // target for the block transfer
uint16_t dstPan; //
static uint32_t blockStartTimer = 0; // reference that holds when the AP sends the next block
uint32_t nextBlockAttempt = 0; // reference time for when the AP can request a new block from the ESP32
uint8_t seq = 0; // holds current sequence number for transmission
uint8_t blockbuffer[BLOCK_XFER_BUFFER_SIZE + 5]; // block transfer buffer
uint8_t lastAckMac[8] = {0};
// these variables hold the current mac were talking to
#define CONCURRENT_REQUEST_DELAY 1200UL
uint32_t lastBlockRequest = 0;
uint8_t lastBlockMac[8];
uint8_t curChannel = 11;
uint8_t curPower = 10;
uint8_t curPendingData = 0;
uint8_t curNoUpdate = 0;
void sendXferCompleteAck(uint8_t *dst);
void sendCancelXfer(uint8_t *dst);
void espNotifyAPInfo();
// tools
void addCRC(void *p, uint8_t len) {
uint8_t total = 0;
for (uint8_t c = 1; c < len; c++) {
total += ((uint8_t *) p)[c];
}
((uint8_t *) p)[0] = total;
}
bool checkCRC(void *p, uint8_t len) {
uint8_t total = 0;
for (uint8_t c = 1; c < len; c++) {
total += ((uint8_t *) p)[c];
}
return ((uint8_t *) p)[0] == total;
}
uint8_t getPacketType(void *buffer) {
struct MacFcs *fcs = buffer;
if ((fcs->frameType == 1) && (fcs->destAddrType == 2) && (fcs->srcAddrType == 3) && (fcs->panIdCompressed == 0)) {
// broadcast frame
uint8_t type = ((uint8_t *) buffer)[sizeof(struct MacFrameBcast)];
return type;
} else if ((fcs->frameType == 1) && (fcs->destAddrType == 3) && (fcs->srcAddrType == 3) && (fcs->panIdCompressed == 1)) {
// normal frame
uint8_t type = ((uint8_t *) buffer)[sizeof(struct MacFrameNormal)];
return type;
}
return 0;
}
uint8_t getBlockDataLength() {
uint8_t partNo = 0;
for (uint8_t c = 0; c < BLOCK_MAX_PARTS; c++) {
if (requestedData.requestedParts[c / 8] & (1 << (c % 8))) {
partNo++;
}
}
return partNo;
}
// pendingdata slot stuff
int8_t findSlotForMac(const uint8_t *mac) {
for (uint8_t c = 0; c < MAX_PENDING_MACS; c++) {
if (memcmp(mac, ((uint8_t *) &(pendingDataArr[c].targetMac)), 8) == 0) {
if (pendingDataArr[c].attemptsLeft != 0) {
return c;
}
}
}
return -1;
}
int8_t findFreeSlot() {
for (uint8_t c = 0; c < MAX_PENDING_MACS; c++) {
if (pendingDataArr[c].attemptsLeft == 0) {
return c;
}
}
return -1;
}
int8_t findSlotForVer(const uint8_t *ver) {
for (uint8_t c = 0; c < MAX_PENDING_MACS; c++) {
if (memcmp(ver, ((uint8_t *) &(pendingDataArr[c].availdatainfo.dataVer)), 8) == 0) {
if (pendingDataArr[c].attemptsLeft != 0) return c;
}
}
return -1;
}
void deleteAllPendingDataForVer(const uint8_t *ver) {
int8_t slot = -1;
do {
slot = findSlotForVer(ver);
if (slot != -1) pendingDataArr[slot].attemptsLeft = 0;
} while (slot != -1);
}
void deleteAllPendingDataForMac(const uint8_t *mac) {
int8_t slot = -1;
do {
slot = findSlotForMac(mac);
if (slot != -1) pendingDataArr[slot].attemptsLeft = 0;
} while (slot != -1);
}
void countSlots() {
curPendingData = 0;
curNoUpdate = 0;
for (uint8_t c = 0; c < MAX_PENDING_MACS; c++) {
if (pendingDataArr[c].attemptsLeft != 0) {
if (pendingDataArr[c].availdatainfo.dataType != 0) {
curPendingData++;
} else {
curNoUpdate++;
}
}
}
}
// processing serial data
#define ZBS_RX_WAIT_HEADER 0
#define ZBS_RX_WAIT_SDA 1 // send data avail
#define ZBS_RX_WAIT_CANCEL 2 // cancel traffic for mac
#define ZBS_RX_WAIT_SCP 3 // set channel power
#define ZBS_RX_WAIT_BLOCKDATA 4
bool isSame(uint8_t *in1, char *in2, int len) {
bool flag = 1;
for (int i = 0; i < len; i++) {
if (in1[i] != in2[i]) flag = 0;
}
return flag;
}
int blockPosition = 0;
void processSerial(uint8_t lastchar) {
static uint8_t cmdbuffer[4];
static uint8_t RXState = 0;
static uint8_t serialbuffer[48];
static uint8_t *serialbufferp;
static uint8_t bytesRemain = 0;
static uint32_t lastSerial = 0;
static uint32_t blockStartTime = 0;
if ((RXState != ZBS_RX_WAIT_HEADER) && ((getMillis() - lastSerial) > 1000)) {
RXState = ZBS_RX_WAIT_HEADER;
ESP_LOGI(TAG, "UART Timeout");
}
lastSerial = getMillis();
switch (RXState) {
case ZBS_RX_WAIT_HEADER:
// shift characters in
for (uint8_t c = 0; c < 3; c++) {
cmdbuffer[c] = cmdbuffer[c + 1];
}
cmdbuffer[3] = lastchar;
if (isSame(cmdbuffer + 1, ">D>", 3)) {
pr("ACK>\n");
blockStartTime = getMillis();
ESP_LOGI(TAG, "Starting BlkData");
blockPosition = 0;
RXState = ZBS_RX_WAIT_BLOCKDATA;
}
if (isSame(cmdbuffer, "SDA>", 4)) {
ESP_LOGI(TAG, "SDA In");
RXState = ZBS_RX_WAIT_SDA;
bytesRemain = sizeof(struct pendingData);
serialbufferp = serialbuffer;
break;
}
if (isSame(cmdbuffer, "CXD>", 4)) {
ESP_LOGI(TAG, "CXD In");
RXState = ZBS_RX_WAIT_CANCEL;
bytesRemain = sizeof(struct pendingData);
serialbufferp = serialbuffer;
break;
}
if (isSame(cmdbuffer, "SCP>", 4)) {
ESP_LOGI(TAG, "SCP In");
RXState = ZBS_RX_WAIT_SCP;
bytesRemain = sizeof(struct espSetChannelPower);
serialbufferp = serialbuffer;
break;
}
if (isSame(cmdbuffer, "NFO?", 4)) {
pr("ACK>");
ESP_LOGI(TAG, "NFO? In");
espNotifyAPInfo();
RXState = ZBS_RX_WAIT_HEADER;
}
if (isSame(cmdbuffer, "RDY?", 4)) {
pr("ACK>");
ESP_LOGI(TAG, "RDY? In");
RXState = ZBS_RX_WAIT_HEADER;
}
if (isSame(cmdbuffer, "RSET", 4)) {
pr("ACK>");
ESP_LOGI(TAG, "RSET In");
delay(100);
// TODO RESET US HERE
RXState = ZBS_RX_WAIT_HEADER;
}
break;
case ZBS_RX_WAIT_BLOCKDATA:
blockbuffer[blockPosition++] = 0xAA ^ lastchar;
if (blockPosition >= 4100) {
ESP_LOGI(TAG, "Blockdata fully received %lu", getMillis() - blockStartTime);
RXState = ZBS_RX_WAIT_HEADER;
}
break;
case ZBS_RX_WAIT_SDA:
*serialbufferp = lastchar;
serialbufferp++;
bytesRemain--;
if (bytesRemain == 0) {
if (checkCRC(serialbuffer, sizeof(struct pendingData))) {
struct pendingData *pd = (struct pendingData *) serialbuffer;
int8_t slot = findSlotForMac(pd->targetMac);
if (slot == -1) slot = findFreeSlot();
if (slot != -1) {
memcpy(&(pendingDataArr[slot]), serialbuffer, sizeof(struct pendingData));
pr("ACK>\n");
} else {
pr("NOQ>\n");
}
} else {
pr("NOK>\n");
}
RXState = ZBS_RX_WAIT_HEADER;
}
break;
case ZBS_RX_WAIT_CANCEL:
*serialbufferp = lastchar;
serialbufferp++;
bytesRemain--;
if (bytesRemain == 0) {
if (checkCRC(serialbuffer, sizeof(struct pendingData))) {
struct pendingData *pd = (struct pendingData *) serialbuffer;
// deleteAllPendingDataForVer((uint8_t *)&pd->availdatainfo.dataVer);
deleteAllPendingDataForMac((uint8_t *) &pd->targetMac);
pr("ACK>\n");
} else {
pr("NOK>\n");
}
RXState = ZBS_RX_WAIT_HEADER;
}
break;
case ZBS_RX_WAIT_SCP:
*serialbufferp = lastchar;
serialbufferp++;
bytesRemain--;
if (bytesRemain == 0) {
if (checkCRC(serialbuffer, sizeof(struct espSetChannelPower))) {
struct espSetChannelPower *scp = (struct espSetChannelPower *) serialbuffer;
for (uint8_t c = 0; c < sizeof(channelList); c++) {
if (channelList[c] == scp->channel) goto SCPchannelFound;
}
goto SCPfailed;
SCPchannelFound:
curChannel = scp->channel;
curPower = scp->power;
radioSetChannel(scp->channel);
radioSetTxPower(scp->power);
pr("ACK>\n");
} else {
SCPfailed:
pr("NOK>\n");
}
RXState = ZBS_RX_WAIT_HEADER;
}
break;
}
}
// sending data to the ESP
void espBlockRequest(const struct blockRequest *br, uint8_t *src) {
struct espBlockRequest *ebr = (struct espBlockRequest *) blockbuffer;
uartTx('R');
uartTx('Q');
uartTx('B');
uartTx('>');
memcpy(&(ebr->ver), &(br->ver), 8);
memcpy(&(ebr->src), src, 8);
ebr->blockId = br->blockId;
addCRC(ebr, sizeof(struct espBlockRequest));
for (uint8_t c = 0; c < sizeof(struct espBlockRequest); c++) {
uartTx(((uint8_t *) ebr)[c]);
}
}
void espNotifyAvailDataReq(const struct AvailDataReq *adr, const uint8_t *src) {
uartTx('A');
uartTx('D');
uartTx('R');
uartTx('>');
struct espAvailDataReq eadr = {0};
memcpy((void *) eadr.src, (void *) src, 8);
memcpy((void *) &eadr.adr, (void *) adr, sizeof(struct AvailDataReq));
addCRC(&eadr, sizeof(struct espAvailDataReq));
for (uint8_t c = 0; c < sizeof(struct espAvailDataReq); c++) {
uartTx(((uint8_t *) &eadr)[c]);
}
}
void espNotifyXferComplete(const uint8_t *src) {
struct espXferComplete exfc;
memcpy(&exfc.src, src, 8);
uartTx('X');
uartTx('F');
uartTx('C');
uartTx('>');
addCRC(&exfc, sizeof(exfc));
for (uint8_t c = 0; c < sizeof(exfc); c++) {
uartTx(((uint8_t *) &exfc)[c]);
}
}
void espNotifyTimeOut(const uint8_t *src) {
struct espXferComplete exfc;
memcpy(&exfc.src, src, 8);
uartTx('X');
uartTx('T');
uartTx('O');
uartTx('>');
addCRC(&exfc, sizeof(exfc));
for (uint8_t c = 0; c < sizeof(exfc); c++) {
uartTx(((uint8_t *) &exfc)[c]);
}
}
void espNotifyAPInfo() {
pr("TYP>%02X\n", HW_TYPE);
pr("VER>%04X\n", version);
pr("MAC>%02X%02X", mSelfMac[0], mSelfMac[1]);
pr("%02X%02X", mSelfMac[2], mSelfMac[3]);
pr("%02X%02X", mSelfMac[4], mSelfMac[5]);
pr("%02X%02X\n", mSelfMac[6], mSelfMac[7]);
pr("ZCH>%02X\n", curChannel);
pr("ZPW>%02X\n", curPower);
countSlots();
pr("PEN>%02X\n", curPendingData);
pr("NOP>%02X\n", curNoUpdate);
}
// process data from tag
void processBlockRequest(const uint8_t *buffer, uint8_t forceBlockDownload) {
struct MacFrameNormal *rxHeader = (struct MacFrameNormal *) buffer;
struct blockRequest *blockReq = (struct blockRequest *) (buffer + sizeof(struct MacFrameNormal) + 1);
if (!checkCRC(blockReq, sizeof(struct blockRequest))) return;
// check if we're already talking to this mac
if (memcmp(rxHeader->src, lastBlockMac, 8) == 0) {
lastBlockRequest = getMillis();
} else {
// we weren't talking to this mac, see if there was a transfer in progress from another mac, recently
if ((getMillis() - lastBlockRequest) > CONCURRENT_REQUEST_DELAY) {
// mark this mac as the new current mac we're talking to
memcpy((void *) lastBlockMac, (void *) rxHeader->src, 8);
lastBlockRequest = getMillis();
} else {
// we're talking to another mac, let this mac know we can't accomodate another request right now
pr("BUSY!\n");
sendCancelXfer(rxHeader->src);
return;
}
}
// check if we have data for this mac
if (findSlotForMac(rxHeader->src) == -1) {
// no data for this mac, politely tell it to fuck off
sendCancelXfer(rxHeader->src);
return;
}
bool requestDataDownload = false;
if ((blockReq->blockId != requestedData.blockId) || (blockReq->ver != requestedData.ver)) {
// requested block isn't already in the buffer
requestDataDownload = true;
} else {
// requested block is already in the buffer
if (forceBlockDownload) {
if ((getMillis() - nextBlockAttempt) > 380) {
requestDataDownload = true;
pr("FORCED\n");
} else {
pr("IGNORED\n");
}
}
}
// copy blockrequest into requested data
memcpy(&requestedData, blockReq, sizeof(struct blockRequest));
struct MacFrameNormal *txHeader = (struct MacFrameNormal *) (radiotxbuffer + 1);
struct blockRequestAck *blockRequestAck = (struct blockRequestAck *) (radiotxbuffer + sizeof(struct MacFrameNormal) + 2);
radiotxbuffer[0] = sizeof(struct MacFrameNormal) + 1 + sizeof(struct blockRequestAck) + RAW_PKT_PADDING;
radiotxbuffer[sizeof(struct MacFrameNormal) + 1] = PKT_BLOCK_REQUEST_ACK;
if (blockStartTimer == 0) {
if (requestDataDownload) {
// check if we need to download the first block; we need to give the ESP32 some additional time to cache the file
if (blockReq->blockId == 0) {
blockRequestAck->pleaseWaitMs = 450;
} else {
blockRequestAck->pleaseWaitMs = 450;
}
} else {
// block is already in buffer
blockRequestAck->pleaseWaitMs = 50;
}
} else {
blockRequestAck->pleaseWaitMs = 50;
}
blockStartTimer = getMillis() + blockRequestAck->pleaseWaitMs;
memcpy(txHeader->src, mSelfMac, 8);
memcpy(txHeader->dst, rxHeader->src, 8);
txHeader->pan = rxHeader->pan;
txHeader->fcs.frameType = 1;
txHeader->fcs.panIdCompressed = 1;
txHeader->fcs.destAddrType = 3;
txHeader->fcs.srcAddrType = 3;
txHeader->seq = seq++;
addCRC((void *) blockRequestAck, sizeof(struct blockRequestAck));
radioTx(radiotxbuffer);
// save the target for the blockdata
memcpy(dstMac, rxHeader->src, 8);
dstPan = rxHeader->pan;
if (requestDataDownload) {
blockPosition = 0;
espBlockRequest(&requestedData, rxHeader->src);
nextBlockAttempt = getMillis();
}
}
void processAvailDataReq(uint8_t *buffer) {
struct MacFrameBcast *rxHeader = (struct MacFrameBcast *) buffer;
struct AvailDataReq *availDataReq = (struct AvailDataReq *) (buffer + sizeof(struct MacFrameBcast) + 1);
if (!checkCRC(availDataReq, sizeof(struct AvailDataReq))) return;
// prepare tx buffer to send a response
memset(radiotxbuffer, 0, sizeof(struct MacFrameNormal) + sizeof(struct AvailDataInfo) + 2); // 120);
struct MacFrameNormal *txHeader = (struct MacFrameNormal *) (radiotxbuffer + 1);
struct AvailDataInfo *availDataInfo = (struct AvailDataInfo *) (radiotxbuffer + sizeof(struct MacFrameNormal) + 2);
radiotxbuffer[0] = sizeof(struct MacFrameNormal) + 1 + sizeof(struct AvailDataInfo) + RAW_PKT_PADDING;
radiotxbuffer[sizeof(struct MacFrameNormal) + 1] = PKT_AVAIL_DATA_INFO;
// check to see if we have data available for this mac
bool haveData = false;
for (uint8_t c = 0; c < MAX_PENDING_MACS; c++) {
if (pendingDataArr[c].attemptsLeft) {
if (memcmp(pendingDataArr[c].targetMac, rxHeader->src, 8) == 0) {
haveData = true;
memcpy((void *) availDataInfo, &(pendingDataArr[c].availdatainfo), sizeof(struct AvailDataInfo));
break;
}
}
}
// couldn't find data for this mac
if (!haveData) availDataInfo->dataType = DATATYPE_NOUPDATE;
memcpy(txHeader->src, mSelfMac, 8);
memcpy(txHeader->dst, rxHeader->src, 8);
txHeader->pan = rxHeader->dstPan;
txHeader->fcs.frameType = 1;
txHeader->fcs.panIdCompressed = 1;
txHeader->fcs.destAddrType = 3;
txHeader->fcs.srcAddrType = 3;
txHeader->seq = seq++;
addCRC(availDataInfo, sizeof(struct AvailDataInfo));
radioTx(radiotxbuffer);
memset(lastAckMac, 0, 8); // reset lastAckMac, so we can record if we've received exactly one ack packet
espNotifyAvailDataReq(availDataReq, rxHeader->src);
}
void processXferComplete(uint8_t *buffer) {
struct MacFrameNormal *rxHeader = (struct MacFrameNormal *) buffer;
sendXferCompleteAck(rxHeader->src);
if (memcmp(lastAckMac, rxHeader->src, 8) != 0) {
memcpy((void *) lastAckMac, (void *) rxHeader->src, 8);
espNotifyXferComplete(rxHeader->src);
int8_t slot = findSlotForMac(rxHeader->src);
if (slot != -1) pendingDataArr[slot].attemptsLeft = 0;
}
}
// send block data to the tag
void sendPart(uint8_t partNo) {
struct MacFrameNormal *frameHeader = (struct MacFrameNormal *) (radiotxbuffer + 1);
struct blockPart *blockPart = (struct blockPart *) (radiotxbuffer + sizeof(struct MacFrameNormal) + 2);
memset(radiotxbuffer + 1, 0, sizeof(struct blockPart) + sizeof(struct MacFrameNormal));
radiotxbuffer[sizeof(struct MacFrameNormal) + 1] = PKT_BLOCK_PART;
radiotxbuffer[0] = sizeof(struct MacFrameNormal) + sizeof(struct blockPart) + BLOCK_PART_DATA_SIZE + 1 + RAW_PKT_PADDING;
memcpy(frameHeader->src, mSelfMac, 8);
memcpy(frameHeader->dst, dstMac, 8);
blockPart->blockId = requestedData.blockId;
blockPart->blockPart = partNo;
memcpy(&(blockPart->data), blockbuffer + (partNo * BLOCK_PART_DATA_SIZE), BLOCK_PART_DATA_SIZE);
addCRC(blockPart, sizeof(struct blockPart) + BLOCK_PART_DATA_SIZE);
frameHeader->fcs.frameType = 1;
frameHeader->fcs.panIdCompressed = 1;
frameHeader->fcs.destAddrType = 3;
frameHeader->fcs.srcAddrType = 3;
frameHeader->seq = seq++;
frameHeader->pan = dstPan;
radioTx(radiotxbuffer);
}
void sendBlockData() {
if (getBlockDataLength() == 0) {
pr("Invalid block request received, 0 parts..\n");
requestedData.requestedParts[0] |= 0x01;
}
uint8_t partNo = 0;
while (partNo < BLOCK_MAX_PARTS) {
for (uint8_t c = 0; (c < BLOCK_MAX_PARTS) && (partNo < BLOCK_MAX_PARTS); c++) {
if (requestedData.requestedParts[c / 8] & (1 << (c % 8))) {
sendPart(c);
partNo++;
}
}
}
}
void sendXferCompleteAck(uint8_t *dst) {
struct MacFrameNormal *frameHeader = (struct MacFrameNormal *) (radiotxbuffer + 1);
memset(radiotxbuffer + 1, 0, sizeof(struct blockPart) + sizeof(struct MacFrameNormal));
radiotxbuffer[sizeof(struct MacFrameNormal) + 1] = PKT_XFER_COMPLETE_ACK;
radiotxbuffer[0] = sizeof(struct MacFrameNormal) + 1 + RAW_PKT_PADDING;
memcpy(frameHeader->src, mSelfMac, 8);
memcpy(frameHeader->dst, dst, 8);
frameHeader->fcs.frameType = 1;
frameHeader->fcs.panIdCompressed = 1;
frameHeader->fcs.destAddrType = 3;
frameHeader->fcs.srcAddrType = 3;
frameHeader->seq = seq++;
frameHeader->pan = dstPan;
radioTx(radiotxbuffer);
}
void sendCancelXfer(uint8_t *dst) {
struct MacFrameNormal *frameHeader = (struct MacFrameNormal *) (radiotxbuffer + 1);
memset(radiotxbuffer + 1, 0, sizeof(struct blockPart) + sizeof(struct MacFrameNormal));
radiotxbuffer[sizeof(struct MacFrameNormal) + 1] = PKT_CANCEL_XFER;
radiotxbuffer[0] = sizeof(struct MacFrameNormal) + 1 + RAW_PKT_PADDING;
memcpy(frameHeader->src, mSelfMac, 8);
memcpy(frameHeader->dst, dst, 8);
frameHeader->fcs.frameType = 1;
frameHeader->fcs.panIdCompressed = 1;
frameHeader->fcs.destAddrType = 3;
frameHeader->fcs.srcAddrType = 3;
frameHeader->seq = seq++;
frameHeader->pan = dstPan;
radioTx(radiotxbuffer);
}
void sendPong(void *buf) {
struct MacFrameBcast *rxframe = (struct MacFrameBcast *) buf;
struct MacFrameNormal *frameHeader = (struct MacFrameNormal *) (radiotxbuffer + 1);
radiotxbuffer[sizeof(struct MacFrameNormal) + 1] = PKT_PONG;
radiotxbuffer[sizeof(struct MacFrameNormal) + 2] = curChannel;
radiotxbuffer[0] = sizeof(struct MacFrameNormal) + 1 + 1 + RAW_PKT_PADDING;
memcpy(frameHeader->src, mSelfMac, 8);
memcpy(frameHeader->dst, rxframe->src, 8);
radiotxbuffer[1] = 0x41; // fast way to set the appropriate bits
radiotxbuffer[2] = 0xCC; // normal frame
frameHeader->seq = seq++;
frameHeader->pan = rxframe->srcPan;
radioTx(radiotxbuffer);
}
void app_main(void) {
init_nvs();
init_led();
init_second_uart();
esp_event_loop_create_default();
radio_init();
requestedData.blockId = 0xFF;
// clear the array with pending information
memset(pendingDataArr, 0, sizeof(pendingDataArr));
radioSetChannel(curChannel);
radioSetTxPower(10);
pr("RES>\n");
pr("RDY>\n");
housekeepingTimer = getMillis();
while (1) {
while ((getMillis() - housekeepingTimer) < ((1000 * HOUSEKEEPING_INTERVAL) - 100)) {
int8_t ret = commsRxUnencrypted(radiorxbuffer);
if (ret > 1) {
led_set(LED1, 1);
// received a packet, lets see what it is
switch (getPacketType(radiorxbuffer)) {
case PKT_AVAIL_DATA_REQ:
if (ret == 28) {
// old version of the AvailDataReq struct, set all the new fields to zero, so it will pass the CRC
processAvailDataReq(radiorxbuffer);
memset(radiorxbuffer + 1 + sizeof(struct MacFrameBcast) + sizeof(struct oldAvailDataReq), 0,
sizeof(struct AvailDataReq) - sizeof(struct oldAvailDataReq) + 2);
} else if (ret == 40) {
// new version of the AvailDataReq struct
processAvailDataReq(radiorxbuffer);
}
break;
case PKT_BLOCK_REQUEST:
processBlockRequest(radiorxbuffer, 1);
break;
case PKT_BLOCK_PARTIAL_REQUEST:
processBlockRequest(radiorxbuffer, 0);
break;
case PKT_XFER_COMPLETE:
processXferComplete(radiorxbuffer);
break;
case PKT_PING:
sendPong(radiorxbuffer);
break;
case PKT_AVAIL_DATA_SHORTREQ:
// a short AvailDataReq is basically a very short (1 byte payload) packet that requires little preparation on the tx side, for optimal
// battery use bytes of the struct are set 0, so it passes the checksum test, and the ESP32 can detect that no interesting payload is
// sent
if (ret == 18) {
memset(radiorxbuffer + 1 + sizeof(struct MacFrameBcast), 0, sizeof(struct AvailDataReq) + 2);
processAvailDataReq(radiorxbuffer);
}
break;
default:
pr("t=%02X\n", getPacketType(radiorxbuffer));
break;
}
led_set(LED1, 0);
}
uint8_t curr_char;
while (getRxCharSecond(&curr_char)) processSerial(curr_char);
if (blockStartTimer) {
// BUG: uint32 overflowing; this will break every once in a while. Don't know how to fix this other than ugly global variables
if (getMillis() > blockStartTimer) {
sendBlockData();
blockStartTimer = 0;
}
}
}
for (uint8_t cCount = 0; cCount < MAX_PENDING_MACS; cCount++) {
if (pendingDataArr[cCount].attemptsLeft == 1) {
if (pendingDataArr[cCount].availdatainfo.dataType != DATATYPE_NOUPDATE) {
espNotifyTimeOut(pendingDataArr[cCount].targetMac);
}
pendingDataArr[cCount].attemptsLeft = 0;
} else if (pendingDataArr[cCount].attemptsLeft > 1) {
pendingDataArr[cCount].attemptsLeft--;
if (pendingDataArr[cCount].availdatainfo.nextCheckIn) pendingDataArr[cCount].availdatainfo.nextCheckIn--;
}
}
housekeepingTimer = getMillis();
}
}

View File

@@ -0,0 +1 @@
#pragma once

View File

@@ -0,0 +1,176 @@
#ifndef _PROTO_H_
#define _PROTO_H_
#include <stdint.h>
#define LED1 22
#define LED2 23
#define PROTO_PAN_ID (0x4447) // PAN ID compression shall be used
#define RADIO_MAX_PACKET_LEN (125) // useful payload, not including the crc
#define ADDR_MODE_NONE (0)
#define ADDR_MODE_SHORT (2)
#define ADDR_MODE_LONG (3)
#define FRAME_TYPE_BEACON (0)
#define FRAME_TYPE_DATA (1)
#define FRAME_TYPE_ACK (2)
#define FRAME_TYPE_MAC_CMD (3)
#define SHORT_MAC_UNUSED (0x10000000UL) // for radioRxFilterCfg's myShortMac
struct MacFcs {
uint8_t frameType : 3;
uint8_t secure : 1;
uint8_t framePending : 1;
uint8_t ackReqd : 1;
uint8_t panIdCompressed : 1;
uint8_t rfu1 : 1;
uint8_t rfu2 : 2;
uint8_t destAddrType : 2;
uint8_t frameVer : 2;
uint8_t srcAddrType : 2;
} __attribute__((packed, aligned(1)));
struct MacFrameFromMaster {
struct MacFcs fcs;
uint8_t seq;
uint16_t pan;
uint8_t dst[8];
uint16_t from;
} __attribute__((packed, aligned(1)));
struct MacFrameNormal {
struct MacFcs fcs;
uint8_t seq;
uint16_t pan;
uint8_t dst[8];
uint8_t src[8];
} __attribute__((packed, aligned(1)));
struct MacFrameBcast {
struct MacFcs fcs;
uint8_t seq;
uint16_t dstPan;
uint16_t dstAddr;
uint16_t srcPan;
uint8_t src[8];
} __attribute__((packed, aligned(1)));
#define PKT_AVAIL_DATA_SHORTREQ 0xE3
#define PKT_AVAIL_DATA_REQ 0xE5
#define PKT_AVAIL_DATA_INFO 0xE6
#define PKT_BLOCK_PARTIAL_REQUEST 0xE7
#define PKT_BLOCK_REQUEST_ACK 0xE9
#define PKT_BLOCK_REQUEST 0xE4
#define PKT_BLOCK_PART 0xE8
#define PKT_XFER_COMPLETE 0xEA
#define PKT_XFER_COMPLETE_ACK 0xEB
#define PKT_CANCEL_XFER 0xEC
#define PKT_PING 0xED
#define PKT_PONG 0xEE
struct AvailDataReq {
uint8_t checksum;
uint8_t lastPacketLQI;
int8_t lastPacketRSSI;
int8_t temperature;
uint16_t batteryMv;
uint8_t hwType;
uint8_t wakeupReason;
uint8_t capabilities;
uint16_t tagSoftwareVersion;
uint8_t currentChannel;
uint8_t customMode;
uint8_t reserved[8];
} __attribute__((packed, aligned(1)));
struct oldAvailDataReq {
uint8_t checksum;
uint8_t lastPacketLQI;
int8_t lastPacketRSSI;
int8_t temperature;
uint16_t batteryMv;
uint8_t hwType;
uint8_t wakeupReason;
uint8_t capabilities;
} __attribute__((packed, aligned(1)));
struct AvailDataInfo {
uint8_t checksum;
uint64_t dataVer; // MD5 of potential traffic
uint32_t dataSize;
uint8_t dataType;
uint8_t dataTypeArgument; // extra specification or instruction for the tag (LUT to be used for drawing image)
uint16_t nextCheckIn; // when should the tag check-in again? Measured in minutes
} __attribute__((packed, aligned(1)));
struct pendingData {
struct AvailDataInfo availdatainfo;
uint16_t attemptsLeft;
uint8_t targetMac[8];
} __attribute__((packed, aligned(1)));
struct blockPart {
uint8_t checksum;
uint8_t blockId;
uint8_t blockPart;
uint8_t data[];
} __attribute__((packed, aligned(1)));
struct blockData {
uint16_t size;
uint16_t checksum;
uint8_t data[];
} __attribute__((packed, aligned(1)));
struct burstMacData {
uint16_t offset;
uint8_t targetMac[8];
} __attribute__((packed, aligned(1)));
#define BLOCK_PART_DATA_SIZE 99
#define BLOCK_MAX_PARTS 42
#define BLOCK_DATA_SIZE 4096UL
#define BLOCK_XFER_BUFFER_SIZE BLOCK_DATA_SIZE + sizeof(struct blockData)
#define BLOCK_REQ_PARTS_BYTES 6
struct blockRequest {
uint8_t checksum;
uint64_t ver;
uint8_t blockId;
uint8_t type;
uint8_t requestedParts[BLOCK_REQ_PARTS_BYTES];
} __attribute__((packed, aligned(1)));
struct blockRequestAck {
uint8_t checksum;
uint16_t pleaseWaitMs;
} __attribute__((packed, aligned(1)));
struct espBlockRequest {
uint8_t checksum;
uint64_t ver;
uint8_t blockId;
uint8_t src[8];
} __attribute__((packed, aligned(1)));
struct espXferComplete {
uint8_t checksum;
uint8_t src[8];
} __attribute__((packed, aligned(1)));
struct espAvailDataReq {
uint8_t checksum;
uint8_t src[8];
struct AvailDataReq adr;
} __attribute__((packed, aligned(1)));
struct espSetChannelPower {
uint8_t checksum;
uint8_t channel;
uint8_t power;
} __attribute__((packed, aligned(1)));
#endif

View File

@@ -0,0 +1,95 @@
#include <esp_mac.h>
#include <math.h>
#include <stdarg.h>
#include <string.h>
#include "driver/gpio.h"
#include "driver/uart.h"
#include "esp_err.h"
#include "esp_event.h"
#include "esp_ieee802154.h"
#include "esp_log.h"
#include "esp_phy_init.h"
#include "esp_timer.h"
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/task.h"
#include "main.h"
#include "proto.h"
#include "sdkconfig.h"
#include "soc/uart_struct.h"
#include "soc\lp_uart_reg.h"
#include "radio.h"
#include "utils.h"
#include "led.h"
static const char *TAG = "RADIO";
uint8_t mSelfMac[8];
volatile uint8_t isInTransmit = 0;
QueueHandle_t packet_bufer = NULL;
void esp_ieee802154_receive_done(uint8_t *frame, esp_ieee802154_frame_info_t *frame_info) {
ESP_EARLY_LOGI(TAG, "RX <- : %d", frame[0]);
BaseType_t xHigherPriorityTaskWoken ;
static uint8_t inner_rxPKT[130];
memcpy(inner_rxPKT, &frame[0], frame[0] + 1);
xQueueSendFromISR(packet_bufer, (void *) &inner_rxPKT, &xHigherPriorityTaskWoken );
portYIELD_FROM_ISR_ARG(xHigherPriorityTaskWoken );
}
void esp_ieee802154_transmit_failed(const uint8_t *frame, esp_ieee802154_tx_error_t error) {
isInTransmit = 0;
ESP_EARLY_LOGE(TAG, "TX Err: %d", error);
}
void esp_ieee802154_transmit_done(const uint8_t *frame, const uint8_t *ack, esp_ieee802154_frame_info_t *ack_frame_info) {
isInTransmit = 0;
ESP_EARLY_LOGI(TAG, "TX -> : %d", frame[0]);
}
void radio_init()
{
packet_bufer = xQueueCreate(32, 130);
esp_ieee802154_enable();
radioSetChannel(11);
esp_ieee802154_set_panid(PROTO_PAN_ID);
esp_ieee802154_set_promiscuous(false); // Filter for our mac and PAN
esp_ieee802154_set_coordinator(false);
esp_ieee802154_set_pending_mode(ESP_IEEE802154_AUTO_PENDING_ZIGBEE);
esp_read_mac(mSelfMac, ESP_MAC_IEEE802154);
esp_ieee802154_set_extended_address(mSelfMac);
esp_ieee802154_set_short_address(0xFFFE);
esp_ieee802154_set_rx_when_idle(true);
esp_ieee802154_receive();
}
uint32_t lastZbTx = 0;
bool radioTx(uint8_t *packet)
{
static uint8_t txPKT[130];
led_set(LED2, 1);
while (isInTransmit) {
}
while (getMillis() - lastZbTx < 6) {
}
memcpy(txPKT, packet, packet[0]);
isInTransmit = 1;
lastZbTx = getMillis();
esp_ieee802154_transmit(txPKT, false);
led_set(LED2, 0);
return true;
}
void radioSetChannel(uint8_t ch) { esp_ieee802154_set_channel(ch); }
void radioSetTxPower(uint8_t power) {}
int8_t commsRxUnencrypted(uint8_t *data) {
static uint8_t inner_rxPKT_out[130];
if (xQueueReceive(packet_bufer, (void *) &inner_rxPKT_out, pdMS_TO_TICKS(100)) == pdTRUE) {
memcpy(data, &inner_rxPKT_out[1], inner_rxPKT_out[0] + 1);
return inner_rxPKT_out[0] - 2;
}
return 0;
}

View File

@@ -0,0 +1,9 @@
#pragma once
extern uint8_t mSelfMac[8];
void radio_init();
bool radioTx(uint8_t *packet);
void radioSetChannel(uint8_t ch);
void radioSetTxPower(uint8_t power);
int8_t commsRxUnencrypted(uint8_t *data);

View File

@@ -0,0 +1,260 @@
#include <esp_mac.h>
#include <math.h>
#include <stdarg.h>
#include <string.h>
#include "driver/gpio.h"
#include "driver/uart.h"
#include "esp_err.h"
#include "esp_event.h"
#include "esp_ieee802154.h"
#include "esp_log.h"
#include "esp_phy_init.h"
#include "esp_timer.h"
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/task.h"
#include "main.h"
#include "nvs.h"
#include "nvs_flash.h"
#include "proto.h"
#include "sdkconfig.h"
#include "soc/uart_struct.h"
#include "soc\lp_uart_reg.h"
static const char *TAG = "SECOND_UART";
#define BUF_SIZE (1024)
#define RD_BUF_SIZE (BUF_SIZE)
static QueueHandle_t uart0_queue;
#define MAX_BUFF_POS 8000
volatile int curr_buff_pos = 0;
volatile int worked_buff_pos = 0;
volatile uint8_t buff_pos[MAX_BUFF_POS + 5];
static void uart_event_task(void *pvParameters);
void init_second_uart() {
uart_config_t uart_config = {
.baud_rate = 115200,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.source_clk = UART_SCLK_DEFAULT,
};
ESP_ERROR_CHECK(uart_driver_install(1, BUF_SIZE * 2, BUF_SIZE * 2, 20, &uart0_queue, 0));
ESP_ERROR_CHECK(uart_param_config(1, &uart_config));
ESP_ERROR_CHECK(uart_set_pin(1, 3, 2, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
xTaskCreate(uart_event_task, "uart_event_task", 16384, NULL, 12, NULL);
}
void uartTx(uint8_t data) { uart_write_bytes(1, (const char *) &data, 1); }
bool getRxCharSecond(uint8_t *newChar) {
if (curr_buff_pos != worked_buff_pos) {
*newChar = buff_pos[worked_buff_pos];
worked_buff_pos++;
worked_buff_pos %= MAX_BUFF_POS;
return true;
}
return false;
}
static void uart_event_task(void *pvParameters) {
uart_event_t event;
uint8_t *dtmp = (uint8_t *) malloc(RD_BUF_SIZE);
for (;;) {
if (xQueueReceive(uart0_queue, (void *) &event, (TickType_t) portMAX_DELAY)) {
bzero(dtmp, RD_BUF_SIZE);
switch (event.type) {
case UART_DATA:
uart_read_bytes(1, dtmp, event.size, portMAX_DELAY);
for (int i = 0; i < event.size; i++) {
buff_pos[curr_buff_pos] = dtmp[i];
curr_buff_pos++;
curr_buff_pos %= MAX_BUFF_POS;
}
break;
default:
ESP_LOGI(TAG, "uart event type: %d", event.type);
break;
}
}
}
free(dtmp);
dtmp = NULL;
vTaskDelete(NULL);
}
static void printchar(char **str, int c) {
if (str) {
**str = c;
++(*str);
} else {
uart_write_bytes(1, (const char *) &c, 1);
}
}
#define PAD_RIGHT 1
#define PAD_ZERO 2
static int prints(char **out, const char *string, int width, int pad) {
register int pc = 0, padchar = ' ';
if (width > 0) {
register int len = 0;
register const char *ptr;
for (ptr = string; *ptr; ++ptr) ++len;
if (len >= width)
width = 0;
else
width -= len;
if (pad & PAD_ZERO) padchar = '0';
}
if (!(pad & PAD_RIGHT)) {
for (; width > 0; --width) {
printchar(out, padchar);
++pc;
}
}
for (; *string; ++string) {
printchar(out, *string);
++pc;
}
for (; width > 0; --width) {
printchar(out, padchar);
++pc;
}
return pc;
}
/* the following should be enough for 32 bit int */
#define PRINT_BUF_LEN 12
static int printi(char **out, int i, int b, int sg, int width, int pad, int letbase) {
char print_buf[PRINT_BUF_LEN];
register char *s;
register int t, neg = 0, pc = 0;
register unsigned int u = i;
if (i == 0) {
print_buf[0] = '0';
print_buf[1] = '\0';
return prints(out, print_buf, width, pad);
}
if (sg && b == 10 && i < 0) {
neg = 1;
u = -i;
}
s = print_buf + PRINT_BUF_LEN - 1;
*s = '\0';
while (u) {
t = u % b;
if (t >= 10) t += letbase - '0' - 10;
*--s = t + '0';
u /= b;
}
if (neg) {
if (width && (pad & PAD_ZERO)) {
printchar(out, '-');
++pc;
--width;
} else {
*--s = '-';
}
}
return pc + prints(out, s, width, pad);
}
static int print(char **out, const char *format, va_list args) {
register int width, pad;
register int pc = 0;
char scr[2];
for (; *format != 0; ++format) {
if (*format == '%') {
++format;
width = pad = 0;
if (*format == '\0') break;
if (*format == '%') goto out;
if (*format == '-') {
++format;
pad = PAD_RIGHT;
}
while (*format == '0') {
++format;
pad |= PAD_ZERO;
}
for (; *format >= '0' && *format <= '9'; ++format) {
width *= 10;
width += *format - '0';
}
if (*format == 's') {
register char *s = (char *) va_arg(args, int);
pc += prints(out, s ? s : "(null)", width, pad);
continue;
}
if (*format == 'd') {
pc += printi(out, va_arg(args, int), 10, 1, width, pad, 'a');
continue;
}
if (*format == 'x') {
pc += printi(out, va_arg(args, int), 16, 0, width, pad, 'a');
continue;
}
if (*format == 'X') {
pc += printi(out, va_arg(args, int), 16, 0, width, pad, 'A');
continue;
}
if (*format == 'u') {
pc += printi(out, va_arg(args, int), 10, 0, width, pad, 'a');
continue;
}
if (*format == 'c') {
/* char are converted to int then pushed on the stack */
scr[0] = (char) va_arg(args, int);
scr[1] = '\0';
pc += prints(out, scr, width, pad);
continue;
}
} else {
out:
printchar(out, *format);
++pc;
}
}
if (out) **out = '\0';
va_end(args);
return pc;
}
int u_printf(const char *format, ...) {
va_list args;
va_start(args, format);
print(0, format, args);
return 0;
}
int u_sprintf(char *out, const char *format, ...) {
va_list args;
va_start(args, format);
return print(&out, format, args);
}
void u_array_printf(unsigned char *data, unsigned int len) {
u_printf("{");
for (int i = 0; i < len; ++i) {
u_printf("%X%s", data[i], i < (len) -1 ? ":" : " ");
}
u_printf("}\n");
}

View File

@@ -0,0 +1,14 @@
#pragma once
void init_second_uart();
void uartTx(uint8_t data);
bool getRxCharSecond(uint8_t *newChar);
int u_printf(const char *fmt, ...);
int u_sprintf(char *s, const char *fmt, ...);
void u_array_printf(unsigned char *data, unsigned int len);
#define pr u_printf
#define sprf u_sprintf
#define array_prf u_array_printf

View File

@@ -0,0 +1,36 @@
#include <esp_mac.h>
#include <math.h>
#include <stdarg.h>
#include <string.h>
#include "driver/gpio.h"
#include "driver/uart.h"
#include "esp_err.h"
#include "esp_event.h"
#include "esp_ieee802154.h"
#include "esp_log.h"
#include "esp_phy_init.h"
#include "esp_timer.h"
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/task.h"
#include "main.h"
#include "proto.h"
#include "sdkconfig.h"
#include "soc/uart_struct.h"
#include "soc\lp_uart_reg.h"
#include "nvs_flash.h"
void delay(int ms) { vTaskDelay(pdMS_TO_TICKS(ms)); }
uint32_t getMillis() { return (uint32_t) (esp_timer_get_time() / 1000); }
void init_nvs()
{
// Initialize NVS
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK( ret );
}

View File

@@ -0,0 +1,5 @@
#pragma once
void delay(int ms);
uint32_t getMillis();
void init_nvs();

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,9 @@
CONTIKI_PROJECT = cc2531_OEPL
DEFINES+=MODELS_CONF_CC2531_USB_STICK=1 CC2530_RF_LOW_POWER_RX=0
all: $(CONTIKI_PROJECT)
CONTIKI = ./contiki
CONTIKI_WITH_RIME = 1
include $(CONTIKI)/Makefile.include

View File

@@ -0,0 +1 @@
TARGET = cc2530dk

View File

@@ -0,0 +1,3 @@
__pycache__
/.idea
/cache

View File

@@ -0,0 +1,3 @@
This codebase is copied from here: https://github.com/Westwoodlabs/WAMP2023-Badge/tree/main/OpenEPaperLink-PyStation
All Credit belongs to the creator

View File

@@ -0,0 +1,69 @@
import ctypes
"""
helpers to interact with c-struct based data, which we transfer with the AP
"""
def c_format(value):
"""
internal formatting helper for ctypes-values.
most importantly renders array-contents as lists or hex (if the data is in bytes)
"""
if isinstance(value, ctypes.Array):
if value._type_ == ctypes.c_byte or value._type_ == ctypes.c_ubyte:
return bytes(value).hex()
else:
return [str(v) for v in value]
else:
return str(value)
def c_pretty(inst):
"""
decorator for ctypes.Structure classes, which adds automatic pretty-printing
"""
def __repr__(self) -> str:
values = ", ".join(f"{name}={c_format(value)}" for name, value in self._asdict().items())
return f"<{self.__class__.__name__}: {values}>"
def _asdict(self) -> dict:
return {field[0]: getattr(self, field[0]) for field in self._fields_}
inst.__repr__ = __repr__
inst._asdict = _asdict
return inst
def hex_reverse_bytes(val, delimiter=":"):
"""
reverses (by copy) the given bytearray and encodes it as colon-delimited hex.
this is mostly used to pretty-print tag MAC addresses, which are transferred in reverse
"""
reversed = bytes(val)[::-1]
return reversed.hex() if delimiter is None else reversed.hex(delimiter)
def hex_bytes(val, delimiter=None):
"""
encodes the given bytes / bytes-convertable into a (optionally delimited) hex-string
"""
return bytes(val).hex() if delimiter is None else bytes(val).hex(delimiter)
def try_decode(buffer, prefix, typ):
"""
tries to decode the given c-struct (typ) from the given buffer,
if the buffer starts with the given prefix.
also returns an indicator, whether the buffer might be decodable to this
type, if the buffer gets more contents / longer.
"""
if len(buffer) < len(prefix):
may_match = buffer == prefix[:len(buffer)]
return may_match, None
may_match = buffer[:len(prefix)] == prefix
if may_match and len(buffer) >= len(prefix) + ctypes.sizeof(typ):
val = typ.from_buffer_copy(buffer, len(prefix))
buffer[:ctypes.sizeof(typ) + len(prefix)] = b''
return True, val
return may_match, None

View File

@@ -0,0 +1,42 @@
import json
import os
from json import JSONDecodeError
from typing import Callable, Any, List
"""
a stupid, trivial and probably suboptimal database implementation,
that we use to preserve some runtime-information about tags
"""
class TagDb:
def __init__(self):
self.tags = {}
self.listeners: List[Callable[[dict], Any]] = []
def get_tag(self, mac):
if mac not in self.tags:
self.tags[mac] = {}
return self.tags[mac]
def save(self):
os.makedirs('cache', exist_ok=True)
with open('cache/tagdb.json', 'wt') as f:
json.dump(self.tags, f, indent=4)
def load(self):
if os.path.exists("cache/tagdb.json"):
try:
with open("cache/tagdb.json", 'rt') as f:
self.tags = json.load(f)
for listener in self.listeners:
listener(self.tags)
except JSONDecodeError:
print("tagdb.json is invalid. ignoring")
def notify_change(self, tag_key):
for listener in self.listeners:
listener({tag_key: self.tags[tag_key]})
def on_update(self, listener: Callable[[dict], Any]):
self.listeners.append(listener)

View File

@@ -0,0 +1,274 @@
import time
import serial
import ctypes
import hashlib
import typing
import logging
from db import TagDb
from pic_fs import PicFS
from time import sleep
from c_util import hex_reverse_bytes, hex_bytes
from proto_def import AvailableDataRequest, AvailDataInfo, BlockRequest, BlockHeader, XferComplete
BLOCK_SIZE = 4096
T = typing.TypeVar('T')
ser = serial.Serial("COM26", 115200, timeout=0.1)
ser.flushInput()
start = 0
arr = bytearray(BLOCK_SIZE)
arrIn = bytearray(BLOCK_SIZE)
rx_state = 0
last_rx_millis = 0
bytesRemain = 0
byteInCount = 0
class Logic:
db = TagDb()
pic = PicFS()
def __init__(self):
self.log = logging.getLogger("logic")
self.log.setLevel(logging.DEBUG)
def cmpare(self, array, string):
if array == string:
return True
else:
return False
def parseUart(self, cha):
global rx_state
global last_rx_millis
global arr
global arrIn
global bytesRemain
global byteInCount
if round(time.time()*1000) - last_rx_millis > 25:
rx_state = 0
last_rx_millis = round(time.time()*1000)
if rx_state == 0: #ZBS_RX_WAIT_HEADER
arr[0] = arr[1]
arr[1] = arr[2]
arr[2] = arr[3]
arr[3] = cha
if self.cmpare(arr[0:4], b"ADR>"):
rx_state = 1 #ZBS_RX_WAIT_SDA
bytesRemain = 30
byteInCount = 0
elif self.cmpare(arr[0:4], b"RQB>"):
rx_state = 2 #ZBS_RX_WAIT_RQB
bytesRemain = 18
byteInCount = 0
elif self.cmpare(arr[0:4], b"XFC>"):
rx_state = 3 #ZBS_RX_WAIT_XFC
bytesRemain = 9
byteInCount = 0
elif self.cmpare(arr[0:4], b"ACK>"):
print("ACKed")
rx_state = 0 #ZBS_RX_WAIT_HEADER
elif rx_state == 1: #ZBS_RX_WAIT_SDA
arrIn[byteInCount] = cha
byteInCount = byteInCount + 1
bytesRemain = bytesRemain-1
if bytesRemain == 0:
rx_state = 0 #ZBS_RX_WAIT_HEADER
data_request = AvailableDataRequest.from_buffer_copy(arrIn)
self.handle_adr(data_request);
elif rx_state == 2: #ZBS_RX_WAIT_RQB
arrIn[byteInCount] = cha
byteInCount = byteInCount + 1
bytesRemain = bytesRemain-1
if bytesRemain == 0:
rx_state = 0 #ZBS_RX_WAIT_HEADER
block_request = BlockRequest.from_buffer_copy(arrIn)
self.handle_rqb(block_request);
elif rx_state == 3: #ZBS_RX_WAIT_XFC
arrIn[byteInCount] = cha
byteInCount = byteInCount + 1
bytesRemain = bytesRemain-1
if bytesRemain == 0:
rx_state = 0 #ZBS_RX_WAIT_HEADER
xfer_request = XferComplete.from_buffer_copy(arrIn)
self.handle_xfc(xfer_request);
def main_loop(self):
global start
while True:
b = ser.read(1)
if len(b):
self.parseUart(b[0])
def handle_adr(self, adr: AvailableDataRequest):
data_type = 0;
"""
handles an AvailableDataRequest, which is issued by the AP
whenever a tag checks-in with the AP. The tag provides some
useful data - which we store.
we then check, if there is new data available for the tag -
hence the method name. if so, we generate an AvailDataInfo,
which the AP will deliver to the tag on its next check-in
"""
self.log.debug(f"{adr}")
pretty_mac = hex_reverse_bytes(adr.sourceMac)
self.log.info(f"Checkin from MAC {pretty_mac}")
# lookup the tag in the DB, might create it, if it's new
tag = self.db.get_tag(pretty_mac)
tag['lastCheckin'] = int(time.time())
if adr.lastPacketLQI != 0:
tag['lastPacketLQI'] = adr.lastPacketLQI
if adr.lastPacketRSSI != 0:
tag['lastPacketRSSI'] = adr.lastPacketRSSI
if adr.temperature != 0:
tag['temperature'] = adr.temperature
if adr.batteryMv != 0:
tag['batteryMv'] = adr.batteryMv
tag['hwType'] = adr.hwType
mac = hex_reverse_bytes(adr.sourceMac, None)
if self.pic.has_image(mac + ".bin"):
# type 0x03 firmware update
data_type = 0x03
data = self.pic.get_image(mac + ".bin", data_type)
elif self.pic.has_image(mac + ".png"):
# we just assume, the picture and tag are bi-color (black and red)
# type 0x20 would be "just black"
data_type = 0x21
data = self.pic.get_image(mac + ".png", data_type)
else:
self.log.info(f"No image found for {pretty_mac}")
return
# the tag and AP firmware uses a 64-bit "version" to check, whether
# it has an up-to date image. we currently just use half of a MD5-hash
# of a picture to generate a new value, if the picture data changes.
md5 = hashlib.md5(data).digest()
data_version = hex_bytes(md5[:8])
# out tag-db knows (caches) which imageVersion was last acknowledged (XFC-command)
# by the tag / AP. if the tags current image version is identical to our local image version,
# the tag is up-to-date
if 'imageVersion' not in tag or tag['imageVersion'] != data_version:
tag['pendingVersion'] = data_version
sda = AvailDataInfo(
checksum=0,
dataVer=(ctypes.c_ubyte * 8).from_buffer_copy(md5[:8]),
dataSize=len(data),
dataType=data_type,
# the tag firmware know multiple "LUT"s (whatever that means).
# it seems to be related to the updating of the epaper contents.
# EPD_LUT_DEFAULT 0
# EPD_LUT_NO_REPEATS 1
# EPD_LUT_FAST_NO_REDS 2
# EPD_LUT_FAST 3
dataTypeArgument=1,
nextCheckIn=0, # default taken from ESP firmware
attemptsLeft=60 * 24, # default taken from ESP firmware
targetMac=adr.sourceMac,
)
# OpenEPaper checksums seem to be quite literal "checksums" even though they call them CRCs
# in their firmware. they're just 8- or 16-bit integers, which overflow while adding.
# we recreate this native overflow behaviour by MOD-0x100-ing them
sda.checksum = sum(bytes(sda)[1:]) % 0x100
self.log.info(f"Tag needs new data. sending {sda}")
ser.write(b'SDA>')
ser.write(bytes(sda))
else:
self.log.info("Tag image is up to date")
self.db.notify_change(pretty_mac)
# persist our database changes
# NOTE: this is stupid, simple and lazy database implementation,
# but sufficient for now. we can change to something fancy later.
self.db.save()
def handle_rqb(self, rqb: BlockRequest):
"""
Handles a BlockRequest, which is issued by the AP when it needs data
to transfer to a tag. data is split-up into blocks of 4096 bytes, prefixed
with a header and sent with a special encoding (XORd with 0xAA) - only god
knows why, as this only makes sense, if the receiver can recover timing info
from the signal (maybe?)
"""
data_type = 0
filename = ""
self.log.debug(f"{rqb}")
self.log.info(f"Got RQB for MAC {hex_reverse_bytes(rqb.srcMac)}")
mac = hex_reverse_bytes(rqb.srcMac, None)
if self.pic.has_image(mac + ".bin"):
data_type = 0x03
filename = mac + ".bin"
cancel = False
elif self.pic.has_image(mac + ".png"):
data_type = 0x21
filename = mac + ".png"
cancel = False
else:
self.log.warning(f"No image found for {hex_reverse_bytes(rqb.srcMac)}")
cancel = True
if cancel:
# cancel block request
self.log.info("Sending: cancel block request")
cxd = AvailDataInfo(targetMac=rqb.srcMac)
cxd.checksum = sum(bytes(cxd)[1:]) & 0x100
ser.write(b'CXD>')
ser.write(bytes(cxd))
else:
# we just assume, that the data is black-and-red (0x21) - see handle_adr()
data = self.pic.get_image(filename, data_type)
offset = rqb.blockId * BLOCK_SIZE
length = min(len(data) - offset, BLOCK_SIZE)
self.log.info(f"Transmitting block {rqb.blockId} of length {length}")
transmit_data = data[offset:offset + length]
self.log.debug(f"Block bytes: {transmit_data.hex()}")
header = bytes(BlockHeader(
length=length,
# see handle_adr() for checksum-foo
checksum=sum(transmit_data) % 0x10000
))
ser.write(b'>D>')
# waiting for a little bit, but not too long, seems to be required for successful transmission.
# this may be due to the fact, that serial-command processing and the below bulk-transfer are
# separate processes, as the below bulk-transfer is implemented as a kind of interrupt-driven DMA
# in the AP.
sleep(.05)
# the AP-firmware XORs the data back on retrieval
ser.write(bytes(b ^ 0xAA for b in header))
ser.write(bytes(b ^ 0xAA for b in transmit_data))
# the AP-firmware expects bulk-transfers to always be 4100 bytes (due to the DMA mechanism)
# thus we need to fill the block with junk (0xFF when unXORd in this case)
ser.write(bytes([0xAA] * (BLOCK_SIZE - length)))
def handle_xfc(self, val: XferComplete):
"""
handles a XFC (transfer-complete) message, which is issued by the AP,
when it "knows" that a tag has fully received the data, announced by a previous
AvailDataInfo. this may happen without any actual data-transfer - e.g. if the
tag has fetched the data from EPROM
"""
pretty_mac = hex_reverse_bytes(val.srcMac)
mac = hex_reverse_bytes(val.srcMac, None)
self.log.info(f"Got XFC for Mac {pretty_mac}")
tag = self.db.get_tag(pretty_mac)
if 'pendingVersion' not in tag:
# should not happen but also is uncritical
self.log.warning("WARNING: pendingVersion not found on XFC")
else:
# persist fact, that the tags imageVersion now is identical to its pendingVersion
tag['imageVersion'] = tag['pendingVersion']
if self.pic.has_image(mac + ".bin"):
self.pic.remove_image(mac + ".bin")
self.db.notify_change(pretty_mac)
self.db.save()
if __name__ == '__main__':
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
Logic().main_loop()

View File

@@ -0,0 +1,86 @@
from PIL import Image
from bitarray import bitarray
import os
"""
helper for loading pictures from the filesystem.
"""
class PicFS:
def __init__(self):
# caches preload and -converted image-data for faster retrieval
# this is actually quite critical, as RQB (AP BlockRequests) must
# be responded to very timely (within a couple dozen milliseconds)
self.data_cache = {}
def _get_path(self, image_path):
# TODO: make picture path better / configurable
return "cache/" + image_path
def has_image(self, image_path):
return os.path.exists(self._get_path(image_path))
def get_image(self, image_path, data_type):
"""
gets the given image from cache (if present)
or loads it otherwise.
TODO: add a mechanism to dispose of stale entries,
as they accumulate memory indefinitely right now
"""
mtime = os.path.getmtime(self._get_path(image_path))
key = (image_path, data_type, mtime)
if key in self.data_cache:
return self.data_cache[key]
data = self.load_image(image_path, data_type)
self.data_cache[key] = data
return data
def remove_image(self, image_path):
"""
removes the given image from cache (if present)
"""
for key in list(self.data_cache.keys()):
if key[0] == image_path:
del self.data_cache[key]
os.remove(self._get_path(image_path))
def load_image(self, image_path, data_type):
if data_type == 0x20:
# just black
colors = [(0x00, 0x00, 0x00)]
elif data_type == 0x21:
colors = [(0x00, 0x00, 0x00), (0xFF, 0x00, 0x00)]
elif data_type == 0x03:
# firmware update
colors = []
else:
raise "no such data_type " + str(data_type)
if data_type == 0x03:
with open(self._get_path(image_path), 'rb') as file:
return bytearray(file.read())
else:
image = Image.open(self._get_path(image_path))
width, height = image.size
pixel_colors = bitarray()
# the tag picture format is:
# - one full frame per color
# - each frame consist of $height columns of $with pixels
# Note: so the image is rotated by 90-deg
# - each column (line from src image) is transferred in reverse
# - each pixel is one bit (the epaper only knows "WHITE" (0) or "COLOR" (1))
# due to the below algorithm, all colors, except for exact matches of the defined
# colors, are ignored (remain white)
for c in colors: # one frame per color
for x in range(width - 1, -1, -1): # transfer lines (columns in output) in reverse
for y in range(height):
pixel = image.getpixel((x, y))
is_similar = abs(pixel[0] - c[0]) < 128 \
and abs(pixel[1] - c[1]) < 128 \
and abs(pixel[2] - c[2]) < 128
# each pixel is on bit (True or False)
pixel_colors.append(is_similar)
return pixel_colors.tobytes() # bitarray handles packing of 8-booleans to one byte

View File

@@ -0,0 +1,68 @@
import ctypes
from c_util import c_pretty
"""
c-structures are copies from AP firmware.
used for serial communication.
"""
@c_pretty
class AvailableDataRequest(ctypes.Structure):
_fields_ = (
('outerChecksum', ctypes.c_uint8),
('sourceMac', ctypes.c_uint8 * 8),
('innerChecksum', ctypes.c_uint8),
('lastPacketLQI', ctypes.c_uint8),
('lastPacketRSSI', ctypes.c_int8),
('temperature', ctypes.c_int8),
('batteryMv', ctypes.c_uint16),
('hwType', ctypes.c_uint8),
('wakeupReason', ctypes.c_uint8),
)
@c_pretty
class AvailDataInfo(ctypes.Structure):
_pack_ = 1
_fields_ = (
('checksum', ctypes.c_uint8),
('dataVer', ctypes.c_uint8 * 8), # MD5 (first half) of potential traffic
('dataSize', ctypes.c_uint32),
('dataType', ctypes.c_uint8), # allows for 16 different datatypes
# extra specification or instruction for the tag (LUT to be used for drawing image)
('dataTypeArgument', ctypes.c_uint8),
('nextCheckIn', ctypes.c_uint16), # when should the tag check-in again? Measured in minutes
('attemptsLeft', ctypes.c_uint16),
('targetMac', ctypes.c_uint8 * 8),
)
@c_pretty
class BlockRequest(ctypes.Structure):
_pack_ = 1
_fields_ = (
('checksum', ctypes.c_uint8),
('dataVer', ctypes.c_uint8 * 8), # MD5 (first half) of potential traffic
('blockId', ctypes.c_uint8),
('srcMac', ctypes.c_uint8 * 8),
)
@c_pretty
class BlockHeader(ctypes.Structure):
_pack_ = 1
_fields_ = (
('length', ctypes.c_uint16),
('checksum', ctypes.c_uint16),
)
@c_pretty
class XferComplete(ctypes.Structure):
_pack_ = 1
_fields_ = (
('checksum', ctypes.c_uint8),
('srcMac', ctypes.c_uint8 * 8),
)

View File

@@ -0,0 +1,931 @@
#include <stdint.h>
#include <stdlib.h>
#include "contiki.h"
#include "net/packetbuf.h"
#include "debug.h"
#include "lib/ringbuf.h"
#include "contiki-conf.h"
#include "dev/io-arch.h"
#include "net/netstack.h"
#include "proto.h"
static struct etimer et;
#define BUFSIZE 128
uint8_t cmd_buf[BUFSIZE];
static struct ringbuf rxbuf;
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
const uint8_t channelList[6] = {11, 15, 20, 25, 26, 27};
void radioTx1(uint8_t *buff)
{
NETSTACK_RADIO.send((uint8_t *)&buff[1], buff[0] - 2);
}
void radioTx(uint8_t *buff)
{
clock_delay_usec(500);
NETSTACK_RADIO.send((uint8_t *)&buff[1], buff[0] - 2);
}
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
#define true 1
#define false 0
#define bool uint8_t
#define DATATYPE_NOUPDATE 0
#define HW_TYPE 0xFF
#define MAX_PENDING_MACS 6
#define HOUSEKEEPING_INTERVAL 60UL
struct pendingData pendingDataArr[MAX_PENDING_MACS];
// VERSION GOES HERE!
uint16_t version = 0x0017;
#define RAW_PKT_PADDING 2
uint8_t radiotxbuffer[128];
uint8_t radiorxbuffer[128];
uint8_t mSelfMac[8];
struct blockRequest requestedData = {0}; // holds which data was requested by the tag
uint8_t dstMac[8]; // target for the block transfer
uint16_t dstPan; //
static uint32_t blockStartTimer = 0; // reference that holds when the AP sends the next block
// extern bool __idata serialBypassActive; // if the serial bypass is disabled, saves bytes straight to the block buffer
uint32_t nextBlockAttempt = 0; // reference time for when the AP can request a new block from the ESP32
uint8_t seq = 0; // holds current sequence number for transmission
uint8_t blockbuffer[BLOCK_XFER_BUFFER_SIZE + 5]; // block transfer buffer
uint8_t lastAckMac[8] = {0};
uint8_t *blockXferBuffer = blockbuffer;
// these variables hold the current mac were talking to
#define CONCURRENT_REQUEST_DELAY (1200UL / 8)
uint32_t lastBlockRequest = 0;
uint8_t lastBlockMac[8];
uint8_t curChannel = 11;
uint8_t curPower = 10;
uint8_t curPendingData = 0;
uint8_t curNoUpdate = 0;
void sendXferCompleteAck(uint8_t *dst);
void sendCancelXfer(uint8_t *dst);
void espNotifyAPInfo();
// tools
void addCRC(void *p, uint8_t len)
{
uint8_t total = 0;
for (uint8_t c = 1; c < len; c++)
{
total += ((uint8_t *)p)[c];
}
((uint8_t *)p)[0] = total;
}
bool checkCRC(void *p, uint8_t len)
{
uint8_t total = 0;
for (uint8_t c = 1; c < len; c++)
{
total += ((uint8_t *)p)[c];
}
return ((uint8_t *)p)[0] == total;
}
uint8_t getPacketType(void *buffer)
{
struct MacFcs *fcs = buffer;
if ((fcs->frameType == 1) && (fcs->destAddrType == 2) && (fcs->srcAddrType == 3) && (fcs->panIdCompressed == 0))
{
// broadcast frame
uint8_t type = ((uint8_t *)buffer)[sizeof(struct MacFrameBcast)];
return type;
}
else if ((fcs->frameType == 1) && (fcs->destAddrType == 3) && (fcs->srcAddrType == 3) && (fcs->panIdCompressed == 1))
{
// normal frame
uint8_t type = ((uint8_t *)buffer)[sizeof(struct MacFrameNormal)];
return type;
}
return 0;
}
uint8_t getBlockDataLength()
{
uint8_t partNo = 0;
for (uint8_t c = 0; c < BLOCK_MAX_PARTS; c++)
{
if (requestedData.requestedParts[c / 8] & (1 << (c % 8)))
{
partNo++;
}
}
return partNo;
}
// pendingdata slot stuff
int8_t findSlotForMac(const uint8_t *mac)
{
for (uint8_t c = 0; c < MAX_PENDING_MACS; c++)
{
// if (u64_isEq((uint64_t *)mac, (uint64_t *)&(pendingDataArr[c].targetMac))) { // this costs 1 sloc :(
if (memcmp(mac, ((uint8_t *)&(pendingDataArr[c].targetMac)), 8) == 0)
{
if (pendingDataArr[c].attemptsLeft != 0)
{
return c;
}
}
}
return -1;
}
int8_t findFreeSlot()
{
for (uint8_t c = 0; c < MAX_PENDING_MACS; c++)
{
if (pendingDataArr[c].attemptsLeft == 0)
{
return c;
}
}
return -1;
}
int8_t findSlotForVer(const uint8_t *ver)
{
for (uint8_t c = 0; c < MAX_PENDING_MACS; c++)
{
// if (u64_isEq((uint64_t *)ver, (uint64_t *)&(pendingDataArr[c].availdatainfo.dataVer))) {
if (memcmp(ver, ((uint8_t *)&(pendingDataArr[c].availdatainfo.dataVer)), 8) == 0)
{
if (pendingDataArr[c].attemptsLeft != 0)
return c;
}
}
return -1;
}
void deleteAllPendingDataForVer(const uint8_t *ver)
{
int8_t slot = -1;
do
{
slot = findSlotForVer(ver);
if (slot != -1)
pendingDataArr[slot].attemptsLeft = 0;
} while (slot != -1);
}
void deleteAllPendingDataForMac(const uint8_t *mac)
{
int8_t slot = -1;
do
{
slot = findSlotForMac(mac);
if (slot != -1)
pendingDataArr[slot].attemptsLeft = 0;
} while (slot != -1);
}
void countSlots()
{
curPendingData = 0;
curNoUpdate = 0;
for (uint8_t c = 0; c < MAX_PENDING_MACS; c++)
{
if (pendingDataArr[c].attemptsLeft != 0)
{
if (pendingDataArr[c].availdatainfo.dataType != 0)
{
curPendingData++;
}
else
{
curNoUpdate++;
}
}
}
}
// processing serial data
#define ZBS_RX_WAIT_HEADER 0
#define ZBS_RX_WAIT_SDA 1 // send data avail
#define ZBS_RX_WAIT_CANCEL 2 // cancel traffic for mac
#define ZBS_RX_WAIT_SCP 3 // set channel power
#define ZBS_RX_WAIT_BLOCKDATA 4
int blockPosition = 0;
void processSerial(uint8_t lastchar)
{
static uint8_t cmdbuffer[4];
static uint8_t RXState = 0;
static uint8_t serialbuffer[48];
static uint8_t *serialbufferp;
static uint8_t bytesRemain = 0;
static uint32_t lastSerial = 0;
if ((clock_time() - lastSerial) > (800 / 8))
{
RXState = ZBS_RX_WAIT_HEADER;
lastSerial = clock_time();
}
else
{
lastSerial = clock_time();
}
// uartTx(lastchar); echo
switch (RXState)
{
case ZBS_RX_WAIT_HEADER:
// shift characters in
for (uint8_t c = 0; c < 3; c++)
{
cmdbuffer[c] = cmdbuffer[c + 1];
}
cmdbuffer[3] = lastchar;
if (strncmp(cmdbuffer + 1, ">D>", 3) == 0)
{
putstring("ACK>\n");
blockPosition = 0;
RXState = ZBS_RX_WAIT_BLOCKDATA;
}
if (strncmp(cmdbuffer, "SDA>", 4) == 0)
{
RXState = ZBS_RX_WAIT_SDA;
bytesRemain = sizeof(struct pendingData);
serialbufferp = serialbuffer;
break;
}
if (strncmp(cmdbuffer, "CXD>", 4) == 0)
{
RXState = ZBS_RX_WAIT_CANCEL;
bytesRemain = sizeof(struct pendingData);
serialbufferp = serialbuffer;
break;
}
if (strncmp(cmdbuffer, "SCP>", 4) == 0)
{
RXState = ZBS_RX_WAIT_SCP;
bytesRemain = sizeof(struct espSetChannelPower);
serialbufferp = serialbuffer;
break;
}
if (strncmp(cmdbuffer, "NFO?", 4) == 0)
{
putstring("ACK>");
espNotifyAPInfo();
}
if (strncmp(cmdbuffer, "RDY?", 4) == 0)
{
putstring("ACK>");
}
if (strncmp(cmdbuffer, "RSET", 4) == 0)
{
putstring("ACK>");
// timerDelay(100);
/*CFGPAGE = 4;
WDTCONF = 0x80;
WDTENA = 1;
WDTRSTVALH = 0xff;
WDTRSTVALM = 0xff;
WDTRSTVALL = 0xff;
while(1);*/
}
break;
case ZBS_RX_WAIT_BLOCKDATA:
blockbuffer[blockPosition++] = 0xAA ^ lastchar;
if (blockPosition >= 4100)
{
putstring("ACK>");
RXState = ZBS_RX_WAIT_HEADER;
}
break;
case ZBS_RX_WAIT_SDA:
*serialbufferp = lastchar;
serialbufferp++;
bytesRemain--;
if (bytesRemain == 0)
{
if (checkCRC(serialbuffer, sizeof(struct pendingData)))
{
struct pendingData *pd = (struct pendingData *)serialbuffer;
int8_t slot = findSlotForMac(pd->targetMac);
if (slot == -1)
slot = findFreeSlot();
if (slot != -1)
{
memcpy(&(pendingDataArr[slot]), serialbuffer, sizeof(struct pendingData));
putstring("ACK>\n");
}
else
{
putstring("NOQ>\n");
}
}
else
{
putstring("NOK>\n");
}
RXState = ZBS_RX_WAIT_HEADER;
}
break;
case ZBS_RX_WAIT_CANCEL:
*serialbufferp = lastchar;
serialbufferp++;
bytesRemain--;
if (bytesRemain == 0)
{
if (checkCRC(serialbuffer, sizeof(struct pendingData)))
{
struct pendingData *pd = (struct pendingData *)serialbuffer;
// deleteAllPendingDataForVer((uint8_t *)&pd->availdatainfo.dataVer);
deleteAllPendingDataForMac((uint8_t *)&pd->targetMac);
putstring("ACK>\n");
}
else
{
putstring("NOK>\n");
}
RXState = ZBS_RX_WAIT_HEADER;
}
break;
case ZBS_RX_WAIT_SCP:
*serialbufferp = lastchar;
serialbufferp++;
bytesRemain--;
if (bytesRemain == 0)
{
if (checkCRC(serialbuffer, sizeof(struct espSetChannelPower)))
{
struct espSetChannelPower *scp = (struct espSetChannelPower *)serialbuffer;
for (uint8_t c = 0; c < sizeof(channelList); c++)
{
if (channelList[c] == scp->channel)
goto SCPchannelFound;
}
goto SCPfailed;
SCPchannelFound:
curChannel = scp->channel;
curPower = scp->power;
NETSTACK_RADIO.set_value(RADIO_PARAM_CHANNEL, scp->channel);
// NETSTACK_RADIO.set_value(RADIO_PARAM_TXPOWER, scp->power);
putstring("ACK>\n");
}
else
{
SCPfailed:
putstring("NOK>\n");
}
RXState = ZBS_RX_WAIT_HEADER;
}
break;
}
}
// sending data to the ESP
void espBlockRequest(const struct blockRequest *br, uint8_t *src)
{
struct espBlockRequest *ebr = (struct espBlockRequest *)blockbuffer;
putchar('R');
putchar('Q');
putchar('B');
putchar('>');
memcpy(&(ebr->ver), &(br->ver), 8);
memcpy(&(ebr->src), src, 8);
ebr->blockId = br->blockId;
addCRC(ebr, sizeof(struct espBlockRequest));
for (uint8_t c = 0; c < sizeof(struct espBlockRequest); c++)
{
putchar(((uint8_t *)ebr)[c]);
}
}
void espNotifyAvailDataReq(const struct AvailDataReq *adr, const uint8_t *src)
{
putchar('A');
putchar('D');
putchar('R');
putchar('>');
struct espAvailDataReq eadr = {0};
memcpy((void *)eadr.src, (void *)src, 8);
memcpy((void *)&eadr.adr, (void *)adr, sizeof(struct AvailDataReq));
addCRC(&eadr, sizeof(struct espAvailDataReq));
for (uint8_t c = 0; c < sizeof(struct espAvailDataReq); c++)
{
putchar(((uint8_t *)eadr)[c]);
}
}
void espNotifyXferComplete(const uint8_t *src)
{
struct espXferComplete exfc;
memcpy(&exfc.src, src, 8);
putchar('X');
putchar('F');
putchar('C');
putchar('>');
addCRC(&exfc, sizeof(exfc));
for (uint8_t c = 0; c < sizeof(exfc); c++)
{
putchar(((uint8_t *)exfc)[c]);
}
}
void espNotifyTimeOut(const uint8_t *src)
{
struct espXferComplete exfc;
memcpy(&exfc.src, src, 8);
putchar('X');
putchar('T');
putchar('O');
putchar('>');
addCRC(&exfc, sizeof(exfc));
for (uint8_t c = 0; c < sizeof(exfc); c++)
{
putchar(((uint8_t *)exfc)[c]);
}
}
void espNotifyAPInfo()
{
putstring("TYP>");
puthex(HW_TYPE);
putchar('\n');
putstring("VER>");
puthex(version >> 8);
puthex(version & 0xFF);
putchar('\n');
putstring("MAC>");
puthex(mSelfMac[0]);
puthex(mSelfMac[1]);
puthex(mSelfMac[2]);
puthex(mSelfMac[3]);
puthex(mSelfMac[4]);
puthex(mSelfMac[5]);
puthex(mSelfMac[6]);
puthex(mSelfMac[7]);
putchar('\n');
putstring("ZCH>");
puthex(curChannel);
putchar('\n');
putstring("ZPW>");
puthex(curPower);
putchar('\n');
countSlots();
putstring("PEN>");
puthex(curPendingData);
putchar('\n');
putstring("NOP>");
puthex(curNoUpdate);
putchar('\n');
}
// process data from tag
void processBlockRequest(const uint8_t *buffer, uint8_t forceBlockDownload)
{
struct MacFrameNormal *rxHeader = (struct MacFrameNormal *)buffer;
struct blockRequest *blockReq = (struct blockRequest *)(buffer + sizeof(struct MacFrameNormal) + 1);
if (!checkCRC(blockReq, sizeof(struct blockRequest)))
return;
// check if we're already talking to this mac
if (memcmp(rxHeader->src, lastBlockMac, 8) == 0)
{
lastBlockRequest = clock_time();
}
else
{
// we weren't talking to this mac, see if there was a transfer in progress from another mac, recently
if ((clock_time() - lastBlockRequest) > CONCURRENT_REQUEST_DELAY)
{
// mark this mac as the new current mac we're talking to
memcpy((void *)lastBlockMac, (void *)rxHeader->src, 8);
lastBlockRequest = clock_time();
}
else
{
// we're talking to another mac, let this mac know we can't accomodate another request right now
putstring("BUSY!\n");
sendCancelXfer(rxHeader->src);
return;
}
}
// check if we have data for this mac
if (findSlotForMac(rxHeader->src) == -1)
{
// no data for this mac, politely tell it to fuck off
sendCancelXfer(rxHeader->src);
return;
}
bool requestDataDownload = false;
if ((blockReq->blockId != requestedData.blockId) || (!memcmp((const uint64_t *)&blockReq->ver, (const uint64_t *)&requestedData.ver, 8)))
{
// requested block isn't already in the buffer
requestDataDownload = true;
}
else
{
// requested block is already in the buffer
if (forceBlockDownload)
{
if ((clock_time() - nextBlockAttempt) > (400 / 8))
{
requestDataDownload = true;
putstring("FORCED\n");
}
else
{
putstring("IGNORED\n");
}
}
}
// copy blockrequest into requested data
memcpy(&requestedData, blockReq, sizeof(struct blockRequest));
struct MacFrameNormal *txHeader = (struct MacFrameNormal *)(radiotxbuffer + 1);
struct blockRequestAck *blockRequestAck = (struct blockRequestAck *)(radiotxbuffer + sizeof(struct MacFrameNormal) + 2);
radiotxbuffer[0] = sizeof(struct MacFrameNormal) + 1 + sizeof(struct blockRequestAck) + RAW_PKT_PADDING;
radiotxbuffer[sizeof(struct MacFrameNormal) + 1] = PKT_BLOCK_REQUEST_ACK;
if (blockStartTimer == 0)
{
if (requestDataDownload)
{
// check if we need to download the first block; we need to give the ESP32 some additional time to cache the file
if (blockReq->blockId == 0)
{
blockRequestAck->pleaseWaitMs = 600;
}
else
{
blockRequestAck->pleaseWaitMs = 600;
}
}
else
{
// block is already in buffer
blockRequestAck->pleaseWaitMs = 600;
}
}
else
{
blockRequestAck->pleaseWaitMs = 600;
}
blockStartTimer = clock_time() + (blockRequestAck->pleaseWaitMs / 8);
memcpy(txHeader->src, mSelfMac, 8);
memcpy(txHeader->dst, rxHeader->src, 8);
txHeader->pan = rxHeader->pan;
txHeader->fcs.frameType = 1;
txHeader->fcs.panIdCompressed = 1;
txHeader->fcs.destAddrType = 3;
txHeader->fcs.srcAddrType = 3;
txHeader->seq = seq++;
addCRC((void *)blockRequestAck, sizeof(struct blockRequestAck));
radioTx(radiotxbuffer);
// save the target for the blockdata
memcpy(dstMac, rxHeader->src, 8);
dstPan = rxHeader->pan;
if (requestDataDownload)
{
// serialBypassActive = false;
espBlockRequest(&requestedData, rxHeader->src);
nextBlockAttempt = clock_time();
}
}
void processAvailDataReq(uint8_t *buffer)
{
struct MacFrameBcast *rxHeader = (struct MacFrameBcast *)buffer;
struct AvailDataReq *availDataReq = (struct AvailDataReq *)(buffer + sizeof(struct MacFrameBcast) + 1);
if (!checkCRC(availDataReq, sizeof(struct AvailDataReq)))
return;
// prepare tx buffer to send a response
memset(radiotxbuffer, 0, sizeof(struct MacFrameNormal) + sizeof(struct AvailDataInfo) + 2); // 120);
struct MacFrameNormal *txHeader = (struct MacFrameNormal *)(radiotxbuffer + 1);
struct AvailDataInfo *availDataInfo = (struct AvailDataInfo *)(radiotxbuffer + sizeof(struct MacFrameNormal) + 2);
radiotxbuffer[0] = sizeof(struct MacFrameNormal) + 1 + sizeof(struct AvailDataInfo) + RAW_PKT_PADDING;
radiotxbuffer[sizeof(struct MacFrameNormal) + 1] = PKT_AVAIL_DATA_INFO;
// check to see if we have data available for this mac
bool haveData = false;
for (uint8_t c = 0; c < MAX_PENDING_MACS; c++)
{
if (pendingDataArr[c].attemptsLeft)
{
if (memcmp(pendingDataArr[c].targetMac, rxHeader->src, 8) == 0)
{
haveData = true;
memcpy((void *)availDataInfo, &(pendingDataArr[c].availdatainfo), sizeof(struct AvailDataInfo));
break;
}
}
}
// couldn't find data for this mac
if (!haveData)
availDataInfo->dataType = DATATYPE_NOUPDATE;
memcpy(txHeader->src, mSelfMac, 8);
memcpy(txHeader->dst, rxHeader->src, 8);
txHeader->pan = rxHeader->dstPan;
txHeader->fcs.frameType = 1;
txHeader->fcs.panIdCompressed = 1;
txHeader->fcs.destAddrType = 3;
txHeader->fcs.srcAddrType = 3;
txHeader->seq = seq++;
addCRC(availDataInfo, sizeof(struct AvailDataInfo));
radioTx(radiotxbuffer);
memset(lastAckMac, 0, 8); // reset lastAckMac, so we can record if we've received exactly one ack packet
espNotifyAvailDataReq(availDataReq, rxHeader->src);
}
void processXferComplete(uint8_t *buffer)
{
struct MacFrameNormal *rxHeader = (struct MacFrameNormal *)buffer;
sendXferCompleteAck(rxHeader->src);
if (memcmp(lastAckMac, rxHeader->src, 8) != 0)
{
memcpy((void *)lastAckMac, (void *)rxHeader->src, 8);
espNotifyXferComplete(rxHeader->src);
int8_t slot = findSlotForMac(rxHeader->src);
if (slot != -1)
pendingDataArr[slot].attemptsLeft = 0;
}
}
// send block data to the tag
void sendPart(uint8_t partNo)
{
struct MacFrameNormal *frameHeader = (struct MacFrameNormal *)(radiotxbuffer + 1);
struct blockPart *blockPart = (struct blockPart *)(radiotxbuffer + sizeof(struct MacFrameNormal) + 2);
memset(radiotxbuffer + 1, 0, sizeof(struct blockPart) + sizeof(struct MacFrameNormal));
radiotxbuffer[sizeof(struct MacFrameNormal) + 1] = PKT_BLOCK_PART;
radiotxbuffer[0] = sizeof(struct MacFrameNormal) + sizeof(struct blockPart) + BLOCK_PART_DATA_SIZE + 1 + RAW_PKT_PADDING;
memcpy(frameHeader->src, mSelfMac, 8);
memcpy(frameHeader->dst, dstMac, 8);
blockPart->blockId = requestedData.blockId;
blockPart->blockPart = partNo;
memcpy(&(blockPart->data), blockbuffer + (partNo * BLOCK_PART_DATA_SIZE), BLOCK_PART_DATA_SIZE);
addCRC(blockPart, sizeof(struct blockPart) + BLOCK_PART_DATA_SIZE);
frameHeader->fcs.frameType = 1;
frameHeader->fcs.panIdCompressed = 1;
frameHeader->fcs.destAddrType = 3;
frameHeader->fcs.srcAddrType = 3;
frameHeader->seq = seq++;
frameHeader->pan = dstPan;
radioTx1(radiotxbuffer);
}
void sendBlockData()
{
if (getBlockDataLength() == 0)
{
putstring("Invalid block request received, 0 parts..\n");
requestedData.requestedParts[0] |= 0x01;
}
uint8_t partNo = 0;
while (partNo < BLOCK_MAX_PARTS)
{
for (uint8_t c = 0; (c < BLOCK_MAX_PARTS) && (partNo < BLOCK_MAX_PARTS); c++)
{
if (requestedData.requestedParts[c / 8] & (1 << (c % 8)))
{
sendPart(c);
partNo++;
}
}
}
}
void sendXferCompleteAck(uint8_t *dst)
{
struct MacFrameNormal *frameHeader = (struct MacFrameNormal *)(radiotxbuffer + 1);
memset(radiotxbuffer + 1, 0, sizeof(struct blockPart) + sizeof(struct MacFrameNormal));
radiotxbuffer[sizeof(struct MacFrameNormal) + 1] = PKT_XFER_COMPLETE_ACK;
radiotxbuffer[0] = sizeof(struct MacFrameNormal) + 1 + RAW_PKT_PADDING;
memcpy(frameHeader->src, mSelfMac, 8);
memcpy(frameHeader->dst, dst, 8);
frameHeader->fcs.frameType = 1;
frameHeader->fcs.panIdCompressed = 1;
frameHeader->fcs.destAddrType = 3;
frameHeader->fcs.srcAddrType = 3;
frameHeader->seq = seq++;
frameHeader->pan = dstPan;
radioTx(radiotxbuffer);
}
void sendCancelXfer(uint8_t *dst)
{
struct MacFrameNormal *frameHeader = (struct MacFrameNormal *)(radiotxbuffer + 1);
memset(radiotxbuffer + 1, 0, sizeof(struct blockPart) + sizeof(struct MacFrameNormal));
radiotxbuffer[sizeof(struct MacFrameNormal) + 1] = PKT_CANCEL_XFER;
radiotxbuffer[0] = sizeof(struct MacFrameNormal) + 1 + RAW_PKT_PADDING;
memcpy(frameHeader->src, mSelfMac, 8);
memcpy(frameHeader->dst, dst, 8);
frameHeader->fcs.frameType = 1;
frameHeader->fcs.panIdCompressed = 1;
frameHeader->fcs.destAddrType = 3;
frameHeader->fcs.srcAddrType = 3;
frameHeader->seq = seq++;
frameHeader->pan = dstPan;
radioTx(radiotxbuffer);
}
void sendPong(void *buf)
{
struct MacFrameBcast *rxframe = (struct MacFrameBcast *)buf;
struct MacFrameNormal *frameHeader = (struct MacFrameNormal *)(radiotxbuffer + 1);
radiotxbuffer[sizeof(struct MacFrameNormal) + 1] = PKT_PONG;
radiotxbuffer[sizeof(struct MacFrameNormal) + 2] = curChannel;
radiotxbuffer[0] = sizeof(struct MacFrameNormal) + 1 + 1 + RAW_PKT_PADDING;
memcpy(frameHeader->src, mSelfMac, 8);
memcpy(frameHeader->dst, rxframe->src, 8);
radiotxbuffer[1] = 0x41; // fast way to set the appropriate bits
radiotxbuffer[2] = 0xCC; // normal frame
frameHeader->seq = seq++;
frameHeader->pan = rxframe->srcPan;
radioTx(radiotxbuffer);
}
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
static void process_incoming_data(void)
{
int c = 0;
uint8_t byte_in;
c = ringbuf_get(&rxbuf);
while (c != -1)
{
byte_in = (uint8_t)c;
processSerial(byte_in);
c = ringbuf_get(&rxbuf);
}
}
PROCESS(cc2531_usb_demo_process, "cc2531 USB Demo process");
PROCESS(cc2531_RF, "cc2531 RF");
AUTOSTART_PROCESSES(&cc2531_usb_demo_process, &cc2531_RF);
static int char_in(unsigned char c)
{
ringbuf_put(&rxbuf, c);
process_poll(&cc2531_usb_demo_process);
return 1;
}
PROCESS_THREAD(cc2531_RF, ev, data)
{
PROCESS_BEGIN();
uint16_t short_addr;
short_addr = mSelfMac[7];
short_addr |= mSelfMac[6] << 8;
NETSTACK_RADIO.off();
NETSTACK_RADIO.set_value(RADIO_PARAM_PAN_ID, PROTO_PAN_ID);
NETSTACK_RADIO.set_value(RADIO_PARAM_16BIT_ADDR, short_addr);
NETSTACK_RADIO.set_object(RADIO_PARAM_64BIT_ADDR, mSelfMac, 8);
NETSTACK_RADIO.set_value(RADIO_PARAM_RX_MODE, 0); // RADIO_RX_MODE_ADDRESS_FILTER);
NETSTACK_RADIO.set_value(RADIO_PARAM_CHANNEL, 11);
NETSTACK_RADIO.on();
while (1)
{
if (NETSTACK_RADIO.pending_packet())
{
int len = NETSTACK_RADIO.read(radiorxbuffer, 128);
if (len)
{
/*putstring("RX: ");
for(int i = 0;i<len;i++){
puthex(radiorxbuffer[i]);
}
putchar('\n');*/
// received a packet, lets see what it is
switch (getPacketType(radiorxbuffer))
{
case PKT_AVAIL_DATA_REQ:
if (len == 28)
{
// old version of the AvailDataReq struct, set all the new fields to zero, so it will pass the CRC
processAvailDataReq(radiorxbuffer);
memset(radiorxbuffer + 1 + sizeof(struct MacFrameBcast) + sizeof(struct oldAvailDataReq), 0, sizeof(struct AvailDataReq) - sizeof(struct oldAvailDataReq) + 2);
}
else if (len == 40)
{
// new version of the AvailDataReq struct
processAvailDataReq(radiorxbuffer);
}
break;
case PKT_BLOCK_REQUEST:
processBlockRequest(radiorxbuffer, 1);
break;
case PKT_BLOCK_PARTIAL_REQUEST:
processBlockRequest(radiorxbuffer, 0);
break;
case PKT_XFER_COMPLETE:
processXferComplete(radiorxbuffer);
break;
case PKT_PING:
sendPong(radiorxbuffer);
break;
case PKT_AVAIL_DATA_SHORTREQ:
// a short AvailDataReq is basically a very short (1 byte payload) packet that requires little preparation on the tx side, for optimal battery use
// bytes of the struct are set 0, so it passes the checksum test, and the ESP32 can detect that no interesting payload is sent
if (len == 18)
{
memset(radiorxbuffer + 1 + sizeof(struct MacFrameBcast), 0, sizeof(struct AvailDataReq) + 2);
processAvailDataReq(radiorxbuffer);
}
break;
default:
putstring("t=");
puthex(getPacketType(radiorxbuffer));
putchar('\n');
break;
}
}
}
if (blockStartTimer)
{
// BUG: uint32 overflowing; this will break every once in a while. Don't know how to fix this other than ugly global variables
if (clock_time() > blockStartTimer)
{
sendBlockData();
blockStartTimer = 0;
}
}
PROCESS_PAUSE();
}
PROCESS_END();
}
PROCESS_THREAD(cc2531_usb_demo_process, ev, data)
{
PROCESS_BEGIN();
ringbuf_init(&rxbuf, cmd_buf, sizeof(cmd_buf));
io_arch_set_input(&char_in);
etimer_set(&et, CLOCK_SECOND * HOUSEKEEPING_INTERVAL);
unsigned char *macp = &X_IEEE_ADDR;
requestedData.blockId = 0xFF;
for (uint8_t c = 0; c < 8; c++)
{
mSelfMac[c] = *macp;
macp++;
}
memset(pendingDataArr, 0, sizeof(pendingDataArr));
putstring("RES>\n");
putstring("RDY>\n");
while (1)
{
PROCESS_YIELD();
if (ev == PROCESS_EVENT_POLL)
{
process_incoming_data();
}
else if (ev == PROCESS_EVENT_TIMER)
{
putstring("Housekeepin\r\n");
for (uint8_t cCount = 0; cCount < MAX_PENDING_MACS; cCount++)
{
if (pendingDataArr[cCount].attemptsLeft == 1)
{
if (pendingDataArr[cCount].availdatainfo.dataType != DATATYPE_NOUPDATE)
{
espNotifyTimeOut(pendingDataArr[cCount].targetMac);
}
pendingDataArr[cCount].attemptsLeft = 0;
}
else if (pendingDataArr[cCount].attemptsLeft > 1)
{
pendingDataArr[cCount].attemptsLeft--;
if (pendingDataArr[cCount].availdatainfo.nextCheckIn)
pendingDataArr[cCount].availdatainfo.nextCheckIn--;
}
}
}
}
PROCESS_END();
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,14 @@
# Set the default behavior, in case people don't have core.autocrlf set.
* text=auto whitespace=trailing-space
# Explicitly declare text files you want to always be normalized and converted
# to native line endings on checkout.
*.c text
*.h text
*.java text
# Denote all files that are truly binary and should not be modified.
*.png binary
*.jpg binary
*.ihex binary
*.s37 binary

View File

@@ -0,0 +1,131 @@
*.a
*.bin
*.map
*.png
*.log
*.elf
*.zip
*.d
*.ihex
*.pyc
*.redbee-econotag
*.econotag
*.native
*.z1
*.minimal-net
*.sky
*.wismote
*.esb
*.avr-raven
*.exp5438
*.mbxxx
*.win32
*.apple2enh
*.atarixl
*.c128
*.c64
*.cc2538dk
*.zoul
*.jn516x
*.srf06-cc26xx
*.ev-aducrf101mkxz
*.report
summary
*.summary
*.runerr
*.runlog
*.faillog
*.orig
*~
.DS_Store
obj_*
symbols.*
Makefile.target
doc/html
doc/latex
patches-*
tools/tunslip
tools/tunslip6
build
tools/coffee-manager/build/
tools/cooja/dist/
tools/collect-view/build/
tools/collect-view/dist/
COOJA.testlog
tools/cooja/apps/mrm/lib/
tools/cooja/apps/mspsim/lib/
tools/cooja/apps/powertracker/lib/
tools/cooja/apps/serial_socket/lib/
tools/coffee-manager/coffee.jar
tools/cooja/apps/avrora/lib/cooja_avrora.jar
tools/cooja/apps/collect-view/cooja-collect-view.jar
# sdcc build artifacts
contiki-cc2530dk.lib
*.ihx
*.hex
*.mem
*.lk
*.omf
*.cdb
*.banks
*.cc2530dk
# VC++ build artifacts
*.exp
*.ilk
*.lib
*.pdb
*.prg
*.dsc
#cc65 build artifacts
*.s
*.eth
*.dsk
*.po
*.atr
*.d64
*.d71
*.d81
# Cooja Build Artifacts
*.cooja
#regression tests artifacts
*.testlog
*.log.prog
regression-tests/[0-9][0-9]-*/report
regression-tests/[0-9][0-9]-*/org/
# rl78 build artifacts
*.eval-adf7xxxmb4z
*.eval-adf7xxxmb4z.srec
# cscope files
cscope.*
# vim swap files
*.swp
*.swo
# x86 UEFI files
cpu/x86/uefi/Makefile.uefi
cpu/x86/uefi/edk2
# galileo bsp files
platform/galileo/bsp/libc/Makefile.libc
platform/galileo/bsp/libc/i586-elf/
platform/galileo/bsp/libc/newlib-2.2.0-1*
platform/galileo/bsp/grub/src/
platform/galileo/bsp/grub/bin/
# galileo build and debug artefacts
*.galileo
*.galileo.dll
*.galileo.efi
LOG_OPENOCD
# nRF52 build artifacts
*.jlink
*.nrf52dk

View File

@@ -0,0 +1,19 @@
[submodule "tools/mspsim"]
path = tools/mspsim
url = https://github.com/contiki-os/mspsim.git
[submodule "tools/cc2538-bsl"]
path = tools/cc2538-bsl
url = https://github.com/JelmerT/cc2538-bsl.git
[submodule "cpu/cc26xx-cc13xx/lib/cc26xxware"]
path = cpu/cc26xx-cc13xx/lib/cc26xxware
url = https://github.com/contiki-os/cc26xxware.git
[submodule "cpu/cc26xx-cc13xx/lib/cc13xxware"]
path = cpu/cc26xx-cc13xx/lib/cc13xxware
url = https://github.com/contiki-os/cc13xxware.git
[submodule "platform/stm32nucleo-spirit1/stm32cube-lib"]
path = platform/stm32nucleo-spirit1/stm32cube-lib
url = https://github.com/STclab/stm32nucleo-spirit1-lib
[submodule "tools/sensniff"]
path = tools/sensniff
url = https://github.com/g-oikonomou/sensniff.git

View File

@@ -0,0 +1,165 @@
# Workaround for the issue found in the stable image promoted on Dec 1, 2016.
# See https://github.com/travis-ci/travis-ci/issues/6928#issuecomment-264227708
group: deprecated
notifications:
email: false
language: c #NOTE: this will set CC=gcc which might cause trouble
before_script:
- WGET="travis_retry wget --continue --tries=20 --waitretry=10 --retry-connrefused --no-dns-cache --timeout 300"
- sudo apt-get -qq update
## Support building a binary that is identical to the CI
- echo -n "Contiki will be compiled with RELSTR=" ; git --git-dir .git describe --tags --always
## Install doxygen
- if [ ${BUILD_CATEGORY:-0} = doxygen ] ; then
sudo add-apt-repository ppa:libreoffice/libreoffice-4-4 -y && sudo apt-get -qq update &&
sudo apt-get --no-install-suggests --no-install-recommends -qq install doxygen &&
doxygen --version ;
fi
## Install msp430 toolchain
- sudo apt-get -qq install lib32z1
- $WGET http://simonduq.github.io/resources/mspgcc-4.7.2-compiled.tar.bz2 &&
tar xjf mspgcc*.tar.bz2 -C /tmp/ &&
sudo cp -f -r /tmp/msp430/* /usr/local/ &&
rm -rf /tmp/msp430 mspgcc*.tar.bz2 &&
msp430-gcc --version
## Install avr toolchain
- $WGET http://atiselsts.github.io/resources/avr-gcc-4.9.2-compiled.tar.bz2 &&
tar xjf avr-gcc*.tar.bz2 -C /tmp/ &&
sudo cp -f -r /tmp/avr-gcc/* /usr/local/ &&
rm -rf /tmp/avr-gcc avr-gcc*.tar.bz2 &&
avr-gcc --version
## Install 32-bit compatibility libraries
- sudo apt-get -qq install libc6:i386 libgcc1:i386 gcc-4.6-base:i386
libstdc++5:i386 libstdc++6:i386
## Install old APCS ARM toolchain for mc1233x and mbxxx
- if [ ${BUILD_ARCH:-0} = arm-apcs ] ; then
$WGET https://raw.githubusercontent.com/wiki/malvira/libmc1322x/files/arm-2008q3-66-arm-none-eabi-i686-pc-linux-gnu.tar.bz2 &&
tar xjf arm-2008q3*.tar.bz2 -C /tmp/ &&
sudo cp -f -r /tmp/arm-2008q3/* /usr/ &&
rm -rf /tmp/arm-2008q3 arm-2008q3*.tar.bz2 &&
sudo apt-get -qq install libconfig-dev uuid-dev libqrencode-dev &&
arm-none-eabi-gcc --version ;
fi
## Install mainline ARM toolchain and srecord.
- if [ ${BUILD_ARCH:-0} = arm-aapcs ] ; then
sudo apt-get -qq install srecord &&
$WGET https://launchpad.net/gcc-arm-embedded/5.0/5-2015-q4-major/+download/gcc-arm-none-eabi-5_2-2015q4-20151219-linux.tar.bz2 &&
tar xjf gcc-arm-none-eabi-5_2-2015q4-20151219-linux.tar.bz2 -C /tmp/ &&
sudo cp -f -r /tmp/gcc-arm-none-eabi-5_2-2015q4/* /usr/local/ &&
rm -rf /tmp/gcc-arm-none-eabi-* gcc-arm-none-eabi-*-linux.tar.bz2 &&
arm-none-eabi-gcc --version ;
fi
## Install RL78 GCC toolchain
- sudo apt-get install libncurses5:i386 zlib1g:i386
- $WGET http://adamdunkels.github.io/contiki-fork/gnurl78-v13.02-elf_1-2_i386.deb &&
sudo dpkg -i gnurl78*.deb
## Install SDCC from a purpose-built bundle
- if [ ${BUILD_ARCH:-0} = 8051 ] ; then
$WGET https://raw.githubusercontent.com/wiki/g-oikonomou/contiki-sensinode/files/sdcc.tar.gz &&
tar xzf sdcc.tar.gz -C /tmp/ &&
sudo cp -f -r /tmp/sdcc/* /usr/local/ &&
rm -rf /tmp/sdcc sdcc.tar.gz &&
sdcc --version &&
sudo apt-get -qq install srecord ;
fi
## Clone and build cc65 when testing 6502 ports
- if [ ${BUILD_ARCH:-0} = 6502 ] ; then
git clone https://github.com/cc65/cc65 /tmp/cc65 &&
make -C /tmp/cc65 bin apple2enh atarixl c64 c128 &&
sudo make -C /tmp/cc65 avail &&
cc65 --version ;
fi
## Install NXP toolchain
- if [ ${BUILD_ARCH:-0} = jn516x ] ; then
$WGET http://simonduq.github.io/resources/ba-elf-gcc-4.7.4-part1.tar.bz2 &&
$WGET http://simonduq.github.io/resources/ba-elf-gcc-4.7.4-part2.tar.bz2 &&
$WGET http://simonduq.github.io/resources/jn516x-sdk-4163.tar.bz2 &&
mkdir /tmp/jn516x-sdk /tmp/ba-elf-gcc &&
tar xjf jn516x-sdk-*.tar.bz2 -C /tmp/jn516x-sdk &&
tar xjf ba-elf-gcc-*part1.tar.bz2 -C /tmp/ba-elf-gcc &&
tar xjf ba-elf-gcc-*part2.tar.bz2 -C /tmp/ba-elf-gcc &&
sudo cp -f -r /tmp/jn516x-sdk /usr/ &&
sudo cp -f -r /tmp/ba-elf-gcc /usr/ &&
export PATH=/usr/ba-elf-gcc/bin:$PATH &&
rm -rf /tmp/ba-elf-gcc* /tmp/jn516x-sdk* &&
ba-elf-gcc --version ;
fi
## Install mainline ARM toolchain and download nRF52 SDK
- if [ ${BUILD_ARCH:-0} = nrf52dk ] ; then
sudo add-apt-repository -y ppa:team-gcc-arm-embedded/ppa &&
sudo apt-get -qq update &&
sudo apt-get -qq install gcc-arm-embedded srecord &&
arm-none-eabi-gcc --version &&
$WGET https://developer.nordicsemi.com/nRF5_IoT_SDK/nRF5_IoT_SDK_v0.9.x/nrf5_iot_sdk_3288530.zip &&
mkdir /tmp/nrf52-sdk &&
unzip nrf5_iot_sdk_3288530.zip -d /tmp/nrf52-sdk &&
export NRF52_SDK_ROOT=/tmp/nrf52-sdk ;
fi
## Compile cooja.jar only when it's going to be needed
- if [ ${BUILD_CATEGORY:-sim} = sim ] ; then
java -version &&
ant -q -f tools/cooja/build.xml jar &&
sudo java -Xshare:dump -version ;
fi
script:
## regression-tests/Makefile handles most of generic logic
- "make -C regression-tests/??-$BUILD_TYPE RUNALL=true summary"
after_script:
## Print cooja test logs
- "[ ${BUILD_CATEGORY:-sim} = sim ] && tail regression-tests/??-$BUILD_TYPE/*.testlog"
## Print a basic summary
- "echo 'Summary:'; cat regression-tests/??-$BUILD_TYPE/summary"
- "FAILS=`grep -c ' FAIL ' regression-tests/??-$BUILD_TYPE/summary`"
## This will detect whether the build should pass or fail
- "test $FAILS -eq 0; exit $?"
env:
## This magically kick-off parallel jobs for each of the for the sets
## of environment variable defined below
- BUILD_TYPE='doxygen' BUILD_CATEGORY='doxygen'
- BUILD_TYPE='compile-base' BUILD_CATEGORY='compile'
- BUILD_TYPE='compile-tools' BUILD_CATEGORY='compile'
- BUILD_TYPE='collect'
- BUILD_TYPE='collect-lossy'
- BUILD_TYPE='rpl'
- BUILD_TYPE='rpl-non-storing'
- BUILD_TYPE='large-rpl'
- BUILD_TYPE='rime'
- BUILD_TYPE='ipv6'
- BUILD_TYPE='ip64' MAKE_TARGETS='cooja'
- BUILD_TYPE='hello-world'
- BUILD_TYPE='base'
# XXX: netperf disabled b/c it's flaky
# - BUILD_TYPE='netperf' MAKE_TARGETS='cooja'
- BUILD_TYPE='shell'
- BUILD_TYPE='elfloader'
# Tests under the ipv4 dir are individually disabled. Thus the entire job can be off
# - BUILD_TYPE='ipv4'
- BUILD_TYPE='ipv6-apps'
- BUILD_TYPE='compile-8051-ports' BUILD_CATEGORY='compile' BUILD_ARCH='8051'
- BUILD_TYPE='compile-arm-apcs-ports' BUILD_CATEGORY='compile' BUILD_ARCH='arm-apcs'
- BUILD_TYPE='compile-6502-ports' BUILD_CATEGORY='compile' BUILD_ARCH='6502'
- BUILD_TYPE='compile-arm-ports' BUILD_CATEGORY='compile' BUILD_ARCH='arm-aapcs'
- BUILD_TYPE='compile-nxp-ports' BUILD_CATEGORY='compile' BUILD_ARCH='jn516x'
- BUILD_TYPE='compile-nrf52-ports' BUILD_CATEGORY='compile' BUILD_ARCH='nrf52dk'
- BUILD_TYPE='slip-radio' MAKE_TARGETS='cooja'
- BUILD_TYPE='llsec' MAKE_TARGETS='cooja'
- BUILD_TYPE='compile-avr' BUILD_CATEGORY='compile' BUILD_ARCH='avr-rss2'
- BUILD_TYPE='ieee802154'

View File

@@ -0,0 +1,278 @@
Code Contributions
==================
Do you have a new cool feature that you'd like to contribute to
Contiki? Or a fix for a bug? Great! The Contiki project loves code
contributions, improvements, and bugfixes, but we require that they
follow a set of guidelines and that they are contributed in a specific
way.
Additional rules apply for contributions of a new hardware platform.
General Advice
--------------
The chance of getting your pull request accepted increases considerably
if you adhere to the following rules in addition to the aforementioned
formatting and naming standards:
* Ensure that all contributed files have a valid copyright statement
and an open-source license.
* Do not bundle commits that are unrelated to each other -- create
separate pull requests instead.
* Adhere to ISO C99 in all C language source files. Exceptions are
allowed for those platform-dependent source files that rely on the
extensions of a specific set of compilers.
* Clean up the commit history. "git rebase -i" is useful for this purpose.
* Do not include executable binary files, because they are usually
rejected for security reasons. Instead, provide instructions for how
to compile the file, so that a trusted member of the merge team can
commit it.
* Write a descriptive pull request message. Explain the advantages and
disadvantages of your proposed changes.
* Before starting to work on a major contribution, discuss your idea
with experienced Contiki programmers (e.g., on the contiki-developers
mailing list) to avoid wasting time on things that have no chance of
getting merged into Contiki.
Source code that goes into the mainline Contiki repository must be of
interest to a large part of the Contiki community. It must be
well-tested and the merge team must have confidence that the code can
be maintained over a longer period. See below for more details
pertaining to platform contributions.
Contributions that have been made in research projects, and typically
do not get maintained thereafter, are better suited for inclusion in
the Contiki projects repository.
Structuring Commits
-------------------
* Write descriptive commit messages. They don't have to be very long,
but you should mention what the commit achieves. Commit messages
like "modified foo/bar.c" are not helpful, should not be used, and
are likely to result in you having to re-write them.
* Please do not add / remove irrelevant new line markers. Don't remove
the new line marker at the EOF.
* Please, make sure that your patch doesn't add lines with trailing
whitespaces. If you run uncrustify as discussed above, this should
get taken care of for you automatically.
* More generally speaking, make sure that each commit in your history
only includes changes necessary to implement whatever it is the
commit is trying to achieve. All changes should be mentioned in the
commit message.
Code Formatting
---------------
We require that all code contributed to the Contiki tree follows the
same code formatting as the existing Contiki code. We are very strict
on this.
Code must be formatted according to
[contiki/doc/code-style.c](https://github.com/contiki-os/contiki/blob/master/doc/code-style.c).
The Contiki source tree contains scripts to assist with correct code formatting
and we recommend [Uncrustify](http://uncrustify.sourceforge.net/) as the
preferred auto formatter. Everything is under
[tools/code-style](https://github.com/contiki-os/contiki/tree/master/tools/code-style).
If you wish, you can format all changed resources in your working tree
automatically if the
[tools/code-style/uncrustify-changed.sh](https://github.com/contiki-os/contiki/blob/master/tools/code-style/uncrustify-changed.sh)
script is added as a [Git pre-commit
hook](http://git-scm.com/book/en/Customizing-Git-Git-Hooks) to your Git
configuration.
Here are some examples of what you can do:
* To check a file's style without changing the file on disk, you can run this:
`./tools/code-style/uncrustify-check-style.sh <path-to-file>`
This script will only accept a single file as its argument.
* To auto format a file (and change it on disk) you can run this:
`./tools/code-style/uncrustify-fix-style.sh <path-to-file>`
* `uncrustify-fix-style.sh` will accept a space-delimited list of files as its argument. Thus, you can auto-format an entire directory by running something like this:
``./tools/code-style/uncrustify-fix-style.sh `find cpu/cc2538 -type f -name "*.[ch]"` ``
This is _not_ a silver bullet and developer intervention is still required. Below are some examples of code which will get misformatted by uncrustify:
* Math symbol following a cast to a typedef
```
a = (uint8_t) ~P0_1; /* Cast to a typedef. Space gets added here (incorrect) */
a = (int)~P0_1; /* Cast to a known type. Space gets removed (correct) */
a = (uint8_t)P0_1; /* Variable directly after the cast. Space gets removed (correct) */
```
* `while(<condition>);` will become `while(<condition>) ;` (space incorrectly added after closing paren)
* `asm("wfi");` becomes `asm ("wfi");`: A space gets added before the opening paren, because the `asm` keyword stops this from getting interpreted as a normal function call / macro invocation. This is only a problem with `asm`. For instance, `foo("bar");` gets formatted correctly.
Naming
------
We require that all code contributed to the Contiki tree follow the
Contiki source code naming standard:
* File names are composed of lower-case characters and dashes. Like
this: simple-udp.c
* Variable and function names are composed of lower-case characters
and underscores. Like this: simple_udp_send();
* Variable and function names that are visible outside of their module
must begin with the name of the module. Like this:
simple_udp_send(), which is in the simple-udp module, declared in
simple-udp.h, and implemented in simple-udp.c.
* C macros are composed of upper-case characters and underscores. Like
this: PROCESS_THREAD().
* Configuration definitions begin with the module name and CONF_. Like
this: PROCESS_CONF_NUMEVENTS.
How to Contribute Code
----------------------
When your code is formatted according to the Contiki code style and
follows the Contiki naming standard, it is time to send it to the
Contiki maintainers to look at!
All code contributions to Contiki are submitted as [Github pull
requests](https://help.github.com/articles/using-pull-requests). Pull
requests will be reviewed and accepted according to the guidelines
found in the next section.
The basic guidelines to to start a Pull-Request:
* Create a new branch for your modifications. This branch should be based on the latest contiki master branch.
* If you already added the commits to another branch you can [cherry-pick](http://git-scm.com/docs/git-cherry-pick) them onto your new branch.
* Push the new branch to github.
* Raise the new Pull Requests on this new branch. Raising a Pull Request for the master branch is almost always a bad idea.
* If changes are requested do not close the pull request but rewrite your history. [Details about rewriting your history](http://git-scm.com/book/en/Git-Tools-Rewriting-History)
* You now force-push the changes to github. The pull-request is automatically updated.
In Git terminology this is equivalent to:
* Make sure you have the original contiki repo as origin.
```bash
$ git remote -v
contiki-orig https://github.com/contiki-os/contiki.git
```
* If not add it
```bash
$ git remote add contiki-orig https://github.com/contiki-os/contiki.git
```
* Make sure you have the latest version of your remotes
```bash
$ git remote update
```
* Create a new branch "my_new_feature" based on the latest contiki master branch
```bash
$ git checkout contiki-orig/master -b my_new_feature
```
* Add your work. For example by cherry-picking your changes from another branch.
```bash
$ git cherry-pick <HASH OF COMMIT>
```
* Push to _your_ github repository
```bash
$ git push origin my_new_feature
```
* Make a Pull Request for that branch
* Rewrite your history if requested
```bash
$ git rebase -i contiki-orig/master
```
* As rewriting your history can break things you must force-push the changes. **Warning**: Force-pushing normally is dangerous and you might break things. Make sure you are never force-pushing branches other people are supposed to work with.
```bash
$ git push origin my_new_feature -f
```
* NOTE: To avoid all the pain of selectively picking commits, rebasing and force-pushing - begin your development with a branch OTHER THAN your master branch, and push changes to that branch after any local commits.
Pull Request Merging Policy
---------------------------
Pull requests (PRs) are reviewed by the [merge team](https://github.com/orgs/contiki-os/people).
Generally, PRs require two "+1" before they can be merged by someone on the merge team.
The since Contiki 3.0, the merging policy is the following:
* The PR receives **one "-1"** from a merge team member (along with specific feedback). The PR is closed. A "-1" must be accompanied with a clear explanation why the PR will not be considered for inclusion.
* The PR receives **two "+1"** from merge team members. The PR is merged.
* The PR was inactive for **two months**. A team member may either:
* Comment "Is there any interest for this PR? Is there any work pending on it? If not I will close it in **one month**." Back to initial state in case of activity, close otherwise.
* Comment "I approve this PR. If nobody disapproves within **one month**, I will merge it." Back to initial state in case of activity, merge otherwise.
There is an exception to the rule.
Code that requires esoteric expertise such as some applications, platforms or tools can be merged after a single "+1" from its domain expert.
Travis / Regression testing
---------------------------
[Travis](https://travis-ci.org/) is a service that runs regression
tests. If you make a pull-request for Contiki this is automatically
forwarded to Travis and regression tests are run. A box with
information about the state of you pull request should show up after a
minute or two.
If the test fails it is likely that something is wrong with your
code. Please look carefully at the log. It might also be that some
package on the testing VM was updated and causes the build to fail. If
you are sure that is is not your code causing the tests to fail start
a new issue describing the problem. Also note this in your pull
request.
You can also register at [Travis](https://travis-ci.org/) for
free. Once you activated your Contiki repository, every push will be
tested at Travis. The configuration is part of the contiki repository
and testing will therefore work out-of-the-box. At Travis you then get
an overview of the state of each of your branches.
New Platforms
-------------
A new hardware port will be considered for inclusion in mainline Contiki
if it satisfies the following rules:
* There must be at least one person willing and committed to maintain it.
They may but do not have to be the people who wrote the code. Similarly,
they may but do not have to be affiliated with the hardware manufacturer.
In the first instance, code maintenance would mean keeping the port up to
speed by submitting pull requests as Contiki moves forward. In the longer
term, people who maintain a reasonable level of commitment and who demonstrate
that they know what they're doing may be invited to become repo collaborators.
* The hardware must be commercially available and of interest to a wide audience.
In other words, ports for bespoke hardware built for e.g. a specific project /
a single customer / niche markets are more suitable for a Contiki fork.
* The code must strictly adhere to the Contiki code style, as discussed above.
* The new files must have a clear copyright notice and license header. Contiki's
preferred software license is the
[3-clause BSD](http://opensource.org/licenses/BSD-3-Clause).
Other licenses may also be considered
as long as they are compatible with the 3-clause BSD (e.g. the Apache 2.0 license).
Conversely, code distributed under GPL cannot be considered. The same applies to
bespoke licenses, such as those allowing use or redistribution only together with
certain kinds of hardware.
* The port must demonstrate a certain degree of completeness and maturity. Common sense
applies here.
* The port must be accompanied by examples demonstrating basic functionality. This could
be a set of examples under `examples/<new-hardware-port>` and/or documentation of
which existing examples are meant to work.
* The port must provide compile regression tests by extending the existing travis
integration testing framework. Again, we can't specify explicitly
what those tests should be, but something more interesting than hello-world is expected.
* The work must be documented. The documentation could be README.md files
under the platform / cpu / example dirs or wiki pages. Doxygen comments are
also encouraged. The documentation should include:
* A getting started guide, including a list of tools required to use the platform
(e.g. toolchain, software to program the device), where to get them from and brief notes
how to install them (can simply be a list of links to external guides)
* A list of things which will work off the shelf
* A list of things which are not meant to work, if any
* Additional reading resources (e.g. datasheets, hardware user guides, web resources)
* A ToDo list, if applicable.
* It must be possible to use the port using free software. We do not discourage the
use of commercial software (e.g. support for a commercial toolchain), quite the opposite.
However, we will insist on the existence of a free alternative for everything.
After the port has been accepted, things meant to work off the shelf should
keep working off the shelf as Contiki moves forward.
We appreciate that, for many people, contributing to Contiki is a spare time
activity and our expectations from port maintainers take this into
consideration. All we ask from maintainers is to comment on and address
relevant pull requests at a reasonable frequency and to make sure travis keeps
passing. In other words, we just want platforms to stay healthy over time and
to thus avoid becoming very broken / obsolete.

View File

@@ -0,0 +1,38 @@
Contiki is licensed under the 3-clause BSD license. This license gives
everyone the right to use and distribute the code, either in binary or
source code format, as long as the copyright license is retained in
the source code.
The copyright for different parts of the code is held by different
people and organizations, but the code is licensed under the same type
of license. The license text is:
* Copyright (c) (Year), (Name of copyright holder)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,304 @@
# -*- makefile -*-
ifndef CONTIKI
${error CONTIKI not defined! You must specify where Contiki resides}
endif
ifeq ($(TARGET),)
-include Makefile.target
ifeq ($(TARGET),)
${info TARGET not defined, using target 'native'}
TARGET=native
else
${info using saved target '$(TARGET)'}
endif
endif
ifeq ($(DEFINES),)
-include Makefile.$(TARGET).defines
ifneq ($(DEFINES),)
${info using saved defines '$(DEFINES)'}
endif
endif
ifndef HOST_OS
ifeq ($(OS),Windows_NT)
## TODO: detect more specific Windows set-ups,
## e.g. CygWin, MingW, VisualC, Watcom, Interix
HOST_OS := Windows
else
HOST_OS := $(shell uname)
endif
endif
#More debug information when running in CI
ifdef CI
ifeq ($(CI),true)
V = 1
endif
endif
usage:
@echo "make MAKETARGETS... [TARGET=(TARGET)] [savetarget] [targets]"
targets:
@ls -1 $(CONTIKI)/platform $(TARGETDIRS) | grep -v CVS
savetarget:
-@rm -f Makefile.target
@echo "saving Makefile.target"
@echo >Makefile.target "TARGET = $(TARGET)"
savedefines:
-@rm -f Makefile.$(TARGET).defines
@echo "saving Makefile.$(TARGET).defines"
@echo >Makefile.$(TARGET).defines "DEFINES = $(DEFINES)"
OBJECTDIR = obj_$(TARGET)
LOWERCASE = -abcdefghijklmnopqrstuvwxyz
UPPERCASE = _ABCDEFGHIJKLMNOPQRSTUVWXYZ
TARGET_UPPERCASE := ${strip ${shell echo $(TARGET) | sed y!$(LOWERCASE)!$(UPPERCASE)!}}
CFLAGS += -DCONTIKI=1 -DCONTIKI_TARGET_$(TARGET_UPPERCASE)=1
MODULES += core/sys core/dev core/lib
# Include IPv6, IPv4, and/or Rime
HAS_STACK = 0
ifeq ($(CONTIKI_WITH_IPV4),1)
HAS_STACK = 1
CFLAGS += -DNETSTACK_CONF_WITH_IPV4=1
MODULES += core/net/ipv4 core/net/ip
endif
ifeq ($(CONTIKI_WITH_RIME),1)
HAS_STACK = 1
CFLAGS += -DNETSTACK_CONF_WITH_RIME=1
MODULES += core/net/rime
endif
# Make IPv6 the default stack
ifeq ($(HAS_STACK),0)
ifneq ($(CONTIKI_WITH_IPV6),0)
CONTIKI_WITH_IPV6 = 1
endif
endif
ifeq ($(CONTIKI_WITH_IPV6),1)
CFLAGS += -DNETSTACK_CONF_WITH_IPV6=1
ifneq ($(CONTIKI_WITH_RPL),0)
CONTIKI_WITH_RPL = 1
endif
MODULES += core/net/ipv6 core/net/ip
endif
ifeq ($(CONTIKI_WITH_RPL),1)
CFLAGS += -DUIP_CONF_IPV6_RPL=1
MODULES += core/net/rpl
else
CFLAGS += -DUIP_CONF_IPV6_RPL=0
endif
CONTIKI_SOURCEFILES += $(CONTIKIFILES)
CONTIKIDIRS += ${addprefix $(CONTIKI)/core/,dev lib net net/llsec net/mac net/rime \
net/rpl sys cfs ctk lib/ctk loader . }
oname = ${patsubst %.c,%.o,${patsubst %.S,%.o,$(1)}}
CONTIKI_OBJECTFILES = ${addprefix $(OBJECTDIR)/,${call oname, $(CONTIKI_SOURCEFILES)}}
PROJECT_OBJECTFILES = ${addprefix $(OBJECTDIR)/,${call oname, $(PROJECT_SOURCEFILES)}}
# Provide way to create $(OBJECTDIR) if it has been removed by make clean
$(OBJECTDIR):
mkdir $@
uniq = $(if $1,$(firstword $1) $(call uniq,$(filter-out $(firstword $1),$1)))
### Include application makefiles
ifdef APPS
APPDS = ${wildcard ${foreach DIR, $(APPDIRS), ${addprefix $(DIR)/, $(APPS)}}} \
${wildcard ${addprefix $(CONTIKI)/apps/, $(APPS)} \
${addprefix $(CONTIKI)/platform/$(TARGET)/apps/, $(APPS)} \
$(APPS)}
APPINCLUDES = ${foreach APP, $(APPS), ${wildcard ${foreach DIR, $(APPDS), $(DIR)/Makefile.$(APP)}}}
-include $(APPINCLUDES)
APP_SOURCES = ${foreach APP, $(APPS), $($(APP)_src)}
DSC_SOURCES = ${foreach APP, $(APPS), $($(APP)_dsc)}
CONTIKI_SOURCEFILES += $(APP_SOURCES) $(DSC_SOURCES)
endif
### Include target makefile (TODO Unsafe?)
target_makefile := $(wildcard $(CONTIKI)/platform/$(TARGET)/Makefile.$(TARGET) ${foreach TDIR, $(TARGETDIRS), $(TDIR)/$(TARGET)/Makefile.$(TARGET)})
# Check if the target makefile exists, and create the object directory if necessary.
ifeq ($(strip $(target_makefile)),)
${error The target platform "$(TARGET)" does not exist (maybe it was misspelled?)}
else
ifneq (1, ${words $(target_makefile)})
${error More than one TARGET Makefile found: $(target_makefile)}
endif
include $(target_makefile)
endif
ifdef MODULES
UNIQUEMODULES = $(call uniq,$(MODULES))
MODULEDIRS = ${wildcard ${addprefix $(CONTIKI)/, $(UNIQUEMODULES)}}
MODULES_SOURCES = ${foreach d, $(MODULEDIRS), ${subst ${d}/,,${wildcard $(d)/*.c}}}
CONTIKI_SOURCEFILES += $(MODULES_SOURCES)
APPDS += $(MODULEDIRS)
endif
### Verbosity control. Use make V=1 to get verbose builds.
ifeq ($(V),1)
TRACE_CC =
TRACE_LD =
TRACE_AR =
TRACE_AS =
Q=
else
TRACE_CC = @echo " CC " $<
TRACE_LD = @echo " LD " $@
TRACE_AR = @echo " AR " $@
TRACE_AS = @echo " AS " $<
Q=@
endif
### Forward comma-separated list of arbitrary defines to the compiler
COMMA := ,
CFLAGS += ${addprefix -D,${subst $(COMMA), ,$(DEFINES)}}
### Setup directory search path for source and header files
CONTIKI_TARGET_DIRS_CONCAT = ${addprefix ${dir $(target_makefile)}, \
$(CONTIKI_TARGET_DIRS)}
CONTIKI_CPU_DIRS_CONCAT = ${addprefix $(CONTIKI_CPU)/, \
$(CONTIKI_CPU_DIRS)}
SOURCEDIRS = . $(PROJECTDIRS) $(CONTIKI_TARGET_DIRS_CONCAT) \
$(CONTIKI_CPU_DIRS_CONCAT) $(CONTIKIDIRS) $(APPDS) $(EXTERNALDIRS) ${dir $(target_makefile)}
vpath %.c $(SOURCEDIRS)
vpath %.S $(SOURCEDIRS)
CFLAGS += ${addprefix -I,$(SOURCEDIRS) $(CONTIKI)}
### Check for a git repo and pass version if found
### git.exe in Windows cmd shells may require no stderr redirection
ifndef RELSTR
RELSTR:=${shell git --git-dir ${CONTIKI}/.git describe --tags --always}
endif
ifneq ($(RELSTR),)
CFLAGS += -DCONTIKI_VERSION_STRING=\"Contiki-$(RELSTR)\"
endif
### Automatic dependency generation
ifneq ($(MAKECMDGOALS),clean)
-include ${addprefix $(OBJECTDIR)/,$(CONTIKI_SOURCEFILES:.c=.d) \
$(PROJECT_SOURCEFILES:.c=.d)}
endif
### See http://make.paulandlesley.org/autodep.html#advanced
define FINALIZE_DEPENDENCY
cp $(@:.o=.d) $(@:.o=.$$$$); \
sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
-e '/^$$/ d' -e 's/$$/ :/' < $(@:.o=.$$$$) >> $(@:.o=.d); \
rm -f $(@:.o=.$$$$)
endef
clean:
-rm -f *~ *core core *.srec \
*.lst *.map \
*.cprg *.bin *.data contiki*.a *.firmware core-labels.S *.ihex *.ini \
*.ce *.co
rm -rf $(CLEAN)
-rm -rf $(OBJECTDIR)
distclean: clean
-rm -f ${addsuffix .$(TARGET),$(CONTIKI_PROJECT)}
-include $(CONTIKI)/platform/$(TARGET)/Makefile.customrules-$(TARGET)
ifndef CUSTOM_RULE_C_TO_CE
%.ce: %.c
$(TRACE_CC)
$(Q)$(CC) $(CFLAGS) -DAUTOSTART_ENABLE -c $< -o $@
$(STRIP) --strip-unneeded -g -x $@
endif
ifndef CUSTOM_RULE_C_TO_OBJECTDIR_O
$(OBJECTDIR)/%.o: %.c | $(OBJECTDIR)
$(TRACE_CC)
$(Q)$(CC) $(CFLAGS) -MMD -c $< -o $@
@$(FINALIZE_DEPENDENCY)
endif
ifndef CUSTOM_RULE_S_TO_OBJECTDIR_O
$(OBJECTDIR)/%.o: %.S | $(OBJECTDIR)
$(TRACE_AS)
$(Q)$(AS) $(ASFLAGS) -o $@ $<
endif
ifndef CUSTOM_RULE_C_TO_O
%.o: %.c
$(TRACE_CC)
$(Q)$(CC) $(CFLAGS) -c $< -o $@
endif
ifndef CUSTOM_RULE_C_TO_CO
%.co: %.c
$(TRACE_CC)
$(Q)$(CC) $(CFLAGS) -DAUTOSTART_ENABLE -c $< -o $@
endif
ifndef AROPTS
AROPTS = rcf
endif
ifndef CUSTOM_RULE_ALLOBJS_TO_TARGETLIB
contiki-$(TARGET).a: $(CONTIKI_OBJECTFILES)
$(TRACE_AR)
$(Q)$(AR) $(AROPTS) $@ $^
endif
ifndef LD
LD = $(CC)
endif
ifndef CUSTOM_RULE_LINK
%.$(TARGET): %.co $(PROJECT_OBJECTFILES) $(PROJECT_LIBRARIES) contiki-$(TARGET).a
$(TRACE_LD)
$(Q)$(LD) $(LDFLAGS) $(TARGET_STARTFILES) ${filter-out %.a,$^} \
${filter %.a,$^} $(TARGET_LIBFILES) -o $@
endif
%.ramprof: %.$(TARGET)
$(NM) -S -td --size-sort $< | grep -i " [abdrw] " | cut -d' ' -f2,4
%.flashprof: %.$(TARGET)
$(NM) -S -td --size-sort $< | grep -i " [t] " | cut -d' ' -f2,4
# Don't treat %.$(TARGET) as an intermediate file because it is
# in fact the primary target.
.PRECIOUS: %.$(TARGET)
# Cancel the predefined implict rule for compiling and linking
# a single C source into a binary to force GNU make to consider
# the match-anything rule below instead.
%: %.c
# Match-anything pattern rule to allow the project makefiles to
# abstract from the actual binary name. It needs to contain some
# command in order to be a rule, not just a prerequisite.
%: %.$(TARGET)
@

View File

@@ -0,0 +1,92 @@
The Contiki build system
========================
The Contiki build system is designed to make it easy to compile Contiki
applications for different hardware platforms or into a simulation platform by
simply supplying different parameters to the make command, without having to
edit makefiles or modify the application code.
The file example project in examples/hello-world/ shows how the Contiki build
system works. The hello-world.c application can be built into a complete
Contiki system by running make in the examples/hello-world/ directory. Running
make without parameters will build a Contiki system using the native target.
The native target is a special Contiki platform that builds an entire Contiki
system as a program that runs on the development system. After compiling the
application for the native target it is possible to run the Contiki system with
the application by running the file hello-world.native. To compile the
application and a Contiki system for the ESB platform the command make
TARGET=esb is used. This produces a hello-world.esb file that can be loaded
into an ESB board.
To compile the hello-world application into a stand-alone executable that can
be loaded into a running Contiki system, the command make hello-world.ce is
used. To build an executable file for the ESB platform, make TARGET=esb
hello-world.ce is run.
To avoid having to type TARGET= every time make is run, it is possible to run
make TARGET=esb savetarget to save the selected target as the default target
platform for subsequent invocations of make. A file called Makefile.target
containing the currently saved target is saved in the project's directory.
Beside TARGET= there's DEFINES= which allows to set arbitrary variables for the
C preprocessor in form of a comma-separated list. Again it is possible to avoid
having to re-type i.e. DEFINES=MYTRACE,MYVALUE=4711 by running make TARGET=esb
DEFINES=MYTRACE,MYVALUE=4711 savedefines. A file called Makefile.esb.defines is
saved in the project's directory containing the currently saved defines for the
ESB platform.
Makefiles used in the Contiki build system The Contiki build system is composed
of a number of Makefiles. These are:
* Makefile: the project's makefile, located in the project directory.
* Makefile.include: the system-wide Contiki makefile, located in the root of
the Contiki source tree.
* Makefile.$(TARGET) (where $(TARGET) is the name of the platform that is
currently being built): rules for the specific platform, located in the
platform's subdirectory in the platform/ directory.
* Makefile.$(CPU) (where $(CPU) is the name of the CPU or microcontroller
architecture used on the platform for which Contiki is built): rules for the
CPU architecture, located in the CPU architecture's subdirectory in the cpu/
directory.
* Makefile.$(APP) (where $(APP) is the name of an application in the apps/
directory): rules for applications in the apps/ directories. Each application
has its own makefile.
The Makefile in the project's directory is intentionally simple. It specifies
where the Contiki source code resides in the system and includes the
system-wide Makefile, Makefile.include. The project's makefile can also define
in the APPS variable a list of applications from the apps/ directory that
should be included in the Contiki system. The Makefile used in the hello-world
example project looks like this:
CONTIKI_PROJECT = hello-world
all: $(CONTIKI_PROJECT)
CONTIKI = ../..
include $(CONTIKI)/Makefile.include
First, the location of the Contiki source code tree is given by defining the
CONTIKI variable. Next, the name of the application is defined. Finally, the
system-wide Makefile.include is included.
The Makefile.include contains definitions of the C files of the core Contiki
system. Makefile.include always reside in the root of the Contiki source tree.
When make is run, Makefile.include includes the Makefile.$(TARGET) as well as
all makefiles for the applications in the APPS list (which is specified by the
project's Makefile).
Makefile.$(TARGET), which is located in the platform/$(TARGET)/ directory,
contains the list of C files that the platform adds to the Contiki system. This
list is defined by the CONTIKI_TARGET_SOURCEFILES variable. The
Makefile.$(TARGET) also includes the Makefile.$(CPU) from the cpu/$(CPU)/
directory.
The Makefile.$(CPU) typically contains definitions for the C compiler used for
the particular CPU. If multiple C compilers are used, the Makefile.$(CPU) can
either contain a conditional expression that allows different C compilers to be
defined, or it can be completely overridden by the platform specific makefile
Makefile.$(TARGET).

View File

@@ -0,0 +1,220 @@
Contiki Examples
================
The examples/ directory contains a few examples that will help you get
started with Contiki.
To run the example programs, you need either to be running Linux or FreeBSD (or
any other UNIX-type system), or install Cygwin if you are running Microsoft
Windows [http://cygwin.com](http://cygwin.com). As a minimum you will need to
have the gcc C compiler installed. To run the examples in the 'netsim' target,
you need to have GTK 1.x development libraries installed. These are usually
called 'gtk-devel', 'libgtk1-devel' or similar in your Linux software
installation programs.
compile-platforms/
------------------
A test script that compiles Contiki for a number of platforms and reports any
errors found during the build.
email/
------
An email program supporting SMTP. It can be compiled and run in the 'win32'
target by typing the following commands:
cd examples/email
make
./email-client.win32
Most likely you'll have to adjust the TCP/IP values set in main() in
platform/win32/contiki-main.c to match your needs.
Please consult cpu/native/net/README-WPCAP.md as well.
esb/
----
A set of demo applications for the ESB board.
ftp/
----
An FTP client supporting download. It can be compiled and run in the 'win32'
target by typing the following commands:
cd examples/ftp
make
./ftp-client.win32
Most likely you'll have to adjust the TCP/IP values set in main() in
platform/win32/contiki-main.c to match your needs.
Please consult cpu/native/net/README-WPCAP.md as well.
hello-world/
------------
A really simple Contiki program that shows how to write Contiki programs. To
compile and test the program, go into the hello-world directory:
cd examples/hello-world
Run the 'make' command.
make
This will compile the hello-world program in the 'native' target. This causes
the entire Contiki operating system and the hello-world application to be
compiled into a single program that can be run by typing the following command:
./hello-world.native
This will print out the following text:
Contiki initiated, now starting process scheduling
Hello, world
The program will then appear to hang, and must be stopped by pressing the C key
while holding down the Control key.
irc/
----
An IRC client. It can be compiled and run in the 'win32' target by
typing the following commands:
cd examples/irc
make
./irc-client.win32
Most likely you'll have to adjust the TCP/IP values set in main() in
platform/win32/contiki-main.c to match your needs.
Please consult cpu/native/net/README-WPCAP.md as well.
multi-threading/
----------------
A quite simple demonstration of the Contiki multi-threading library
employing two worker threads each running a recursive function. It
can be compiled and run in the 'native' target by typing the
following commands:
cd examples/multi-threading
make
./multi-threading.native
rime/
-----
Contains a set of examples on how to use the Rime communications
stack. To run those examples in the 'netsim' target (a very simple
Contiki network simulator), compile the programs with:
make TARGET=netsim
You will need to have GTK 1.x development libraries installed.
Run the different programs:
./test-abc.netsim
./test-meshroute.netsim
./test-rudolph0.netsim
./test-rudolph1.netsim
./test-treeroute.netsim
./test-trickle.netsim
Most of the examples requires you to click with the middle mouse
button on one of the simulated nodes for something to happen.
sky/
----
Examples inteded for running on the Tmote Sky board. To compile those, you need
to have msp430-gcc (the gcc C compiler for the MSP430 microcontroller)
installed.
The follwing programs are included:
- blink.c A simple program that blinks the on-board LEDs
- sky-collect.c Collects sensor data and energy profile values to a sink.
Press the "user" button on the Tmote Sky that is connected to the PC to make
the node a sink.
- test-button.c Toggles the LEDs when the button is pressed.
- test-cfs.c Tests the 1 mb flash memory of the Tmote Sky
telnet-server/
--------------
A simple TCP telnet server with a simple command shell. It can be
compiled and run in the 'minimal-net' target by typing the following
commands:
cd examples/telnet-server
make
./telnet-server.minimal-net
Most likely you'll have to adjust the TCP/IP values set in main() in
platform/minimal-net/contiki-main.c to match your needs.
Please consult cpu/native/net/README-WPCAP.md as well if you are running
Microsoft Windows.
webbrowser/
-----------
A text mode web browser supporting links and forms. It can be compiled
and run in the 'win32' target by typing the following commands:
cd examples/webbrowser
make
./webbrowser.win32
Most likely you'll have to adjust the TCP/IP values set in main() in
platform/win32/contiki-main.c to match your needs.
Please consult cpu/native/net/README-WPCAP.md as well.
webserver/
----------
A web server supporting dynamic content creation using "scripts" which
are actually compiled-in C-functions. It can be compiled and run in the
'minimal-net' target by typing the following commands:
cd examples/webserver
make
./webserver-example.minimal-net
As an alternative to the static and dynamic compiled-in content the web
server can instead support "external" static-only content loaded from
any storage supported by the 'Contiki File System' (CFS) interface. To
compile it in the 'minimal-net' target and have it load files from disk
use the following command:
make HTTPD-CFS=1
Most likely you'll have to adjust the TCP/IP values set in main() in
platform/minimal-net/contiki-main.c to match your needs.
Please consult cpu/native/net/README-WPCAP.md as well if you are running
Microsoft Windows.
wget/
-----
A command line program that retrieves files from web servers and saves them
using the 'Contiki File System' (CFS). It can be compiled and run in the
'minimal-net' target by typing the following commands:
cd examples/wget
make
./wget.minimal-net
Most likely you'll have to adjust the TCP/IP values set in main() in
platform/minimal-net/contiki-main.c to match your needs.
Please consult cpu/native/net/README-WPCAP.md as well if you are running
Microsoft Windows.

View File

@@ -0,0 +1,19 @@
The Contiki Operating System
============================
[![Build Status](https://travis-ci.org/contiki-os/contiki.svg?branch=release-3-1)](https://travis-ci.org/contiki-os/contiki/branches)
Contiki is an open source operating system that runs on tiny low-power
microcontrollers and makes it possible to develop applications that
make efficient use of the hardware while providing standardized
low-power wireless communication for a range of hardware platforms.
Contiki is used in numerous commercial and non-commercial systems,
such as city sound monitoring, street lights, networked electrical
power meters, industrial monitoring, radiation monitoring,
construction site monitoring, alarm systems, remote house monitoring,
and so on.
For more information, see the Contiki website:
[http://contiki-os.org](http://contiki-os.org)

View File

@@ -0,0 +1,2 @@
about_src = about.c
about_dsc = about-dsc.c

View File

@@ -0,0 +1,72 @@
/*
* Copyright (c) 2003, Adam Dunkels.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* 3. The name of the author may not be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This file is part of the Contiki desktop environment
*
*
*/
#include "sys/dsc.h"
/*-----------------------------------------------------------------------------------*/
#if CTK_CONF_ICON_BITMAPS
static unsigned char abouticon_bitmap[3*3*8] = {
0x00, 0x7f, 0x43, 0x4c, 0x58, 0x53, 0x60, 0x6f,
0x00, 0xff, 0x00, 0x7e, 0x00, 0xff, 0x00, 0xff,
0x00, 0xfe, 0xc2, 0x32, 0x1a, 0xca, 0x06, 0xf6,
0x40, 0x5f, 0x40, 0x5f, 0x40, 0x5f, 0x40, 0x4f,
0x00, 0xff, 0x00, 0xff, 0x00, 0xfc, 0x01, 0xf3,
0x02, 0xfa, 0x02, 0x82, 0x3e, 0xfe, 0xfe, 0xfe,
0x60, 0x67, 0x50, 0x59, 0x4c, 0x43, 0x7f, 0x00,
0x07, 0xe7, 0x0f, 0xef, 0x0f, 0x0f, 0xff, 0x00,
0x8e, 0x06, 0x06, 0x06, 0x8e, 0xfe, 0xfe, 0x00
};
#endif /* CTK_CONF_ICON_BITMAPS */
#if CTK_CONF_ICON_TEXTMAPS
static char abouticon_textmap[9] = {
' ', ' ', 'c',
' ', '?', ' ',
'.', ' ', ' '
};
#endif /* CTK_CONF_ICON_TEXTMAPS */
#if CTK_CONF_ICONS
static struct ctk_icon about_icon =
{CTK_ICON("About Contiki", abouticon_bitmap, abouticon_textmap)};
#endif /* CTK_CONF_ICONS */
/*-----------------------------------------------------------------------------------*/
DSC(about_dsc,
"About Contiki",
"about.prg",
about_process,
&about_icon);
/*-----------------------------------------------------------------------------------*/

View File

@@ -0,0 +1,41 @@
/*
* Copyright (c) 2003, Adam Dunkels.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* 3. The name of the author may not be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This file is part of the Contiki desktop environment
*
*
*/
#ifndef ABOUT_DSC_H_
#define ABOUT_DSC_H_
#include "sys/dsc.h"
DSC_HEADER(about_dsc);
#endif /* ABOUT_DSC_H_ */

View File

@@ -0,0 +1,129 @@
/*
* Copyright (c) 2002, Adam Dunkels.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* 3. The name of the author may not be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This file is part of the Contiki desktop environment
*
*
*/
#include <string.h>
#include "contiki.h"
#include "ctk/ctk.h"
#include "lib/petsciiconv.h"
static struct ctk_window aboutdialog;
static struct ctk_label aboutlabel1 =
{CTK_LABEL(2, 0, 28, 1, "The Contiki Operating System")};
static struct ctk_label aboutlabel2 =
{CTK_LABEL(3, 2, 28, 1, "A modern, Internet-enabled")};
static struct ctk_label aboutlabel3 =
{CTK_LABEL(6, 3, 20, 1, "operating system and")};
static struct ctk_label aboutlabel4 =
{CTK_LABEL(6, 4, 20, 1, "desktop environment.")};
static char abouturl_petscii[] = "http://www.sics.se/~adam/contiki/";
static char abouturl_ascii[40];
static struct ctk_hyperlink abouturl =
{CTK_HYPERLINK(0, 6, 32, "http://www.sics.se/~adam/contiki/",
abouturl_ascii)};
static struct ctk_button aboutclose =
{CTK_BUTTON(12, 8, 5, "Close")};
PROCESS(about_process, "About Contiki");
AUTOSTART_PROCESSES(&about_process);
/*-----------------------------------------------------------------------------------*/
static void
about_quit(void)
{
ctk_dialog_close();
process_exit(&about_process);
LOADER_UNLOAD();
}
/*-----------------------------------------------------------------------------------*/
PROCESS_THREAD(about_process, ev, data)
{
unsigned char width;
PROCESS_BEGIN();
width = ctk_desktop_width(NULL);
strcpy(abouturl_ascii, abouturl_petscii);
petsciiconv_toascii(abouturl_ascii, sizeof(abouturl_ascii));
if(width > 34) {
ctk_dialog_new(&aboutdialog, 32, 9);
} else {
ctk_dialog_new(&aboutdialog, width - 2, 9);
}
CTK_WIDGET_ADD(&aboutdialog, &aboutlabel1);
CTK_WIDGET_ADD(&aboutdialog, &aboutlabel2);
CTK_WIDGET_ADD(&aboutdialog, &aboutlabel3);
CTK_WIDGET_ADD(&aboutdialog, &aboutlabel4);
if(width > 34) {
CTK_WIDGET_ADD(&aboutdialog, &abouturl);
CTK_WIDGET_SET_FLAG(&abouturl, CTK_WIDGET_FLAG_MONOSPACE);
} else {
CTK_WIDGET_SET_XPOS(&aboutlabel1, 0);
CTK_WIDGET_SET_XPOS(&aboutlabel2, 0);
CTK_WIDGET_SET_XPOS(&aboutlabel3, 0);
CTK_WIDGET_SET_XPOS(&aboutlabel4, 0);
CTK_WIDGET_SET_XPOS(&aboutclose, 0);
}
CTK_WIDGET_ADD(&aboutdialog, &aboutclose);
CTK_WIDGET_FOCUS(&aboutdialog, &aboutclose);
ctk_dialog_open(&aboutdialog);
while(1) {
PROCESS_WAIT_EVENT();
if(ev == PROCESS_EVENT_EXIT) {
about_quit();
PROCESS_EXIT();
} else if(ev == ctk_signal_button_activate) {
if(data == (process_data_t)&aboutclose) {
about_quit();
PROCESS_EXIT();
}
} else if(ev == ctk_signal_hyperlink_activate) {
if((struct ctk_widget *)data == (struct ctk_widget *)&abouturl) {
about_quit();
PROCESS_EXIT();
}
}
}
PROCESS_END();
}
/*-----------------------------------------------------------------------------------*/

View File

@@ -0,0 +1,4 @@
antelope_src = antelope.c aql-adt.c aql-exec.c aql-lexer.c aql-parser.c \
index.c index-inline.c index-maxheap.c lvm.c relation.c \
result.c storage-cfs.c
antelope_dsc =

View File

@@ -0,0 +1,160 @@
/*
* Copyright (c) 2010, Swedish Institute of Computer Science
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/**
* \file
* Main functions for Antelope, a DBMS for sensor devices.
*
* Antelope is described and evaluated in the paper A Database in
* Every Sensor, N. Tsiftes and A. Dunkels, in Proceedings of
* ACM SenSys 2011.
* \author
* Nicolas Tsiftes <nvt@sics.se>
*/
#include <stdio.h>
#include "antelope.h"
static db_output_function_t output = printf;
void
db_init(void)
{
relation_init();
index_init();
}
void
db_set_output_function(db_output_function_t f)
{
output = f;
}
const char *
db_get_result_message(db_result_t code)
{
switch(code) {
case DB_FINISHED:
return "Iteration finished";
case DB_OK:
return "Operation succeeded";
case DB_LIMIT_ERROR:
return "Limit reached";
case DB_ALLOCATION_ERROR:
return "Allocation error";
case DB_STORAGE_ERROR:
return "Storage error";
case DB_PARSING_ERROR:
return "Parsing error";
case DB_NAME_ERROR:
return "Invalid name";
case DB_RELATIONAL_ERROR:
return "Semantic error";
case DB_TYPE_ERROR:
return "Type error";
case DB_IMPLEMENTATION_ERROR:
return "Implementation error";
case DB_INDEX_ERROR:
return "Index error";
case DB_BUSY_ERROR:
return "Busy with processing";
case DB_INCONSISTENCY_ERROR:
return "Inconsistent handle";
case DB_ARGUMENT_ERROR:
return "Invalid argument";
default:
return "Unknown result code";
};
}
db_result_t
db_print_header(db_handle_t *handle)
{
int column;
attribute_t *attr;
output("[relation = %s, attributes = (", handle->result_rel->name);
attr = list_head(handle->result_rel->attributes);
for(column = 0; column < handle->ncolumns; column++) {
if(attr == NULL) {
return DB_IMPLEMENTATION_ERROR;
} else if(attr->flags & ATTRIBUTE_FLAG_NO_STORE) {
continue;
}
output("%s%s", column > 0 ? ", " : "", attr->name);
attr = attr->next;
}
output(")]\n");
return DB_OK;
}
db_result_t
db_print_tuple(db_handle_t *handle)
{
int column;
attribute_value_t value;
db_result_t result;
output("Row %lu:\t", (unsigned long)handle->current_row);
for(column = 0; column < handle->ncolumns; column++) {
result = db_get_value(&value, handle, column);
if(DB_ERROR(result)) {
output("Unable to get the value for row %lu, column %u: %s\n",
(unsigned long)handle->current_row, column,
db_get_result_message(result));
return result;
}
switch(value.domain) {
case DOMAIN_STRING:
output("\"%s\"\t", VALUE_STRING(&value));
break;
case DOMAIN_INT:
output("%d\t", (int)VALUE_INT(&value));
break;
case DOMAIN_LONG:
output("%ld\t", (long)VALUE_LONG(&value));
break;
default:
output("\nUnrecognized domain: %d\n", value.domain);
return DB_IMPLEMENTATION_ERROR;
}
}
output("\n");
return DB_OK;
}
int
db_processing(db_handle_t *handle)
{
return handle->flags & DB_HANDLE_FLAG_PROCESSING;
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright (c) 2010, Swedish Institute of Computer Science
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/**
* \file
* Declarations of the main Antelope functions.
* \author
* Nicolas Tsiftes <nvt@sics.se>
*/
#ifndef DB_H
#define DB_H
#include "db-types.h"
#include "result.h"
#include "aql.h"
typedef int (*db_output_function_t)(const char *, ...);
void db_init(void);
void db_set_output_function(db_output_function_t f);
const char *db_get_result_message(db_result_t code);
db_result_t db_print_header(db_handle_t *handle);
db_result_t db_print_tuple(db_handle_t *handle);
int db_processing(db_handle_t *handle);
#endif /* DB_H */

View File

@@ -0,0 +1,149 @@
/*
* Copyright (c) 2010, Swedish Institute of Computer Science
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of the Contiki operating system.
*
*/
/**
* \file
* Utilities for building the internal representation of an AQL command.
* \author
* Nicolas Tsiftes <nvt@sics.se>
*/
#include <string.h>
#include "aql.h"
#define DEBUG DEBUG_NONE
#include "net/ip/uip-debug.h"
static unsigned char char_buf[DB_MAX_CHAR_SIZE_PER_ROW];
static uint8_t next_free_offset;
static aql_attribute_t *
get_attribute(aql_adt_t *adt, char *name)
{
int i;
for(i = 0; i < AQL_ATTRIBUTE_COUNT(adt); i++) {
if(strcmp(adt->attributes[i].name, name) == 0) {
return &adt->attributes[i];
}
}
return NULL;
}
static unsigned char *
save_char(unsigned char *ptr, size_t length)
{
unsigned char *start_ptr;
if(length + next_free_offset > DB_MAX_CHAR_SIZE_PER_ROW) {
return NULL;
}
start_ptr = char_buf + next_free_offset;
memcpy(start_ptr, ptr, length);
next_free_offset += length;
return start_ptr;
}
void
aql_clear(aql_adt_t *adt)
{
char_buf[0] = 0;
next_free_offset = 0;
adt->optype = AQL_TYPE_NONE;
adt->relation_count = 0;
adt->attribute_count = 0;
adt->value_count = 0;
adt->flags = 0;
memset(adt->aggregators, 0, sizeof(adt->aggregators));
}
db_result_t
aql_add_attribute(aql_adt_t *adt, char *name, domain_t domain,
unsigned element_size, int processed_only)
{
aql_attribute_t *attr;
if(adt->attribute_count == AQL_ATTRIBUTE_LIMIT) {
return DB_LIMIT_ERROR;
}
if(processed_only && get_attribute(adt, name)) {
/* No need to have multiple instances of attributes that are only
used for processing in the PLE. */
return DB_OK;
}
attr = &adt->attributes[adt->attribute_count++];
if(strlen(name) + 1 > sizeof(attr->name)) {
return DB_LIMIT_ERROR;
}
strcpy(attr->name, name);
attr->domain = domain;
attr->element_size = element_size;
attr->flags = processed_only ? ATTRIBUTE_FLAG_NO_STORE : 0;
return DB_OK;
}
db_result_t
aql_add_value(aql_adt_t *adt, domain_t domain, void *value_ptr)
{
attribute_value_t *value;
if(adt->value_count == AQL_ATTRIBUTE_LIMIT) {
return DB_LIMIT_ERROR;
}
value = &adt->values[adt->value_count++];
value->domain = domain;
switch(domain) {
case DOMAIN_INT:
VALUE_LONG(value) = *(long *)value_ptr;
break;
case DOMAIN_STRING:
VALUE_STRING(value) = save_char(value_ptr, strlen(value_ptr) + 1);
if(VALUE_STRING(value) != NULL) {
break;
}
default:
return DB_TYPE_ERROR;
}
return DB_OK;
}

View File

@@ -0,0 +1,240 @@
/*
* Copyright (c) 2010, Swedish Institute of Computer Science
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of the Contiki operating system.
*
*/
/**
* \file
* Query execution functions for AQL.
* \author
* Nicolas Tsiftes <nvt@sics.se>
*/
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#define DEBUG DEBUG_NONE
#include "net/ip/uip-debug.h"
#include "index.h"
#include "relation.h"
#include "result.h"
#include "aql.h"
static aql_adt_t adt;
static void
clear_handle(db_handle_t *handle)
{
memset(handle, 0, sizeof(*handle));
handle->result_rel = NULL;
handle->left_rel = NULL;
handle->right_rel = NULL;
handle->join_rel = NULL;
}
static db_result_t
aql_execute(db_handle_t *handle, aql_adt_t *adt)
{
uint8_t optype;
int first_rel_arg;
db_result_t result;
relation_t *rel;
aql_attribute_t *attr;
attribute_t *relattr;
optype = AQL_GET_TYPE(adt);
if(optype == AQL_TYPE_NONE) {
/* No-ops always succeed. These can be generated by
empty lines or comments in the query language. */
return DB_OK;
}
/* If the ASSIGN flag is set, the first relation in the array is
the desired result relation. */
first_rel_arg = !!(adt->flags & AQL_FLAG_ASSIGN);
if(optype != AQL_TYPE_CREATE_RELATION &&
optype != AQL_TYPE_REMOVE_RELATION &&
optype != AQL_TYPE_JOIN) {
rel = relation_load(adt->relations[first_rel_arg]);
if(rel == NULL) {
return DB_NAME_ERROR;
}
} else {
rel = NULL;
}
result = DB_RELATIONAL_ERROR;
switch(optype) {
case AQL_TYPE_CREATE_ATTRIBUTE:
attr = &adt->attributes[0];
if(relation_attribute_add(rel, DB_STORAGE, attr->name, attr->domain,
attr->element_size) != NULL) {
result = DB_OK;
}
break;
case AQL_TYPE_CREATE_INDEX:
relattr = relation_attribute_get(rel, adt->attributes[0].name);
if(relattr == NULL) {
result = DB_NAME_ERROR;
break;
}
result = index_create(AQL_GET_INDEX_TYPE(adt), rel, relattr);
break;
case AQL_TYPE_CREATE_RELATION:
if(relation_create(adt->relations[0], DB_STORAGE) != NULL) {
result = DB_OK;
}
break;
case AQL_TYPE_REMOVE_ATTRIBUTE:
result = relation_attribute_remove(rel, adt->attributes[0].name);
break;
case AQL_TYPE_REMOVE_INDEX:
relattr = relation_attribute_get(rel, adt->attributes[0].name);
if(relattr != NULL) {
if(relattr->index != NULL) {
result = index_destroy(relattr->index);
} else {
result = DB_OK;
}
} else {
result = DB_NAME_ERROR;
}
break;
case AQL_TYPE_REMOVE_RELATION:
result = relation_remove(adt->relations[0], 1);
break;
#if DB_FEATURE_REMOVE
case AQL_TYPE_REMOVE_TUPLES:
/* Overwrite the attribute array with a full copy of the original
relation's attributes. */
adt->attribute_count = 0;
for(relattr = list_head(rel->attributes);
relattr != NULL;
relattr = relattr->next) {
AQL_ADD_ATTRIBUTE(adt, relattr->name, DOMAIN_UNSPECIFIED, 0);
}
AQL_SET_FLAG(adt, AQL_FLAG_INVERSE_LOGIC);
#endif /* DB_FEATURE_REMOVE */
case AQL_TYPE_SELECT:
if(handle == NULL) {
result = DB_ARGUMENT_ERROR;
break;
}
result = relation_select(handle, rel, adt);
break;
case AQL_TYPE_INSERT:
result = relation_insert(rel, adt->values);
break;
#if DB_FEATURE_JOIN
case AQL_TYPE_JOIN:
if(handle == NULL) {
result = DB_ARGUMENT_ERROR;
break;
}
handle->left_rel = relation_load(adt->relations[first_rel_arg]);
if(handle->left_rel == NULL) {
break;
}
handle->right_rel = relation_load(adt->relations[first_rel_arg + 1]);
if(handle->right_rel == NULL) {
relation_release(handle->left_rel);
break;
}
result = relation_join(handle, adt);
break;
#endif /* DB_FEATURE_JOIN */
default:
break;
}
if(rel != NULL) {
if(handle == NULL || !(handle->flags & DB_HANDLE_FLAG_PROCESSING)) {
relation_release(rel);
}
}
return result;
}
db_result_t
db_query(db_handle_t *handle, const char *format, ...)
{
va_list ap;
char query_string[AQL_MAX_QUERY_LENGTH];
va_start(ap, format);
vsnprintf(query_string, sizeof(query_string), format, ap);
va_end(ap);
if(handle != NULL) {
clear_handle(handle);
}
if(AQL_ERROR(aql_parse(&adt, query_string))) {
return DB_PARSING_ERROR;
}
/*aql_optimize(&adt);*/
return aql_execute(handle, &adt);
}
db_result_t
db_process(db_handle_t *handle)
{
uint8_t optype;
optype = ((aql_adt_t *)handle->adt)->optype;
switch(optype) {
#if DB_FEATURE_REMOVE
case AQL_TYPE_REMOVE_TUPLES:
return relation_process_remove(handle);
break;
#endif
case AQL_TYPE_SELECT:
return relation_process_select(handle);
break;
#if DB_FEATURE_JOIN
case AQL_TYPE_JOIN:
return relation_process_join(handle);
#endif /* DB_FEATURE_JOIN */
default:
break;
}
PRINTF("DB: Invalid operation type: %d\n", optype);
return DB_INCONSISTENCY_ERROR;
}

View File

@@ -0,0 +1,274 @@
/*
* Copyright (c) 2010, Swedish Institute of Computer Science
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/**
* \file
* Lexical analyzer for AQL, the Antelope Query Language.
* \author
* Nicolas Tsiftes <nvt@sics.se>
*/
#include "aql.h"
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct keyword {
char *string;
token_t token;
};
/* The keywords are arranged primarily by length and
secondarily by expected lookup frequency. */
static const struct keyword keywords[] = {
{";", END},
{"(", LEFT_PAREN},
{")", RIGHT_PAREN},
{",", COMMA},
{"=", EQUAL},
{">", GT},
{"<", LT},
{".", DOT},
{"+", ADD},
{"-", SUB},
{"*", MUL},
{"/", DIV},
{"#", COMMENT},
{">=", GEQ},
{"<=", LEQ},
{"<>", NOT_EQUAL},
{"<-", ASSIGN},
{"OR", OR},
{"IS", IS},
{"ON", ON},
{"IN", IN},
{"AND", AND},
{"NOT", NOT},
{"SUM", SUM},
{"MAX", MAX},
{"MIN", MIN},
{"INT", INT},
{"INTO", INTO},
{"FROM", FROM},
{"MEAN", MEAN},
{"JOIN", JOIN},
{"LONG", LONG},
{"TYPE", TYPE},
{"WHERE", WHERE},
{"COUNT", COUNT},
{"INDEX", INDEX},
{"INSERT", INSERT},
{"SELECT", SELECT},
{"REMOVE", REMOVE},
{"CREATE", CREATE},
{"MEDIAN", MEDIAN},
{"DOMAIN", DOMAIN},
{"STRING", STRING},
{"INLINE", INLINE},
{"PROJECT", PROJECT},
{"MAXHEAP", MAXHEAP},
{"MEMHASH", MEMHASH},
{"RELATION", RELATION},
{"ATTRIBUTE", ATTRIBUTE}
};
/* Provides a pointer to the first keyword of a specific length. */
static const int8_t skip_hint[] = {0, 13, 21, 27, 33, 36, 44, 47, 48};
static char separators[] = "#.;,() \t\n";
int
lexer_start(lexer_t *lexer, char *input, token_t *token, value_t *value)
{
lexer->input = input;
lexer->prev_pos = input;
lexer->token = token;
lexer->value = value;
return 0;
}
static token_t
get_token_id(const char *string, const size_t length)
{
int start, end;
int i;
if(sizeof(skip_hint) < length || length < 1) {
return NONE;
}
start = skip_hint[length - 1];
if(sizeof(skip_hint) == length) {
end = sizeof(keywords) / sizeof(keywords[0]);
} else {
end = skip_hint[length];
}
for(i = start; i < end; i++) {
if(strncasecmp(keywords[i].string, string, length) == 0) {
return keywords[i].token;
}
}
return NONE;
}
static int
next_real(lexer_t *lexer, const char *s)
{
char *end;
long long_value;
#if DB_FEATURE_FLOATS
float float_value;
#endif /* DB_FEATURE_FLOATS */
errno = 0;
long_value = strtol(s, &end, 10);
#if DB_FEATURE_FLOATS
if(*end == '.') {
/* Process a float value. */
float_value = strtof(s, &end);
if(float_value == 0 && s == end) {
return -1;
}
memcpy(lexer->value, &float_value, sizeof(float_value));
*lexer->token = FLOAT_VALUE;
lexer->input = end;
return 1;
}
#endif /* DB_FEATURE_FLOATS */
/* Process an integer value. */
if(long_value == 0 && errno != 0) {
return -1;
}
memcpy(lexer->value, &long_value, sizeof(long_value));
*lexer->token = INTEGER_VALUE;
lexer->input = end;
return 1;
}
static int
next_string(lexer_t *lexer, const char *s)
{
char *end;
size_t length;
end = strchr(s, '\'');
if(end == NULL) {
return -1;
}
length = end - s;
*lexer->token = STRING_VALUE;
lexer->input = end + 1; /* Skip the closing delimiter. */
memcpy(lexer->value, s, length);
(*lexer->value)[length] = '\0';
return 1;
}
static int
next_token(lexer_t *lexer, const char *s)
{
size_t length;
length = strcspn(s, separators);
if(length == 0) {
/* We encountered a separator, so we try to get a token of
precisely 1 byte. */
length = 1;
}
*lexer->token = get_token_id(s, length);
lexer->input = s + length;
if(*lexer->token != NONE) {
return 1;
}
/* The input did not constitute a valid token,
so we regard it as an identifier. */
*lexer->token = IDENTIFIER;
memcpy(lexer->value, s, length);
(*lexer->value)[length] = '\0';
return 1;
}
int
lexer_next(lexer_t *lexer)
{
const char *s;
*lexer->token = NONE;
s = lexer->input;
s += strspn(s, " \t\n");
lexer->prev_pos = s;
switch(*s) {
case '\'':
/* Process the string that follows the delimiter. */
return next_string(lexer, s + 1);
case '\0':
return 0;
default:
if(isdigit((int)*s) || (*s == '-' && isdigit((int)s[1]))) {
return next_real(lexer, s);
}
/* Process a token. */
return next_token(lexer, s);
}
}
void
lexer_rewind(lexer_t *lexer)
{
lexer->input = lexer->prev_pos;
}

View File

@@ -0,0 +1,856 @@
/*
* Copyright (c) 2010, Swedish Institute of Computer Science
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/**
* \file
* A recursive parser for AQL, the Antelope Query Language.
* \author
* Nicolas Tsiftes <nvt@sics.se>
*/
#include "attribute.h"
#include "db-options.h"
#include "index.h"
#include "aql.h"
#include "lvm.h"
#include <limits.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#define DEBUG DEBUG_NONE
#include "debug.h"
#if DEBUG
static char error_message[DB_ERROR_BUF_SIZE];
static int error_line;
static const char *error_function;
#define RETURN(value) \
do { \
if(error_message[0] == '\0') { \
strncpy(error_message, lexer->input, sizeof(error_message) - 1); \
error_line = __LINE__; \
error_function = __func__; \
} \
} while(0); \
return (value)
#define RESET_ERROR() \
do { \
error_message[0] = '\0'; \
error_line = 0; \
error_function = NULL; \
} while(0)
#else
#define RETURN(value) return (value)
#define RESET_ERROR()
#endif
#define PARSER(name) \
static aql_status_t \
parse_##name(lexer_t *lexer)
#define PARSER_ARG(name, arg) \
static aql_status_t \
parse_##name(lexer_t *lexer, arg)
#define PARSER_TOKEN(name) \
static token_t \
parse_##name(lexer_t *lexer)
#define PARSE(name) \
!AQL_ERROR(parse_##name(lexer))
#define PARSE_TOKEN(name) \
parse_##name(lexer)
#define NEXT lexer_next(lexer)
#define REWIND lexer_rewind(lexer); RESET_ERROR()
#define TOKEN *lexer->token
#define VALUE *lexer->value
#define CONSUME(token) \
do { \
NEXT; \
if(TOKEN != (token)) { \
RETURN(SYNTAX_ERROR); \
} \
} while(0)
/* The parsing of AQL results in this aql_adt_t object. */
static aql_adt_t *adt;
/* Conditional statements are compiled into VM bytecode, which is stored in
an instance of the LogicVM. */
static lvm_instance_t p;
static unsigned char vmcode[DB_VM_BYTECODE_SIZE];
/* Parsing functions for AQL. */
PARSER_TOKEN(cmp)
{
NEXT;
switch(TOKEN) {
case EQUAL:
case NOT_EQUAL:
case GT:
case LT:
case GEQ:
case LEQ:
return TOKEN;
default:
return NONE;
}
}
PARSER_TOKEN(op)
{
NEXT;
switch(TOKEN) {
case ADD:
case SUB:
case MUL:
case DIV:
case RIGHT_PAREN:
return TOKEN;
default:
return NONE;
}
}
PARSER_TOKEN(aggregator)
{
NEXT;
switch(TOKEN) {
case COUNT:
case SUM:
case MEAN:
case MEDIAN:
case MAX:
case MIN:
return TOKEN;
default:
return NONE;
}
}
PARSER(attributes)
{
token_t token;
aql_aggregator_t function;
token = PARSE_TOKEN(aggregator);
if(token != NONE) {
switch(TOKEN) {
case COUNT:
function = AQL_COUNT;
break;
case SUM:
function = AQL_SUM;
break;
case MEAN:
function = AQL_MEAN;
break;
case MEDIAN:
function = AQL_MEDIAN;
break;
case MAX:
function = AQL_MAX;
break;
case MIN:
function = AQL_MIN;
break;
default:
RETURN(SYNTAX_ERROR);
}
AQL_SET_FLAG(adt, AQL_FLAG_AGGREGATE);
PRINTF("aggregator: %d\n", TOKEN);
/* Parse the attribute to aggregate. */
CONSUME(LEFT_PAREN);
CONSUME(IDENTIFIER);
AQL_ADD_AGGREGATE(adt, function, VALUE);
PRINTF("aggregated attribute: %s\n", VALUE);
CONSUME(RIGHT_PAREN);
goto check_more_attributes;
} else {
REWIND;
}
/* Plain identifier. */
CONSUME(IDENTIFIER);
AQL_ADD_ATTRIBUTE(adt, VALUE, DOMAIN_UNSPECIFIED, 0);
check_more_attributes:
NEXT;
if(TOKEN == COMMA) {
if(!PARSE(attributes)) {
RETURN(SYNTAX_ERROR);
}
} else {
REWIND;
}
RETURN(OK);
}
PARSER(relations)
{
/* Parse comma-separated identifiers for relations. */
CONSUME(IDENTIFIER);
AQL_ADD_RELATION(adt, VALUE);
NEXT;
if(TOKEN == COMMA) {
if(!PARSE(relations)) {
RETURN(SYNTAX_ERROR);
}
} else {
REWIND;
}
RETURN(OK);
}
PARSER(values)
{
/* Parse comma-separated attribute values. */
NEXT;
switch(TOKEN) {
case STRING_VALUE:
AQL_ADD_VALUE(adt, DOMAIN_STRING, VALUE);
break;
case INTEGER_VALUE:
AQL_ADD_VALUE(adt, DOMAIN_INT, VALUE);
break;
default:
RETURN(SYNTAX_ERROR);
}
NEXT;
if(TOKEN == COMMA) {
return PARSE(values);
} else {
REWIND;
}
RETURN(OK);
}
PARSER(operand)
{
NEXT;
switch(TOKEN) {
case IDENTIFIER:
lvm_register_variable(VALUE, LVM_LONG);
lvm_set_variable(&p, VALUE);
AQL_ADD_PROCESSING_ATTRIBUTE(adt, VALUE);
break;
case STRING_VALUE:
break;
case FLOAT_VALUE:
break;
case INTEGER_VALUE:
lvm_set_long(&p, *(long *)lexer->value);
break;
default:
RETURN(SYNTAX_ERROR);
}
RETURN(OK);
}
PARSER(expr)
{
token_t token;
size_t saved_end;
operator_t op;
saved_end = lvm_get_end(&p);
NEXT;
if(TOKEN == LEFT_PAREN) {
if(!PARSE(expr)) {
RETURN(SYNTAX_ERROR);
}
CONSUME(RIGHT_PAREN);
} else {
REWIND;
if(!PARSE(operand)) {
RETURN(SYNTAX_ERROR);
}
}
while(1) {
token = PARSE_TOKEN(op);
if(token == NONE) {
saved_end = lvm_get_end(&p);
REWIND;
break;
} else if (token == RIGHT_PAREN) {
break;
}
if(!PARSE(operand) && !PARSE(expr)) {
RETURN(SYNTAX_ERROR);
}
saved_end = lvm_shift_for_operator(&p, saved_end);
switch(token) {
case ADD:
op = LVM_ADD;
break;
case SUB:
op = LVM_SUB;
break;
case MUL:
op = LVM_MUL;
break;
case DIV:
op = LVM_DIV;
break;
default:
RETURN(SYNTAX_ERROR);
}
lvm_set_op(&p, op);
lvm_set_end(&p, saved_end);
}
return OK;
}
PARSER(comparison)
{
token_t token;
size_t saved_end;
operator_t rel;
saved_end = lvm_jump_to_operand(&p);
if(!PARSE(expr)) {
RETURN(SYNTAX_ERROR);
}
saved_end = lvm_set_end(&p, saved_end);
token = PARSE_TOKEN(cmp);
if(token == NONE) {
RETURN(SYNTAX_ERROR);
}
switch(token) {
case GT:
rel = LVM_GE;
break;
case GEQ:
rel = LVM_GEQ;
break;
case LT:
rel = LVM_LE;
break;
case LEQ:
rel = LVM_LEQ;
break;
case EQUAL:
rel = LVM_EQ;
break;
case NOT_EQUAL:
rel = LVM_NEQ;
break;
default:
RETURN(SYNTAX_ERROR);
}
lvm_set_relation(&p, rel);
lvm_set_end(&p, saved_end);
if(!PARSE(expr)) {
RETURN(SYNTAX_ERROR);
}
RETURN(OK);
}
PARSER(where)
{
int r;
operator_t connective;
size_t saved_end;
if(!PARSE(comparison)) {
RETURN(SYNTAX_ERROR);
}
saved_end = 0;
/* The WHERE clause can consist of multiple prepositions. */
for(;;) {
NEXT;
if(TOKEN != AND && TOKEN != OR) {
REWIND;
break;
}
connective = TOKEN == AND ? LVM_AND : LVM_OR;
saved_end = lvm_shift_for_operator(&p, saved_end);
lvm_set_relation(&p, connective);
lvm_set_end(&p, saved_end);
NEXT;
if(TOKEN == LEFT_PAREN) {
r = PARSE(where);
if(!r) {
RETURN(SYNTAX_ERROR);
}
CONSUME(RIGHT_PAREN);
} else {
REWIND;
r = PARSE(comparison);
if(!r) {
RETURN(r);
}
}
}
lvm_print_code(&p);
return OK;
}
PARSER(join)
{
AQL_SET_TYPE(adt, AQL_TYPE_JOIN);
CONSUME(IDENTIFIER);
PRINTF("Left relation: %s\n", VALUE);
AQL_ADD_RELATION(adt, VALUE);
CONSUME(COMMA);
CONSUME(IDENTIFIER);
PRINTF("Right relation: %s\n", VALUE);
AQL_ADD_RELATION(adt, VALUE);
CONSUME(ON);
CONSUME(IDENTIFIER);
PRINTF("Join on attribute %s\n", VALUE);
AQL_ADD_ATTRIBUTE(adt, VALUE, DOMAIN_UNSPECIFIED, 0);
CONSUME(PROJECT);
/* projection attributes... */
if(!PARSE(attributes)) {
RETURN(SYNTAX_ERROR);
}
CONSUME(END);
RETURN(OK);
}
PARSER(select)
{
AQL_SET_TYPE(adt, AQL_TYPE_SELECT);
/* projection attributes... */
if(!PARSE(attributes)) {
RETURN(SYNTAX_ERROR);
}
CONSUME(FROM);
if(!PARSE(relations)) {
RETURN(SYNTAX_ERROR);
}
NEXT;
if(TOKEN == WHERE) {
lvm_reset(&p, vmcode, sizeof(vmcode));
if(!PARSE(where)) {
RETURN(SYNTAX_ERROR);
}
AQL_SET_CONDITION(adt, &p);
} else {
REWIND;
RETURN(OK);
}
CONSUME(END);
return OK;
}
PARSER(insert)
{
AQL_SET_TYPE(adt, AQL_TYPE_INSERT);
CONSUME(LEFT_PAREN);
if(!PARSE(values)) {
RETURN(SYNTAX_ERROR);
}
CONSUME(RIGHT_PAREN);
CONSUME(INTO);
if(!PARSE(relations)) {
RETURN(SYNTAX_ERROR);
}
RETURN(OK);
}
PARSER(remove_attribute)
{
AQL_SET_TYPE(adt, AQL_TYPE_REMOVE_ATTRIBUTE);
CONSUME(IDENTIFIER);
AQL_ADD_RELATION(adt, VALUE);
CONSUME(DOT);
CONSUME(IDENTIFIER);
PRINTF("Removing the index for the attribute %s\n", VALUE);
AQL_ADD_ATTRIBUTE(adt, VALUE, DOMAIN_UNSPECIFIED, 0);
RETURN(OK);
}
#if DB_FEATURE_REMOVE
PARSER(remove_from)
{
AQL_SET_TYPE(adt, AQL_TYPE_REMOVE_TUPLES);
/* Use a temporary persistent relation to assign the query result to. */
AQL_SET_FLAG(adt, AQL_FLAG_ASSIGN);
AQL_ADD_RELATION(adt, REMOVE_RELATION);
CONSUME(IDENTIFIER);
AQL_ADD_RELATION(adt, VALUE);
CONSUME(WHERE);
lvm_reset(&p, vmcode, sizeof(vmcode));
AQL_SET_CONDITION(adt, &p);
return PARSE(where);
}
#endif /* DB_FEATURE_REMOVE */
PARSER(remove_index)
{
AQL_SET_TYPE(adt, AQL_TYPE_REMOVE_INDEX);
CONSUME(IDENTIFIER);
AQL_ADD_RELATION(adt, VALUE);
CONSUME(DOT);
CONSUME(IDENTIFIER);
PRINTF("remove index: %s\n", VALUE);
AQL_ADD_ATTRIBUTE(adt, VALUE, DOMAIN_UNSPECIFIED, 0);
RETURN(OK);
}
PARSER(remove_relation)
{
AQL_SET_TYPE(adt, AQL_TYPE_REMOVE_RELATION);
CONSUME(IDENTIFIER);
PRINTF("remove relation: %s\n", VALUE);
AQL_ADD_RELATION(adt, VALUE);
RETURN(OK);
}
PARSER(remove)
{
aql_status_t r;
NEXT;
switch(TOKEN) {
case ATTRIBUTE:
r = PARSE(remove_attribute);
break;
#if DB_FEATURE_REMOVE
case FROM:
r = PARSE(remove_from);
break;
#endif
case INDEX:
r = PARSE(remove_index);
break;
case RELATION:
r = PARSE(remove_relation);
break;
default:
RETURN(SYNTAX_ERROR);
}
if(!r) {
RETURN(SYNTAX_ERROR);
}
CONSUME(END);
RETURN(OK);
}
PARSER_TOKEN(index_type)
{
index_type_t type;
NEXT;
switch(TOKEN) {
case INLINE:
type = INDEX_INLINE;
break;
case MAXHEAP:
type = INDEX_MAXHEAP;
break;
case MEMHASH:
type = INDEX_MEMHASH;
break;
default:
return NONE;
};
AQL_SET_INDEX_TYPE(adt, type);
return TOKEN;
}
PARSER(create_index)
{
AQL_SET_TYPE(adt, AQL_TYPE_CREATE_INDEX);
CONSUME(IDENTIFIER);
AQL_ADD_RELATION(adt, VALUE);
CONSUME(DOT);
CONSUME(IDENTIFIER);
PRINTF("Creating an index for the attribute %s\n", VALUE);
AQL_ADD_ATTRIBUTE(adt, VALUE, DOMAIN_UNSPECIFIED, 0);
CONSUME(TYPE);
if(PARSE_TOKEN(index_type) == NONE) {
RETURN(SYNTAX_ERROR);
}
RETURN(OK);
}
PARSER(create_relation)
{
CONSUME(IDENTIFIER);
AQL_SET_TYPE(adt, AQL_TYPE_CREATE_RELATION);
AQL_ADD_RELATION(adt, VALUE);
RETURN(OK);
}
PARSER_ARG(domain, char *name)
{
domain_t domain;
unsigned element_size;
NEXT;
switch(TOKEN) {
case STRING:
domain = DOMAIN_STRING;
/* Parse the amount of characters for this domain. */
CONSUME(LEFT_PAREN);
CONSUME(INTEGER_VALUE);
element_size = *(long *)lexer->value;
CONSUME(RIGHT_PAREN);
break;
case LONG:
domain = DOMAIN_LONG;
element_size = 4;
break;
case INT:
domain = DOMAIN_INT;
element_size = 2;
break;
default:
return NONE;
}
AQL_ADD_ATTRIBUTE(adt, name, domain, element_size);
return OK;
}
PARSER(create_attributes)
{
aql_status_t r;
char name[ATTRIBUTE_NAME_LENGTH];
AQL_SET_TYPE(adt, AQL_TYPE_CREATE_ATTRIBUTE);
CONSUME(IDENTIFIER);
strncpy(name, VALUE, sizeof(name) - 1);
name[sizeof(name) - 1] = '\0';
CONSUME(DOMAIN);
r = parse_domain(lexer, name);
if(AQL_ERROR(r)) {
RETURN(r);
}
CONSUME(IN);
CONSUME(IDENTIFIER);
AQL_ADD_RELATION(adt, VALUE);
RETURN(OK);
}
PARSER(create)
{
aql_status_t r;
NEXT;
switch(TOKEN) {
case ATTRIBUTE:
r = PARSE(create_attributes);
break;
case INDEX:
r = PARSE(create_index);
break;
case RELATION:
r = PARSE(create_relation);
break;
default:
RETURN(SYNTAX_ERROR);
}
if(!r) {
RETURN(SYNTAX_ERROR);
}
CONSUME(END);
RETURN(OK);
}
aql_status_t
aql_parse(aql_adt_t *external_adt, char *input_string)
{
lexer_t lex;
token_t token = NONE;
value_t value;
aql_status_t result;
RESET_ERROR();
PRINTF("Parsing \"%s\"\n", input_string);
adt = external_adt;
AQL_CLEAR(adt);
AQL_SET_CONDITION(adt, NULL);
lexer_start(&lex, input_string, &token, &value);
result = lexer_next(&lex);
if(!AQL_ERROR(result)) {
switch(token) {
case IDENTIFIER:
PRINTF("Assign the result to relation %s\n", *lex.value);
AQL_ADD_RELATION(adt, *lex.value);
AQL_SET_FLAG(adt, AQL_FLAG_ASSIGN);
if(AQL_ERROR(lexer_next(&lex))) {
result = SYNTAX_ERROR;
break;
}
if(*lex.token != ASSIGN) {
result = SYNTAX_ERROR;
break;
}
if(AQL_ERROR(lexer_next(&lex))) {
result = SYNTAX_ERROR;
break;
}
switch(*lex.token) {
case SELECT:
result = parse_select(&lex);
break;
case JOIN:
result = parse_join(&lex);
break;
default:
result = SYNTAX_ERROR;
}
break;
case JOIN:
result = parse_join(&lex);
break;
case CREATE:
result = parse_create(&lex);
break;
case REMOVE:
result = parse_remove(&lex);
break;
case INSERT:
result = parse_insert(&lex);
break;
case SELECT:
result = parse_select(&lex);
break;
case NONE:
case COMMENT:
result = OK;
case END:
break;
default:
result = SYNTAX_ERROR;
}
}
if(AQL_ERROR(result)) {
PRINTF("Error in function %s, line %d: input \"%s\"\n",
error_function, error_line, error_message);
}
return result;
}

View File

@@ -0,0 +1,221 @@
/*
* Copyright (c) 2010, Swedish Institute of Computer Science
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of the Contiki operating system.
*
*/
/**
* \file
* Definitions and declarations for AQL, the Antelope Query Language.
* \author
* Nicolas Tsiftes <nvt@sics.se>
*/
#ifndef AQL_H
#define AQL_H
#include "db-options.h"
#include "index.h"
#include "relation.h"
#include "result.h"
enum aql_status {
OK = 2,
SYNTAX_ERROR = 3,
INVALID_TOKEN = 9,
PLE_ERROR = 12
};
typedef enum aql_status aql_status_t;
#define AQL_ERROR(x) ((x) >= 3)
enum token {
END = 0,
LEFT_PAREN = 1,
RIGHT_PAREN = 2,
COMMA = 3,
EQUAL = 4,
GT = 5,
LT = 6,
DOT = 7,
ADD = 8,
SUB = 9,
MUL = 10,
DIV = 11,
COMMENT = 12,
GEQ = 13,
LEQ = 14,
NOT_EQUAL = 15,
ASSIGN = 16,
OR = 17,
IS = 18,
ON = 19,
IN = 20,
AND = 21,
NOT = 22,
SUM = 23,
MAX = 24,
MIN = 25,
INT = 26,
INTO = 27,
FROM = 28,
MEAN = 29,
JOIN = 30,
LONG = 31,
TYPE = 32,
WHERE = 33,
COUNT = 34,
INDEX = 35,
INSERT = 36,
SELECT = 37,
REMOVE = 38,
CREATE = 39,
MEDIAN = 40,
DOMAIN = 41,
STRING = 42,
INLINE = 43,
PROJECT = 44,
MAXHEAP = 45,
MEMHASH = 46,
RELATION = 47,
ATTRIBUTE = 48,
INTEGER_VALUE = 251,
FLOAT_VALUE = 252,
STRING_VALUE = 253,
IDENTIFIER = 254,
NONE = 255
};
typedef enum token token_t;
typedef char value_t[DB_MAX_ELEMENT_SIZE];
struct lexer {
const char *input;
const char *prev_pos;
token_t *token;
value_t *value;
};
typedef struct lexer lexer_t;
enum aql_aggregator {
AQL_NONE = 0,
AQL_COUNT = 1,
AQL_SUM = 2,
AQL_MIN = 3,
AQL_MAX = 4,
AQL_MEAN = 5,
AQL_MEDIAN = 6
};
typedef enum aql_aggregator aql_aggregator_t;
struct aql_attribute {
domain_t domain;
uint8_t element_size;
uint8_t flags;
char name[ATTRIBUTE_NAME_LENGTH + 1];
};
typedef struct aql_attribute aql_attribute_t;
struct aql_adt {
char relations[AQL_RELATION_LIMIT][RELATION_NAME_LENGTH + 1];
aql_attribute_t attributes[AQL_ATTRIBUTE_LIMIT];
aql_aggregator_t aggregators[AQL_ATTRIBUTE_LIMIT];
attribute_value_t values[AQL_ATTRIBUTE_LIMIT];
index_type_t index_type;
uint8_t relation_count;
uint8_t attribute_count;
uint8_t value_count;
uint8_t optype;
uint8_t flags;
void *lvm_instance;
};
typedef struct aql_adt aql_adt_t;
#define AQL_TYPE_NONE 0
#define AQL_TYPE_SELECT 1
#define AQL_TYPE_INSERT 2
#define AQL_TYPE_UPDATE 3
#define AQL_TYPE_DROP 4
#define AQL_TYPE_DELETE 5
#define AQL_TYPE_RENAME 6
#define AQL_TYPE_CREATE_ATTRIBUTE 7
#define AQL_TYPE_CREATE_INDEX 8
#define AQL_TYPE_CREATE_RELATION 9
#define AQL_TYPE_REMOVE_ATTRIBUTE 10
#define AQL_TYPE_REMOVE_INDEX 11
#define AQL_TYPE_REMOVE_RELATION 12
#define AQL_TYPE_REMOVE_TUPLES 13
#define AQL_TYPE_JOIN 14
#define AQL_FLAG_AGGREGATE 1
#define AQL_FLAG_ASSIGN 2
#define AQL_FLAG_INVERSE_LOGIC 4
#define AQL_CLEAR(adt) aql_clear(adt)
#define AQL_SET_TYPE(adt, type) (((adt))->optype = (type))
#define AQL_GET_TYPE(adt) ((adt)->optype)
#define AQL_SET_INDEX_TYPE(adt, type) ((adt)->index_type = (type))
#define AQL_GET_INDEX_TYPE(adt) ((adt)->index_type)
#define AQL_SET_FLAG(adt, flag) (((adt)->flags) |= (flag))
#define AQL_GET_FLAGS(adt) ((adt)->flags)
#define AQL_ADD_RELATION(adt, rel) \
strcpy((adt)->relations[(adt)->relation_count++], (rel))
#define AQL_RELATION_COUNT(adt) ((adt)->relation_count)
#define AQL_ADD_ATTRIBUTE(adt, attr, dom, size) \
aql_add_attribute(adt, attr, dom, size, 0)
#define AQL_ADD_PROCESSING_ATTRIBUTE(adt, attr) \
aql_add_attribute((adt), (attr), DOMAIN_UNSPECIFIED, 0, 1)
#define AQL_ADD_AGGREGATE(adt, function, attr) \
do { \
(adt)->aggregators[(adt)->attribute_count] = (function); \
aql_add_attribute((adt), (attr), DOMAIN_UNSPECIFIED, 0, 0); \
} while(0)
#define AQL_ATTRIBUTE_COUNT(adt) ((adt)->attribute_count)
#define AQL_SET_CONDITION(adt, cond) ((adt)->lvm_instance = (cond))
#define AQL_ADD_VALUE(adt, domain, value) \
aql_add_value((adt), (domain), (value))
int lexer_start(lexer_t *, char *, token_t *, value_t *);
int lexer_next(lexer_t *);
void lexer_rewind(lexer_t *);
void aql_clear(aql_adt_t *adt);
aql_status_t aql_parse(aql_adt_t *adt, char *query_string);
db_result_t aql_add_attribute(aql_adt_t *adt, char *name,
domain_t domain, unsigned element_size,
int processed_only);
db_result_t aql_add_value(aql_adt_t *adt, domain_t domain, void *value);
db_result_t db_query(db_handle_t *handle, const char *format, ...);
db_result_t db_process(db_handle_t *handle);
#endif /* !AQL_H */

View File

@@ -0,0 +1,89 @@
/*
* Copyright (c) 2010, Swedish Institute of Computer Science
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/**
* \file
* Definitions for attributes.
* \author
* Nicolas Tsiftes <nvt@sics.se>
*/
#ifndef ATTRIBUTE_H
#define ATTRIBUTE_H
#include <stdint.h>
#include <stdlib.h>
#include "lib/list.h"
#include "db-options.h"
typedef enum {
DOMAIN_UNSPECIFIED = 0,
DOMAIN_INT = 1,
DOMAIN_LONG = 2,
DOMAIN_STRING = 3,
DOMAIN_FLOAT = 4
} domain_t;
#define ATTRIBUTE_FLAG_NO_STORE 0x1
#define ATTRIBUTE_FLAG_INVALID 0x2
#define ATTRIBUTE_FLAG_PRIMARY_KEY 0x4
#define ATTRIBUTE_FLAG_UNIQUE 0x8
struct attribute {
struct attribute *next;
void *index;
long aggregation_value;
uint8_t aggregator;
uint8_t domain;
uint8_t element_size;
uint8_t flags;
char name[ATTRIBUTE_NAME_LENGTH + 1];
};
typedef struct attribute attribute_t;
typedef uint8_t attribute_id_t;
struct attribute_value {
union {
int int_value;
long long_value;
unsigned char *string_value;
} u;
domain_t domain;
};
typedef struct attribute_value attribute_value_t;
#define VALUE_LONG(value) (value)->u.long_value
#define VALUE_INT(value) (value)->u.int_value
#define VALUE_STRING(value) (value)->u.string_value
#endif /* ATTRIBUTES_H */

View File

@@ -0,0 +1,231 @@
/*
* Copyright (c) 2010, Swedish Institute of Computer Science
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/**
* \file
* Database configuration options.
* \author
* Nicolas Tsiftes <nvt@sics.se>
*/
#ifndef DB_OPTIONS_H
#define DB_OPTIONS_H
#include "contiki-conf.h"
/*----------------------------------------------------------------------------*/
/* Optional Antelope features. Include only what is needed
in order to save space. */
/* Support join operations on relations. */
#ifndef DB_FEATURE_JOIN
#define DB_FEATURE_JOIN 1
#endif /* DB_FEATURE_JOIN */
/* Support tuple removals. */
#ifndef DB_FEATURE_REMOVE
#define DB_FEATURE_REMOVE 1
#endif /* DB_FEATURE_REMOVE */
/* Support floating-point values in attributes. */
#ifndef DB_FEATURE_FLOATS
#define DB_FEATURE_FLOATS 0
#endif /* DB_FEATURE_FLOATS */
/* Optimize storage access for the Coffee file system. */
#ifndef DB_FEATURE_COFFEE
#define DB_FEATURE_COFFEE 1
#endif /* DB_FEATURE_COFFEE */
/* Enable basic data integrity checks. */
#ifndef DB_FEATURE_INTEGRITY
#define DB_FEATURE_INTEGRITY 0
#endif /* DB_FEATURE_INTEGRITY */
/*----------------------------------------------------------------------------*/
/* Configuration parameters that may be trimmed to save space. */
/* The size of the error message buffer used by the parser. */
#ifndef DB_ERROR_BUF_SIZE
#define DB_ERROR_BUF_SIZE 50
#endif /* DB_ERROR_BUF_SIZE */
/* The maximum number of indexes in use by all relations loaded in memory. */
#ifndef DB_INDEX_POOL_SIZE
#define DB_INDEX_POOL_SIZE 3
#endif /* DB_INDEX_POOL_SIZE */
/* The maximum number of relations loaded in memory. */
#ifndef DB_RELATION_POOL_SIZE
#define DB_RELATION_POOL_SIZE 5
#endif /* DB_RELATION_POOL_SIZE */
/* The maximum number of attributes loaded in memory. */
#ifndef DB_ATTRIBUTE_POOL_SIZE
#define DB_ATTRIBUTE_POOL_SIZE 16
#endif /* DB_ATTRIBUTE_POOL_SIZE */
/* The maximum number of attributes in a relation. */
#ifndef DB_MAX_ATTRIBUTES_PER_RELATION
#define DB_MAX_ATTRIBUTES_PER_RELATION 6
#endif /* DB_MAX_ATTRIBUTES_PER_RELATION */
/* The maximum physical storage size on an attribute value. */
#ifndef DB_MAX_ELEMENT_SIZE
#define DB_MAX_ELEMENT_SIZE 16
#endif /* DB_MAX_ELEMENT_SIZE */
/* The maximum size of the LVM bytecode compiled from a
single database query. */
#ifndef DB_VM_BYTECODE_SIZE
#define DB_VM_BYTECODE_SIZE 128
#endif /* DB_VM_BYTECODE_SIZE */
/*----------------------------------------------------------------------------*/
/* Language options. */
/* The maximum length of a database query in AQL text format. */
#ifndef AQL_MAX_QUERY_LENGTH
#define AQL_MAX_QUERY_LENGTH 128
#endif /* AQL_MAX_QUERY_LENGTH */
#ifndef AQL_MAX_VALUE_LENGTH
#define AQL_MAX_VALUE_LENGTH DB_MAX_ELEMENT_SIZE
#endif /* AQL_MAX_VALUE_LENGTH */
/* The maximum number of relations used in a single query. */
#ifndef AQL_RELATION_LIMIT
#define AQL_RELATION_LIMIT 3
#endif /* AQL_RELATION_LIMIT */
/* The maximum number of attributes used in a single query. */
#ifndef AQL_ATTRIBUTE_LIMIT
#define AQL_ATTRIBUTE_LIMIT 5
#endif /* AQL_ATTRIBUTE_LIMIT */
/*----------------------------------------------------------------------------*/
/*
* Physical storage options. Changing these options might cause
* compatibility problems if the database files are moved between
* different installations of Antelope.
*/
/* The default relation file size to reserve when using Coffee. */
#ifndef DB_COFFEE_RESERVE_SIZE
#define DB_COFFEE_RESERVE_SIZE (128 * 1024UL)
#endif /* DB_COFFEE_RESERVE_SIZE */
/* The maximum size of the physical storage of a tuple (labelled a "row"
in Antelope's terminology. */
#ifndef DB_MAX_CHAR_SIZE_PER_ROW
#define DB_MAX_CHAR_SIZE_PER_ROW 64
#endif /* DB_MAX_CHAR_SIZE_PER_ROW */
/* The maximum file name length to use for creating various database file. */
#ifndef DB_MAX_FILENAME_LENGTH
#define DB_MAX_FILENAME_LENGTH 16
#endif /* DB_MAX_FILENAME_LENGTH */
/* The maximum length of an attribute name. */
#ifndef ATTRIBUTE_NAME_LENGTH
#define ATTRIBUTE_NAME_LENGTH 12
#endif /* ATTRIBUTE_NAME_LENGTH */
/* The maximum length on a relation name. */
#ifndef RELATION_NAME_LENGTH
#define RELATION_NAME_LENGTH 10
#endif /* RELATION_NAME_LENGTH */
/* The name of the intermediate "result" relation file, which is used
for presenting the result of a query to a user. */
#ifndef RESULT_RELATION
#define RESULT_RELATION "db-result"
#endif /* RESULT_RELATION */
/* The name of the relation used for processing a REMOVE query. */
#ifndef REMOVE_RELATION
#define REMOVE_RELATION "db-remove"
#endif /* REMOVE_RELATION */
/*----------------------------------------------------------------------------*/
/* Index options. */
#ifndef DB_INDEX_COST
#define DB_INDEX_COST 64
#endif /* DB_INDEX_COST */
/* The maximum number of hash table indexes. */
#ifndef DB_MEMHASH_INDEX_LIMIT
#define DB_MEMHASH_INDEX_LIMIT 1
#endif /* DB_MEMHASH_INDEX_LIMIT */
/* The default hash table index size. */
#ifndef DB_MEMHASH_TABLE_SIZE
#define DB_MEMHASH_TABLE_SIZE 61
#endif /* DB_MEMHASH_TABLE_SIZE */
/* The maximum number of Maxheap indexes. */
#ifndef DB_HEAP_INDEX_LIMIT
#define DB_HEAP_INDEX_LIMIT 1
#endif /* DB_HEAP_INDEX_LIMIT */
/* The maximum number of buckets cached in the MaxHeap index. */
#ifndef DB_HEAP_CACHE_LIMIT
#define DB_HEAP_CACHE_LIMIT 1
#endif /* DB_HEAP_CACHE_LIMIT */
/*----------------------------------------------------------------------------*/
/* LVM options. */
/* The maximum length of a variable in LVM. This value should preferably
be identical to the maximum attribute name length. */
#ifndef LVM_MAX_NAME_LENGTH
#define LVM_MAX_NAME_LENGTH ATTRIBUTE_NAME_LENGTH
#endif /* LVM_MAX_NAME_LENGTH */
/* The maximum variable identifier number in the LVM. The default
value corresponds to the highest attribute ID. */
#ifndef LVM_MAX_VARIABLE_ID
#define LVM_MAX_VARIABLE_ID AQL_ATTRIBUTE_LIMIT - 1
#endif /* LVM_MAX_VARIABLE_ID */
/* Specify whether floats should be used or not inside the LVM. */
#ifndef LVM_USE_FLOATS
#define LVM_USE_FLOATS DB_FEATURE_FLOATS
#endif /* LVM_USE_FLOATS */
#endif /* !DB_OPTIONS_H */

View File

@@ -0,0 +1,64 @@
/*
* Copyright (c) 2010, Swedish Institute of Computer Science
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/**
* \file
* .
* \author
* Nicolas Tsiftes <nvt@sics.se>
*/
#ifndef DB_TYPES_H
#define DB_TYPES_H
enum db_result {
DB_FINISHED = 3,
DB_GOT_ROW = 2,
DB_OK = 1,
DB_LIMIT_ERROR = -1,
DB_ALLOCATION_ERROR = -2,
DB_STORAGE_ERROR = -3,
DB_PARSING_ERROR = -4,
DB_NAME_ERROR = -5,
DB_RELATIONAL_ERROR = -6,
DB_TYPE_ERROR = -7,
DB_IMPLEMENTATION_ERROR = -8,
DB_INDEX_ERROR = -9,
DB_BUSY_ERROR = -10,
DB_INCONSISTENCY_ERROR = -11,
DB_ARGUMENT_ERROR = -12
};
typedef enum db_result db_result_t;
typedef int db_storage_id_t;
#define DB_ERROR(result_code) ((result_code) < DB_OK)
#define DB_SUCCESS(result_code) !DB_ERROR(result_code)
#endif /* !DB_TYPES_H */

View File

@@ -0,0 +1,63 @@
/*
* Copyright (c) 2010, Swedish Institute of Computer Science.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of the Contiki operating system.
*
*/
/**
* \file
* A set of debugging macros.
*
* \author Nicolas Tsiftes <nvt@sics.se>
* Niclas Finne <nfi@sics.se>
* Joakim Eriksson <joakime@sics.se>
*/
#ifndef UIP_DEBUG_H
#define UIP_DEBUG_H
#define DEBUG_NONE 0
#define DEBUG_PRINT 1
#define DEBUG_ANNOTATE 2
#define DEBUG_FULL DEBUG_ANNOTATE | DEBUG_PRINT
#if (DEBUG) & DEBUG_ANNOTATE
#include <stdio.h>
#define ANNOTATE(...) printf(__VA_ARGS__)
#else
#define ANNOTATE(...)
#endif /* (DEBUG) & DEBUG_ANNOTATE */
#if (DEBUG) & DEBUG_PRINT
#include <stdio.h>
#define PRINTF(...) printf(__VA_ARGS__)
#else
#define PRINTF(...)
#endif /* (DEBUG) & DEBUG_PRINT */
#endif

View File

@@ -0,0 +1,227 @@
/*
* Copyright (c) 2010, Swedish Institute of Computer Science
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/**
* \file
* A binary search index for attributes that are constrained to be
* monotonically increasing, which is a rather common pattern for
* time series or keys. Since this index has no storage overhead,
* it does not wear out the flash memory nor does it occupy any
* space. Furthermore, unlike B+-trees, it has a O(1) memory
* footprint in relation to the number of data items.
* \author
* Nicolas Tsiftes <nvt@sics.se>
*/
#include <stdlib.h>
#include <string.h>
#include "index.h"
#include "relation.h"
#include "result.h"
#include "storage.h"
#define DEBUG DEBUG_NONE
#include "net/ip/uip-debug.h"
struct search_handle {
index_t *index;
tuple_id_t start_row;
tuple_id_t end_row;
};
struct search_handle handle;
static db_result_t null_op(index_t *);
static db_result_t insert(index_t *, attribute_value_t *, tuple_id_t);
static db_result_t delete(index_t *, attribute_value_t *);
static tuple_id_t get_next(index_iterator_t *);
/*
* The create, destroy, load, release, insert, and delete operations
* of the index API always succeed because the index does not store
* items separately from the row file. The four former operations share
* the same signature, and are thus implemented by the null_op function
* to save space.
*/
index_api_t index_inline = {
INDEX_INLINE,
INDEX_API_EXTERNAL | INDEX_API_COMPLETE | INDEX_API_RANGE_QUERIES,
null_op,
null_op,
null_op,
null_op,
insert,
delete,
get_next
};
static attribute_value_t *
get_value(tuple_id_t *index, relation_t *rel, attribute_t *attr)
{
unsigned char row[rel->row_length];
static attribute_value_t value;
if(DB_ERROR(storage_get_row(rel, index, row))) {
return NULL;
}
if(DB_ERROR(relation_get_value(rel, attr, row, &value))) {
PRINTF("DB: Unable to retrieve a value from tuple %ld\n", (long)(*index));
return NULL;
}
return &value;
}
static tuple_id_t
binary_search(index_iterator_t *index_iterator,
attribute_value_t *target_value,
int exact_match)
{
relation_t *rel;
attribute_t *attr;
attribute_value_t *cmp_value;
tuple_id_t min;
tuple_id_t max;
tuple_id_t center;
rel = index_iterator->index->rel;
attr = index_iterator->index->attr;
max = relation_cardinality(rel);
if(max == INVALID_TUPLE) {
return INVALID_TUPLE;
}
max--;
min = 0;
do {
center = min + ((max - min) / 2);
cmp_value = get_value(&center, rel, attr);
if(cmp_value == NULL) {
PRINTF("DB: Failed to get the center value, index = %ld\n",
(long)center);
return INVALID_TUPLE;
}
if(db_value_to_long(target_value) > db_value_to_long(cmp_value)) {
min = center + 1;
} else {
max = center - 1;
}
} while(min <= max &&
db_value_to_long(target_value) != db_value_to_long(cmp_value));
if(exact_match &&
db_value_to_long(target_value) != db_value_to_long(cmp_value)) {
PRINTF("DB: Could not find value %ld in the inline index\n",
db_value_to_long(target_value));
return INVALID_TUPLE;
}
return center;
}
static tuple_id_t
range_search(index_iterator_t *index_iterator,
tuple_id_t *start, tuple_id_t *end)
{
attribute_value_t *low_target;
attribute_value_t *high_target;
int exact_match;
low_target = &index_iterator->min_value;
high_target = &index_iterator->max_value;
PRINTF("DB: Search index for value range (%ld, %ld)\n",
db_value_to_long(low_target), db_value_to_long(high_target));
exact_match = db_value_to_long(low_target) == db_value_to_long(high_target);
/* Optimize later so that the other search uses the result
from the first one. */
*start = binary_search(index_iterator, low_target, exact_match);
if(*start == INVALID_TUPLE) {
return DB_INDEX_ERROR;
}
*end = binary_search(index_iterator, high_target, exact_match);
if(*end == INVALID_TUPLE) {
return DB_INDEX_ERROR;
}
return DB_OK;
}
static db_result_t
null_op(index_t *index)
{
return DB_OK;
}
static db_result_t
insert(index_t *index, attribute_value_t *value, tuple_id_t tuple_id)
{
return DB_OK;
}
static db_result_t
delete(index_t *index, attribute_value_t *value)
{
return DB_OK;
}
static tuple_id_t
get_next(index_iterator_t *iterator)
{
static tuple_id_t cached_start;
static tuple_id_t cached_end;
if(iterator->next_item_no == 0) {
/*
* We conduct the actual index search when the caller attempts to
* access the first item in the iteration. The first and last tuple
* id:s of the result get cached for subsequent iterations.
*/
if(DB_ERROR(range_search(iterator, &cached_start, &cached_end))) {
cached_start = 0;
cached_end = 0;
return INVALID_TUPLE;
}
PRINTF("DB: Cached the tuple range (%ld,%ld)\n",
(long)cached_start, (long)cached_end);
++iterator->next_item_no;
return cached_start;
} else if(cached_start + iterator->next_item_no <= cached_end) {
return cached_start + iterator->next_item_no++;
}
return INVALID_TUPLE;
}

View File

@@ -0,0 +1,750 @@
/*
* Copyright (c) 2010, Swedish Institute of Computer Science
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/**
* \file
* MaxHeap - A binary maximum heap index for flash memory.
*
* The idea behind the MaxHeap index is to write entries sequentially
* into small buckets, which are indexed in a binary maximum heap.
* Although sequential writes make the entries unsorted within a
* bucket, the time to load and scan a single bucket is small. The
* sequential write is important for flash memories, which are
* unable to handle multiple rewrites of the same page without doing
* an expensive erase operation between the rewrites.
*
* Each bucket specifies a range (a,b) of values that it accepts.
* Once a bucket fills up, two buckets are created with the ranges
* (a,mean) and (mean+1, b), respectively. The entries from the
* original bucket are then copied into the appropriate new bucket
* before the old bucket gets deleted.
* \author
* Nicolas Tsiftes <nvt@sics.se>
*/
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cfs/cfs.h"
#include "cfs/cfs-coffee.h"
#include "lib/memb.h"
#include "lib/random.h"
#include "db-options.h"
#include "index.h"
#include "result.h"
#include "storage.h"
#define DEBUG DEBUG_NONE
#include "net/ip/uip-debug.h"
#define BRANCH_FACTOR 2
#define BUCKET_SIZE 128
#define NODE_LIMIT 511
#define NODE_DEPTH 9
#if (1 << NODE_DEPTH) != (NODE_LIMIT + 1)
#error "NODE_DEPTH is set incorrectly."
#endif
#define EMPTY_NODE(node) ((node)->min == 0 && (node)->max == 0)
#define EMPTY_PAIR(pair) ((pair)->key == 0 && (pair)->value == 0)
typedef uint16_t maxheap_key_t;
typedef uint16_t maxheap_value_t;
#define KEY_MIN 0
#define KEY_MAX 65535
struct heap_node {
maxheap_key_t min;
maxheap_key_t max;
};
typedef struct heap_node heap_node_t;
struct key_value_pair {
maxheap_key_t key;
maxheap_value_t value;
};
struct bucket {
struct key_value_pair pairs[BUCKET_SIZE];
};
typedef struct bucket bucket_t;
struct heap {
db_storage_id_t heap_storage;
db_storage_id_t bucket_storage;
/* Remember where the next free slot for each bucket is located. */
uint8_t next_free_slot[NODE_LIMIT];
};
typedef struct heap heap_t;
struct bucket_cache {
heap_t *heap;
uint16_t bucket_id;
bucket_t bucket;
};
/* Keep a cache of buckets read from storage. */
static struct bucket_cache bucket_cache[DB_HEAP_CACHE_LIMIT];
MEMB(heaps, heap_t, DB_HEAP_INDEX_LIMIT);
static struct bucket_cache *get_cache(heap_t *, int);
static struct bucket_cache *get_cache_free(void);
static void invalidate_cache(void);
static maxheap_key_t transform_key(maxheap_key_t);
static int heap_read(heap_t *, int, heap_node_t *);
static int heap_write(heap_t *, int, heap_node_t *);
static int heap_insert(heap_t *, maxheap_key_t, maxheap_key_t);
static int heap_find(heap_t *, maxheap_key_t key, int *iterator);
#if HEAP_DEBUG
static void heap_print(heap_t *);
#endif
static int bucket_read(heap_t *, int, bucket_t *);
static struct bucket_cache *bucket_load(heap_t *, int);
static int bucket_append(heap_t *, int, struct key_value_pair *);
static int bucket_split(heap_t *, int);
static db_result_t create(index_t *);
static db_result_t destroy(index_t *);
static db_result_t load(index_t *);
static db_result_t release(index_t *);
static db_result_t insert(index_t *, attribute_value_t *, tuple_id_t);
static db_result_t delete(index_t *, attribute_value_t *);
static tuple_id_t get_next(index_iterator_t *);
index_api_t index_maxheap = {
INDEX_MAXHEAP,
INDEX_API_EXTERNAL,
create,
destroy,
load,
release,
insert,
delete,
get_next
};
static struct bucket_cache *
get_cache(heap_t *heap, int bucket_id)
{
int i;
for(i = 0; i < DB_HEAP_CACHE_LIMIT; i++) {
if(bucket_cache[i].heap == heap && bucket_cache[i].bucket_id == bucket_id) {
return &bucket_cache[i];
}
}
return NULL;
}
static struct bucket_cache *
get_cache_free(void)
{
int i;
for(i = 0; i < DB_HEAP_CACHE_LIMIT; i++) {
if(bucket_cache[i].heap == NULL) {
return &bucket_cache[i];
}
}
return NULL;
}
static void
invalidate_cache(void)
{
int i;
for(i = 0; i < DB_HEAP_CACHE_LIMIT; i++) {
if(bucket_cache[i].heap != NULL) {
bucket_cache[i].heap = NULL;
break;
}
}
}
static maxheap_key_t
transform_key(maxheap_key_t key)
{
random_init(key);
return random_rand();
}
static int
heap_read(heap_t *heap, int bucket_id, heap_node_t *node)
{
if(DB_ERROR(storage_read(heap->heap_storage, node,
DB_MAX_FILENAME_LENGTH + (unsigned long)bucket_id * sizeof(*node), sizeof(*node)))) {
return 0;
}
return 1;
}
static int
heap_write(heap_t *heap, int bucket_id, heap_node_t *node)
{
if(DB_ERROR(storage_write(heap->heap_storage, node,
DB_MAX_FILENAME_LENGTH + (unsigned long)bucket_id * sizeof(*node), sizeof(*node)))) {
return 0;
}
return 1;
}
static int
heap_insert(heap_t *heap, maxheap_key_t min, maxheap_key_t max)
{
int i;
heap_node_t node;
PRINTF("DB: Insert node (%ld,%ld) into the heap\n", (long)min, (long)max);
if(min > max) {
return -1;
}
for(i = 0; i < NODE_LIMIT;) {
if(heap_read(heap, i, &node) == 0) {
PRINTF("DB: Failed to read heap node %d\n", i);
return -1;
}
if(EMPTY_NODE(&node)) {
node.min = min;
node.max = max;
if(heap_write(heap, i, &node) == 0) {
PRINTF("DB: Failed to write heap node %d\n", i);
return -1;
}
return i;
} else if(node.min <= min && max <= node.max) {
i = BRANCH_FACTOR * i + 1;
} else {
i++;
}
}
PRINTF("DB: No more nodes available\n");
return -1;
}
static int
heap_find(heap_t *heap, maxheap_key_t key, int *iterator)
{
maxheap_key_t hashed_key;
int i;
int first_child;
static heap_node_t node;
hashed_key = transform_key(key);
for(i = *iterator; i < NODE_LIMIT;) {
if(heap_read(heap, i, &node) == 0) {
break;
}
if(EMPTY_NODE(&node)) {
break;
} else if(node.min <= hashed_key && hashed_key <= node.max) {
first_child = BRANCH_FACTOR * i + 1;
if(first_child >= NODE_LIMIT) {
break;
}
*iterator = first_child;
return i;
} else {
i++;
}
}
return -1;
}
#if HEAP_DEBUG
static void
heap_print(heap_t *heap)
{
int level_count;
int branch_count;
int branch_amount;
int i, j;
heap_node_t node;
level_count = 0;
branch_count = 0;
branch_amount = BRANCH_FACTOR;
for(i = 0;; i++) {
if(heap_read(heap, i, &node) == 0 || EMPTY_NODE(&node)) {
break;
}
for(j = 0; j < level_count; j++) {
PRINTF("\t");
}
PRINTF("(%ld,%ld)\n", (long)node.min, (long)node.max);
if(level_count == 0) {
level_count++;
} else if(branch_count + 1 == branch_amount) {
level_count++;
branch_count = 0;
branch_amount = branch_amount * BRANCH_FACTOR;
} else {
branch_count++;
}
}
}
#endif /* HEAP_DEBUG */
static int
bucket_read(heap_t *heap, int bucket_id, bucket_t *bucket)
{
size_t size;
if(heap->next_free_slot[bucket_id] == 0) {
size = BUCKET_SIZE;
} else {
size = heap->next_free_slot[bucket_id];
}
size *= sizeof(struct key_value_pair);
if(DB_ERROR(storage_read(heap->bucket_storage, bucket,
(unsigned long)bucket_id * sizeof(*bucket), size))) {
return 0;
}
return 1;
}
static struct bucket_cache *
bucket_load(heap_t *heap, int bucket_id)
{
int i;
struct bucket_cache *cache;
cache = get_cache(heap, bucket_id);
if(cache != NULL) {
return cache;
}
cache = get_cache_free();
if(cache == NULL) {
invalidate_cache();
cache = get_cache_free();
if(cache == NULL) {
return NULL;
}
}
if(bucket_read(heap, bucket_id, &cache->bucket) == 0) {
return NULL;
}
cache->heap = heap;
cache->bucket_id = bucket_id;
if(heap->next_free_slot[bucket_id] == 0) {
for(i = 0; i < BUCKET_SIZE; i++) {
if(EMPTY_PAIR(&cache->bucket.pairs[i])) {
break;
}
}
heap->next_free_slot[bucket_id] = i;
}
PRINTF("DB: Loaded bucket %d, the next free slot is %u\n", bucket_id,
(unsigned)heap->next_free_slot[bucket_id]);
return cache;
}
static int
bucket_append(heap_t *heap, int bucket_id, struct key_value_pair *pair)
{
unsigned long offset;
if(heap->next_free_slot[bucket_id] >= BUCKET_SIZE) {
PRINTF("DB: Invalid write attempt to the full bucket %d\n", bucket_id);
return 0;
}
offset = (unsigned long)bucket_id * sizeof(bucket_t);
offset += heap->next_free_slot[bucket_id] * sizeof(struct key_value_pair);
if(DB_ERROR(storage_write(heap->bucket_storage, pair, offset, sizeof(*pair)))) {
return 0;
}
heap->next_free_slot[bucket_id]++;
return 1;
}
static int
bucket_split(heap_t *heap, int bucket_id)
{
heap_node_t node;
maxheap_key_t mean;
int small_bucket_index;
int large_bucket_index;
if(heap_read(heap, bucket_id, &node) == 0) {
return 0;
}
mean = node.min + ((node.max - node.min) / 2);
PRINTF("DB: Split bucket %d (%ld, %ld) at mean value %ld\n", bucket_id,
(long)node.min, (long)node.max, (long)mean);
small_bucket_index = heap_insert(heap, node.min, mean);
if(small_bucket_index < 0) {
return 0;
}
large_bucket_index = heap_insert(heap, mean + 1, node.max);
if(large_bucket_index < 0) {
/*heap_remove(small_bucket);*/
return 0;
}
return 1;
}
int
insert_item(heap_t *heap, maxheap_key_t key, maxheap_value_t value)
{
int heap_iterator;
int bucket_id, last_good_bucket_id;
struct key_value_pair pair;
for(heap_iterator = 0, last_good_bucket_id = -1;;) {
bucket_id = heap_find(heap, key, &heap_iterator);
if(bucket_id < 0) {
break;
}
last_good_bucket_id = bucket_id;
}
bucket_id = last_good_bucket_id;
if(bucket_id < 0) {
PRINTF("DB: No bucket for key %ld\n", (long)key);
return 0;
}
pair.key = key;
pair.value = value;
if(heap->next_free_slot[bucket_id] == BUCKET_SIZE) {
PRINTF("DB: Bucket %d is full\n", bucket_id);
if(bucket_split(heap, bucket_id) == 0) {
return 0;
}
/* Select one of the newly created buckets. */
bucket_id = heap_find(heap, key, &heap_iterator);
if(bucket_id < 0) {
return 0;
}
}
if(bucket_append(heap, bucket_id, &pair) == 0) {
return 0;
}
PRINTF("DB: Inserted key %ld (hash %ld) into the heap at bucket_id %d\n",
(long)key, (long)transform_key(key), bucket_id);
return 1;
}
static db_result_t
create(index_t *index)
{
char heap_filename[DB_MAX_FILENAME_LENGTH];
char bucket_filename[DB_MAX_FILENAME_LENGTH];
char *filename;
db_result_t result;
heap_t *heap;
heap = NULL;
filename = NULL;
bucket_filename[0] = '\0';
/* Generate the heap file, which is the main index file that is
referenced from the metadata of the relation. */
filename = storage_generate_file("heap",
(unsigned long)NODE_LIMIT * sizeof(heap_node_t));
if(filename == NULL) {
PRINTF("DB: Failed to generate a heap file\n");
return DB_INDEX_ERROR;
}
memcpy(index->descriptor_file, filename,
sizeof(index->descriptor_file));
PRINTF("DB: Generated the heap file \"%s\" using %lu bytes of space\n",
index->descriptor_file, (unsigned long)NODE_LIMIT * sizeof(heap_node_t));
index->opaque_data = heap = memb_alloc(&heaps);
if(heap == NULL) {
PRINTF("DB: Failed to allocate a heap\n");
result = DB_ALLOCATION_ERROR;
goto end;
}
heap->heap_storage = -1;
heap->bucket_storage = -1;
/* Generate the bucket file, which stores the (key, value) pairs. */
filename = storage_generate_file("bucket",
(unsigned long)NODE_LIMIT * sizeof(bucket_t));
if(filename == NULL) {
PRINTF("DB: Failed to generate a bucket file\n");
result = DB_INDEX_ERROR;
goto end;
}
memcpy(bucket_filename, filename, sizeof(bucket_filename));
PRINTF("DB: Generated the bucket file \"%s\" using %lu bytes of space\n",
bucket_filename, (unsigned long)NODE_LIMIT * sizeof(bucket_t));
/* Initialize the heap. */
memset(&heap->next_free_slot, 0, sizeof(heap->next_free_slot));
heap->heap_storage = storage_open(index->descriptor_file);
heap->bucket_storage = storage_open(bucket_filename);
if(heap->heap_storage < 0 || heap->bucket_storage < 0) {
result = DB_STORAGE_ERROR;
goto end;
}
if(DB_ERROR(storage_write(heap->heap_storage, &bucket_filename, 0,
sizeof(bucket_filename)))) {
result = DB_STORAGE_ERROR;
goto end;
}
if(heap_insert(heap, KEY_MIN, KEY_MAX) < 0) {
PRINTF("DB: Heap insertion error\n");
result = DB_INDEX_ERROR;
goto end;
}
PRINTF("DB: Created a heap index\n");
result = DB_OK;
end:
if(result != DB_OK) {
if(heap != NULL) {
storage_close(heap->bucket_storage);
storage_close(heap->heap_storage);
memb_free(&heaps, heap);
}
if(index->descriptor_file[0] != '\0') {
cfs_remove(heap_filename);
index->descriptor_file[0] = '\0';
}
if(bucket_filename[0] != '\0') {
cfs_remove(bucket_filename);
}
}
return result;
}
static db_result_t
destroy(index_t *index)
{
release(index);
return DB_INDEX_ERROR;
}
static db_result_t
load(index_t *index)
{
heap_t *heap;
db_storage_id_t fd;
char bucket_file[DB_MAX_FILENAME_LENGTH];
index->opaque_data = heap = memb_alloc(&heaps);
if(heap == NULL) {
PRINTF("DB: Failed to allocate a heap\n");
return DB_ALLOCATION_ERROR;
}
fd = storage_open(index->descriptor_file);
if(fd < 0) {
return DB_STORAGE_ERROR;
}
if(storage_read(fd, bucket_file, 0, sizeof(bucket_file)) !=
sizeof(bucket_file)) {
storage_close(fd);
return DB_STORAGE_ERROR;
}
storage_close(fd);
heap->heap_storage = storage_open(index->descriptor_file);
heap->bucket_storage = storage_open(bucket_file);
memset(&heap->next_free_slot, 0, sizeof(heap->next_free_slot));
PRINTF("DB: Loaded max-heap index from file %s and bucket file %s\n",
index->descriptor_file, bucket_file);
return DB_OK;
}
static db_result_t
release(index_t *index)
{
heap_t *heap;
heap = index->opaque_data;
storage_close(heap->bucket_storage);
storage_close(heap->heap_storage);
memb_free(&heaps, index->opaque_data);
return DB_INDEX_ERROR;
}
static db_result_t
insert(index_t *index, attribute_value_t *key, tuple_id_t value)
{
heap_t *heap;
long long_key;
heap = (heap_t *)index->opaque_data;
long_key = db_value_to_long(key);
if(insert_item(heap, (maxheap_key_t)long_key,
(maxheap_value_t)value) == 0) {
PRINTF("DB: Failed to insert key %ld into a max-heap index\n", long_key);
return DB_INDEX_ERROR;
}
return DB_OK;
}
static db_result_t
delete(index_t *index, attribute_value_t *value)
{
return DB_INDEX_ERROR;
}
static tuple_id_t
get_next(index_iterator_t *iterator)
{
struct iteration_cache {
index_iterator_t *index_iterator;
int heap_iterator;
tuple_id_t found_items;
uint8_t start;
int visited_buckets[NODE_DEPTH];
int end;
};
static struct iteration_cache cache;
heap_t *heap;
maxheap_key_t key;
int bucket_id;
int tmp_heap_iterator;
int i;
struct bucket_cache *bcache;
uint8_t next_free_slot;
heap = (heap_t *)iterator->index->opaque_data;
key = *(maxheap_key_t *)&iterator->min_value;
if(cache.index_iterator != iterator || iterator->next_item_no == 0) {
/* Initialize the cache for a new search. */
cache.end = NODE_DEPTH - 1;
cache.found_items = cache.start = 0;
cache.index_iterator = iterator;
/* Find the downward path through the heap consisting of all nodes
that could possibly contain the key. */
for(i = tmp_heap_iterator = 0; i < NODE_DEPTH; i++) {
cache.visited_buckets[i] = heap_find(heap, key, &tmp_heap_iterator);
if(cache.visited_buckets[i] < 0) {
cache.end = i - 1;
break;
}
}
cache.heap_iterator = cache.end;
}
/*
* Search for the key in each heap node, starting from the bottom
* of the heap. Because the bottom nodes contain are very narrow
* range of keys, there is a much higher chance that the key will be
* there rather than at the top.
*/
for(; cache.heap_iterator >= 0; cache.heap_iterator--) {
bucket_id = cache.visited_buckets[cache.heap_iterator];
PRINTF("DB: Find key %lu in bucket %d\n", (unsigned long)key, bucket_id);
if((bcache = bucket_load(heap, bucket_id)) == NULL) {
PRINTF("DB: Failed to load bucket %d\n", bucket_id);
return INVALID_TUPLE;
}
/* Because keys are stored in an unsorted order in the bucket, we
* need to search the bucket sequentially. */
next_free_slot = heap->next_free_slot[bucket_id];
for(i = cache.start; i < next_free_slot; i++) {
if(bcache->bucket.pairs[i].key == key) {
if(cache.found_items++ == iterator->next_item_no) {
iterator->next_item_no++;
cache.start = i + 1;
PRINTF("DB: Found key %ld with value %lu\n", (long)key,
(unsigned long)bcache->bucket.pairs[i].value);
return (tuple_id_t)bcache->bucket.pairs[i].value;
}
}
}
}
if(VALUE_INT(&iterator->min_value) == VALUE_INT(&iterator->max_value)) {
PRINTF("DB: Could not find key %ld in the index\n", (long)key);
return INVALID_TUPLE;
}
iterator->next_item_no = 0;
VALUE_INT(&iterator->min_value)++;
return get_next(iterator);
}

View File

@@ -0,0 +1,194 @@
/*
* Copyright (c) 2010, Swedish Institute of Computer Science
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/**
* \file
* A memory-resident hash map used as a DB index.
* \author
* Nicolas Tsiftes <nvt@sics.se>
*/
#include <string.h>
#include "lib/memb.h"
#include "db-options.h"
#include "index.h"
#define DEBUG DEBUG_NONE
#include "net/ip/uip-debug.h"
static db_result_t create(index_t *);
static db_result_t destroy(index_t *);
static db_result_t load(index_t *);
static db_result_t release(index_t *);
static db_result_t insert(index_t *, attribute_value_t *, tuple_id_t);
static db_result_t delete(index_t *, attribute_value_t *);
static tuple_id_t get_next(index_iterator_t *);
index_api_t index_memhash = {
INDEX_MEMHASH,
INDEX_API_INTERNAL,
create,
destroy,
load,
release,
insert,
delete,
get_next
};
struct hash_item {
tuple_id_t tuple_id;
attribute_value_t value;
};
typedef struct hash_item hash_item_t;
typedef hash_item_t hash_map_t[DB_MEMHASH_TABLE_SIZE];
MEMB(hash_map_memb, hash_map_t, DB_MEMHASH_INDEX_LIMIT);
static unsigned
calculate_hash(attribute_value_t *value)
{
unsigned char *cp, *end;
unsigned hash_value;
cp = (unsigned char *)value;
end = cp + sizeof(*value);
hash_value = 0;
while(cp < end) {
hash_value = hash_value * 33 + *cp++;
}
return hash_value % DB_MEMHASH_TABLE_SIZE;
}
static db_result_t
create(index_t *index)
{
int i;
hash_map_t *hash_map;
PRINTF("Creating a memory-resident hash map index\n");
hash_map = memb_alloc(&hash_map_memb);
if(hash_map == NULL) {
return DB_ALLOCATION_ERROR;
}
for(i = 0; i < DB_MEMHASH_TABLE_SIZE; i++) {
hash_map[i]->tuple_id = INVALID_TUPLE;
}
index->opaque_data = hash_map;
return DB_OK;
}
static db_result_t
destroy(index_t *index)
{
memb_free(&hash_map_memb, index->opaque_data);
return DB_OK;
}
static db_result_t
load(index_t *index)
{
return create(index);
}
static db_result_t
release(index_t *index)
{
return destroy(index);
}
static db_result_t
insert(index_t *index, attribute_value_t *value, tuple_id_t tuple_id)
{
hash_map_t *hash_map;
uint16_t hash_value;
hash_map = index->opaque_data;
hash_value = calculate_hash(value);
hash_map[hash_value]->tuple_id = tuple_id;
hash_map[hash_value]->value = *value;
PRINTF("DB: Inserted value %ld into the hash table\n", VALUE_LONG(value));
return DB_OK;
}
static db_result_t
delete(index_t *index, attribute_value_t *value)
{
hash_map_t *hash_map;
uint16_t hash_value;
hash_map = index->opaque_data;
hash_value = calculate_hash(value);
if(memcmp(&hash_map[hash_value]->value, value, sizeof(*value)) != 0) {
return DB_INDEX_ERROR;
}
hash_map[hash_value]->tuple_id = INVALID_TUPLE;
return DB_OK;
}
static tuple_id_t
get_next(index_iterator_t *iterator)
{
hash_map_t *hash_map;
uint16_t hash_value;
if(iterator->next_item_no == 1) {
/* The memhash supports only unique values at the moment. */
return INVALID_TUPLE;
}
hash_map = iterator->index->opaque_data;
hash_value = calculate_hash(&iterator->min_value);
if(memcmp(&hash_map[hash_value]->value, &iterator->min_value, sizeof(iterator->min_value)) != 0) {
return INVALID_TUPLE;
}
iterator->next_item_no++;
PRINTF("DB: Found value %ld in the hash table\n",
VALUE_LONG(&iterator->min_value));
return hash_map[hash_value]->tuple_id;
}

View File

@@ -0,0 +1,433 @@
/*
* Copyright (c) 2010, Swedish Institute of Computer Science
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/**
* \file
* This component forwards index calls using the generic index
* API to specific implementations.
* \author
* Nicolas Tsiftes <nvt@sics.se>
*/
#include "contiki.h"
#include "lib/memb.h"
#include "lib/list.h"
#define DEBUG DEBUG_NONE
#include "net/ip/uip-debug.h"
#include "antelope.h"
#include "attribute.h"
#include "db-options.h"
#include "index.h"
#include "storage.h"
static index_api_t *index_components[] = {&index_inline,
&index_maxheap};
LIST(indices);
MEMB(index_memb, index_t, DB_INDEX_POOL_SIZE);
static process_event_t load_request_event;
PROCESS(db_indexer, "DB Indexer");
static index_api_t *
find_index_api(index_type_t index_type)
{
int i;
for(i = 0; i < sizeof(index_components) / sizeof(index_components[0]); i++) {
if(index_components[i]->type == index_type) {
return index_components[i];
}
}
return NULL;
}
void
index_init(void)
{
list_init(indices);
memb_init(&index_memb);
process_start(&db_indexer, NULL);
}
db_result_t
index_create(index_type_t index_type, relation_t *rel, attribute_t *attr)
{
tuple_id_t cardinality;
index_t *index;
index_api_t *api;
cardinality = relation_cardinality(rel);
if(cardinality == INVALID_TUPLE) {
return DB_STORAGE_ERROR;
}
if(attr->domain != DOMAIN_INT && attr->domain != DOMAIN_LONG) {
PRINTF("DB: Cannot create an index for a non-number attribute!\n");
return DB_INDEX_ERROR;
}
api = find_index_api(index_type);
if(api == NULL) {
PRINTF("DB: No API for index type %d\n", (int)index_type);
return DB_INDEX_ERROR;
}
if(attr->index != NULL) {
/* Refuse to overwrite the old index. */
PRINTF("DB: The attribute %s is already indexed\n", attr->name);
return DB_INDEX_ERROR;
}
index = memb_alloc(&index_memb);
if(index == NULL) {
PRINTF("DB: Failed to allocate an index\n");
return DB_ALLOCATION_ERROR;
}
index->rel = rel;
index->attr = attr;
index->api = api;
index->flags = 0;
index->opaque_data = NULL;
index->descriptor_file[0] = '\0';
index->type = index_type;
if(DB_ERROR(api->create(index))) {
memb_free(&index_memb, index);
PRINTF("DB: Index-specific creation failed for attribute %s\n", attr->name);
return DB_INDEX_ERROR;
}
attr->index = index;
list_push(indices, index);
if(index->descriptor_file[0] != '\0' &&
DB_ERROR(storage_put_index(index))) {
api->destroy(index);
memb_free(&index_memb, index);
PRINTF("DB: Failed to store index data in file \"%s\"\n",
index->descriptor_file);
return DB_INDEX_ERROR;
}
if(!(api->flags & INDEX_API_INLINE) && cardinality > 0) {
PRINTF("DB: Created an index for an old relation; issuing a load request\n");
index->flags = INDEX_LOAD_NEEDED;
process_post(&db_indexer, load_request_event, NULL);
} else {
/* Inline indexes (i.e., those using the existing storage of the relation)
do not need to be reloaded after restarting the system. */
PRINTF("DB: Index created for attribute %s\n", attr->name);
index->flags |= INDEX_READY;
}
return DB_OK;
}
db_result_t
index_destroy(index_t *index)
{
if(DB_ERROR(index_release(index)) ||
DB_ERROR(index->api->destroy(index))) {
return DB_INDEX_ERROR;
}
return DB_OK;
}
db_result_t
index_load(relation_t *rel, attribute_t *attr)
{
index_t *index;
index_api_t *api;
PRINTF("DB: Attempting to load an index over %s.%s\n", rel->name, attr->name);
index = memb_alloc(&index_memb);
if(index == NULL) {
PRINTF("DB: No more index objects available\n");
return DB_ALLOCATION_ERROR;
}
if(DB_ERROR(storage_get_index(index, rel, attr))) {
PRINTF("DB: Failed load an index descriptor from storage\n");
memb_free(&index_memb, index);
return DB_INDEX_ERROR;
}
index->rel = rel;
index->attr = attr;
index->opaque_data = NULL;
api = find_index_api(index->type);
if(api == NULL) {
PRINTF("DB: No API for index type %d\n", index->type);
return DB_INDEX_ERROR;
}
index->api = api;
if(DB_ERROR(api->load(index))) {
PRINTF("DB: Index-specific load failed\n");
return DB_INDEX_ERROR;
}
list_push(indices, index);
attr->index = index;
index->flags = INDEX_READY;
return DB_OK;
}
db_result_t
index_release(index_t *index)
{
if(DB_ERROR(index->api->release(index))) {
return DB_INDEX_ERROR;
}
index->attr->index = NULL;
list_remove(indices, index);
memb_free(&index_memb, index);
return DB_OK;
}
db_result_t
index_insert(index_t *index, attribute_value_t *value,
tuple_id_t tuple_id)
{
return index->api->insert(index, value, tuple_id);
}
db_result_t
index_delete(index_t *index, attribute_value_t *value)
{
if(index->flags != INDEX_READY) {
return DB_INDEX_ERROR;
}
return index->api->delete(index, value);
}
db_result_t
index_get_iterator(index_iterator_t *iterator, index_t *index,
attribute_value_t *min_value,
attribute_value_t *max_value)
{
tuple_id_t cardinality;
unsigned long range;
unsigned long max_range;
long max;
long min;
cardinality = relation_cardinality(index->rel);
if(cardinality == INVALID_TUPLE) {
return DB_STORAGE_ERROR;
}
if(index->flags != INDEX_READY) {
return DB_INDEX_ERROR;
}
min = db_value_to_long(min_value);
max = db_value_to_long(max_value);
range = (unsigned long)max - min;
if(range > 0) {
/*
* Index structures that do not have a natural ability to handle
* range queries (e.g., a hash index) can nevertheless emulate them.
*
* The range query emulation attempts to look up the key for each
* value in the search range. If the search range is sparse, this
* iteration will incur a considerable overhead per found key.
*
* Hence, the emulation is preferable when an external module wants
* to iterate over a narrow range of keys, for which the total
* search cost is smaller than that of an iteration over all tuples
* in the relation.
*/
if(!(index->api->flags & INDEX_API_RANGE_QUERIES)) {
PRINTF("DB: Range query requested for an index that does not support it\n");
max_range = cardinality / DB_INDEX_COST;
if(range > max_range) {
return DB_INDEX_ERROR;
}
PRINTF("DB: Using the index anyway because the range is small enough (%lu <= %lu)\n",
range, max_range);
}
}
iterator->index = index;
iterator->min_value = *min_value;
iterator->max_value = *max_value;
iterator->next_item_no = 0;
PRINTF("DB: Acquired an index iterator for %s.%s over the range (%ld,%ld)\n",
index->rel->name, index->attr->name,
min_value->u.long_value, max_value->u.long_value);
return DB_OK;
}
tuple_id_t
index_get_next(index_iterator_t *iterator)
{
long min;
long max;
if(iterator->index == NULL) {
/* This attribute is not indexed. */
return INVALID_TUPLE;
}
if((iterator->index->attr->flags & ATTRIBUTE_FLAG_UNIQUE) &&
iterator->next_item_no == 1) {
min = db_value_to_long(&iterator->min_value);
max = db_value_to_long(&iterator->max_value);
if(min == max) {
/*
* We stop if this is an equivalence search on an attribute
* whose values are unique, and we already found one item.
*/
PRINTF("DB: Equivalence search finished\n");
return INVALID_TUPLE;
}
}
return iterator->index->api->get_next(iterator);
}
int
index_exists(attribute_t *attr)
{
index_t *index;
index = (index_t *)attr->index;
if(index == NULL || index->flags != INDEX_READY) {
return 0;
}
return 1;
}
static index_t *
get_next_index_to_load(void)
{
index_t *index;
for(index = list_head(indices); index != NULL; index = index->next) {
if(index->flags & INDEX_LOAD_NEEDED) {
return index;
}
}
return NULL;
}
PROCESS_THREAD(db_indexer, ev, data)
{
static index_t *index;
static db_handle_t handle;
static tuple_id_t row;
db_result_t result;
attribute_value_t value;
int column;
PROCESS_BEGIN();
load_request_event = process_alloc_event();
for(;;) {
PROCESS_WAIT_EVENT_UNTIL(ev == load_request_event);
index = get_next_index_to_load();
if(index == NULL) {
PRINTF("DB: Request to load an index, but no index is set to be loaded\n");
continue;
}
PRINTF("DB: Loading the index for %s.%s...\n",
index->rel->name, index->attr->name);
/* Project the values of the indexed attribute from all tuples in
the relation, and insert them into the index again. */
if(DB_ERROR(db_query(&handle, "SELECT %s FROM %s;", index->attr->name, index->rel->name))) {
index->flags |= INDEX_LOAD_ERROR;
index->flags &= ~INDEX_LOAD_NEEDED;
continue;
}
for(;; row++) {
PROCESS_PAUSE();
result = db_process(&handle);
if(DB_ERROR(result)) {
PRINTF("DB: Index loading failed while processing: %s\n",
db_get_result_message(result));
index->flags |= INDEX_LOAD_ERROR;
goto cleanup;
}
if(result == DB_FINISHED) {
break;
}
for(column = 0; column < handle.ncolumns; column++) {
if(DB_ERROR(db_get_value(&value, &handle, column))) {
index->flags |= INDEX_LOAD_ERROR;
goto cleanup;
}
if(DB_ERROR(index_insert(index, &value, row))) {
index->flags |= INDEX_LOAD_ERROR;
goto cleanup;
}
}
}
PRINTF("DB: Loaded %lu rows into the index\n",
(unsigned long)handle.current_row);
cleanup:
if(index->flags & INDEX_LOAD_ERROR) {
PRINTF("DB: Failed to load the index for %s.%s\n",
index->rel->name, index->attr->name);
}
index->flags &= ~INDEX_LOAD_NEEDED;
index->flags |= INDEX_READY;
db_free(&handle);
}
PROCESS_END();
}

View File

@@ -0,0 +1,113 @@
/*
* Copyright (c) 2010, Swedish Institute of Computer Science
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/**
* \file
* .
* \author
* Nicolas Tsiftes <nvt@sics.se>
*/
#ifndef INDEX_H
#define INDEX_H
#include "relation.h"
typedef enum {
INDEX_NONE = 0,
INDEX_INLINE = 1,
INDEX_MEMHASH = 2,
INDEX_MAXHEAP = 3
} index_type_t;
#define INDEX_READY 0x00
#define INDEX_LOAD_NEEDED 0x01
#define INDEX_LOAD_ERROR 0x02
#define INDEX_API_INTERNAL 0x01
#define INDEX_API_EXTERNAL 0x02
#define INDEX_API_INLINE 0x04
#define INDEX_API_COMPLETE 0x08
#define INDEX_API_RANGE_QUERIES 0x10
struct index_api;
struct index {
struct index *next;
char descriptor_file[DB_MAX_FILENAME_LENGTH];
relation_t *rel;
attribute_t *attr;
struct index_api *api;
void *opaque_data;
index_type_t type;
uint8_t flags;
};
typedef struct index index_t;
struct index_iterator {
index_t *index;
attribute_value_t min_value;
attribute_value_t max_value;
tuple_id_t next_item_no;
tuple_id_t found_items;
};
typedef struct index_iterator index_iterator_t;
struct index_api {
index_type_t type;
uint8_t flags;
db_result_t (*create)(index_t *);
db_result_t (*destroy)(index_t *);
db_result_t (*load)(index_t *);
db_result_t (*release)(index_t *);
db_result_t (*insert)(index_t *, attribute_value_t *, tuple_id_t);
db_result_t (*delete)(index_t *, attribute_value_t *);
tuple_id_t (*get_next)(index_iterator_t *);
};
typedef struct index_api index_api_t;
extern index_api_t index_inline;
extern index_api_t index_maxheap;
extern index_api_t index_memhash;
void index_init(void);
db_result_t index_create(index_type_t, relation_t *, attribute_t *);
db_result_t index_destroy(index_t *);
db_result_t index_load(relation_t *, attribute_t *);
db_result_t index_release(index_t *);
db_result_t index_insert(index_t *, attribute_value_t *, tuple_id_t);
db_result_t index_delete(index_t *, attribute_value_t *);
db_result_t index_get_iterator(index_iterator_t *, index_t *,
attribute_value_t *, attribute_value_t *);
tuple_id_t index_get_next(index_iterator_t *);
int index_exists(attribute_t *);
#endif /* !INDEX_H */

View File

@@ -0,0 +1,973 @@
/*
* Copyright (c) 2010, Swedish Institute of Computer Science
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/**
* \file
* Logic engine used for quickly evaluating data constraints in relations.
* \author
* Nicolas Tsiftes <nvt@sics.se>
*/
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "aql.h"
#include "lvm.h"
#define DEBUG DEBUG_NONE
#include "debug.h"
/*
* The logic engine determines whether a logical predicate is true for
* each tuple in a relation. It uses a stack-based execution model of
* operations that are arranged in prefix (Polish) notation.
*/
/* Default option values. */
#ifndef LVM_MAX_NAME_LENGTH
#define LVM_MAX_NAME_LENGTH 16
#endif
#ifndef LVM_MAX_VARIABLE_ID
#define LVM_MAX_VARIABLE_ID 8
#endif
#ifndef LVM_USE_FLOATS
#define LVM_USE_FLOATS 0
#endif
#define IS_CONNECTIVE(op) ((op) & LVM_CONNECTIVE)
struct variable {
operand_type_t type;
operand_value_t value;
char name[LVM_MAX_NAME_LENGTH + 1];
};
typedef struct variable variable_t;
struct derivation {
operand_value_t max;
operand_value_t min;
uint8_t derived;
};
typedef struct derivation derivation_t;
/* Registered variables for a LVM expression. Their values may be
changed between executions of the expression. */
static variable_t variables[LVM_MAX_VARIABLE_ID - 1];
/* Range derivations of variables that are used for index searches. */
static derivation_t derivations[LVM_MAX_VARIABLE_ID - 1];
#if DEBUG
static void
print_derivations(derivation_t *d)
{
int i;
for(i = 0; i < LVM_MAX_VARIABLE_ID; i++) {
if(d[i].derived) {
printf("%s is constrained to (%ld,%ld)\n", variables[i].name,
d[i].min.l, d[i].max.l);
}
}
}
#endif /* DEBUG */
static variable_id_t
lookup(char *name)
{
variable_t *var;
for(var = variables; var <= &variables[LVM_MAX_VARIABLE_ID - 1] && var->name[0] != '\0'; var++) {
if(strcmp(var->name, name) == 0) {
break;
}
}
return (variable_id_t)(var - &variables[0]);
}
static operator_t *
get_operator(lvm_instance_t *p)
{
operator_t *operator;
operator = (operator_t *)&p->code[p->ip];
p->ip += sizeof(operator_t);
return operator;
}
static void
get_operand(lvm_instance_t *p, operand_t *operand)
{
memcpy(operand, &p->code[p->ip], sizeof(*operand));
p->ip += sizeof(*operand);
}
static node_type_t
get_type(lvm_instance_t *p)
{
node_type_t node_type;
node_type = *(node_type_t *)(p->code + p->ip);
p->ip += sizeof(node_type);
return node_type;
}
static long
operand_to_long(operand_t *operand)
{
switch(operand->type) {
case LVM_LONG:
return operand->value.l;
#if LVM_USE_FLOATS
case LVM_FLOAT:
return (long)operand->value.f;
break;
#endif /* LVM_USE_FLOATS */
case LVM_VARIABLE:
return variables[operand->value.id].value.l;
default:
return 0;
}
}
static lvm_status_t
eval_expr(lvm_instance_t *p, operator_t op, operand_t *result)
{
int i;
node_type_t type;
operator_t *operator;
operand_t operand[2];
long value[2];
long result_value;
lvm_status_t r;
for(i = 0; i < 2; i++) {
type = get_type(p);
switch(type) {
case LVM_ARITH_OP:
operator = get_operator(p);
r = eval_expr(p, *operator, &operand[i]);
if(LVM_ERROR(r)) {
return r;
}
break;
case LVM_OPERAND:
get_operand(p, &operand[i]);
break;
default:
return SEMANTIC_ERROR;
}
value[i] = operand_to_long(&operand[i]);
}
switch(op) {
case LVM_ADD:
result_value = value[0] + value[1];
break;
case LVM_SUB:
result_value = value[0] - value[1];
break;
case LVM_MUL:
result_value = value[0] * value[1];
break;
case LVM_DIV:
if(value[1] == 0) {
return MATH_ERROR;
}
result_value = value[0] / value[1];
break;
default:
return EXECUTION_ERROR;
}
result->type = LVM_LONG;
result->value.l = result_value;
return TRUE;
}
static int
eval_logic(lvm_instance_t *p, operator_t *op)
{
int i;
int r;
operand_t operand;
long result[2];
node_type_t type;
operator_t *operator;
long l1, l2;
int logic_result[2];
unsigned arguments;
if(IS_CONNECTIVE(*op)) {
arguments = *op == LVM_NOT ? 1 : 2;
for(i = 0; i < arguments; i++) {
type = get_type(p);
if(type != LVM_CMP_OP) {
return SEMANTIC_ERROR;
}
operator = get_operator(p);
logic_result[i] = eval_logic(p, operator);
if(LVM_ERROR(logic_result[i])) {
return logic_result[i];
}
}
if(*op == LVM_NOT) {
return !logic_result[0];
} else if(*op == LVM_AND) {
return logic_result[0] == TRUE && logic_result[1] == TRUE;
} else {
return logic_result[0] == TRUE || logic_result[1] == TRUE;
}
}
for(i = 0; i < 2; i++) {
type = get_type(p);
switch(type) {
case LVM_ARITH_OP:
operator = get_operator(p);
r = eval_expr(p, *operator, &operand);
if(LVM_ERROR(r)) {
return r;
}
break;
case LVM_OPERAND:
get_operand(p, &operand);
break;
default:
return SEMANTIC_ERROR;
}
result[i] = operand_to_long(&operand);
}
l1 = result[0];
l2 = result[1];
PRINTF("Result1: %ld\nResult2: %ld\n", l1, l2);
switch(*op) {
case LVM_EQ:
return l1 == l2;
case LVM_NEQ:
return l1 != l2;
case LVM_GE:
return l1 > l2;
case LVM_GEQ:
return l1 >= l2;
case LVM_LE:
return l1 < l2;
case LVM_LEQ:
return l1 <= l2;
default:
break;
}
return EXECUTION_ERROR;
}
void
lvm_reset(lvm_instance_t *p, unsigned char *code, lvm_ip_t size)
{
memset(code, 0, size);
p->code = code;
p->size = size;
p->end = 0;
p->ip = 0;
p->error = 0;
memset(variables, 0, sizeof(variables));
memset(derivations, 0, sizeof(derivations));
}
lvm_ip_t
lvm_jump_to_operand(lvm_instance_t *p)
{
lvm_ip_t old_end;
old_end = p->end;
p->end += sizeof(operator_t) + sizeof(node_type_t);
if(p->end >= p->size) {
p->error = __LINE__;
p->end = old_end;
}
return old_end;
}
lvm_ip_t
lvm_shift_for_operator(lvm_instance_t *p, lvm_ip_t end)
{
unsigned char *ptr;
lvm_ip_t old_end;
old_end = p->end;
if(p->end + sizeof(operator_t) > p->size || end >= old_end) {
p->error = __LINE__;
return 0;
}
ptr = p->code + end;
memmove(ptr + sizeof(operator_t) + sizeof(node_type_t), ptr, old_end - end);
p->end = end;
return old_end + sizeof(operator_t) + sizeof(node_type_t);
}
lvm_ip_t
lvm_get_end(lvm_instance_t *p)
{
return p->end;
}
lvm_ip_t
lvm_set_end(lvm_instance_t *p, lvm_ip_t end)
{
lvm_ip_t old_end;
if(end >= p->size) {
p->error = __LINE__;
return p->end;
}
old_end = p->end;
p->end = end;
return old_end;
}
void
lvm_set_type(lvm_instance_t *p, node_type_t type)
{
*(node_type_t *)(p->code + p->end) = type;
p->end += sizeof(type);
}
lvm_status_t
lvm_execute(lvm_instance_t *p)
{
node_type_t type;
operator_t *operator;
lvm_status_t status;
p->ip = 0;
status = EXECUTION_ERROR;
type = get_type(p);
switch(type) {
case LVM_CMP_OP:
operator = get_operator(p);
status = eval_logic(p, operator);
if(!LVM_ERROR(status)) {
PRINTF("The statement is %s\n", status == TRUE ? "true" : "false");
} else {
PRINTF("Execution error: %d\n", (int)status);
}
break;
default:
PRINTF("Error: The code must start with a relational operator\n");
}
return status;
}
void
lvm_set_op(lvm_instance_t *p, operator_t op)
{
lvm_set_type(p, LVM_ARITH_OP);
memcpy(&p->code[p->end], &op, sizeof(op));
p->end += sizeof(op);
}
void
lvm_set_relation(lvm_instance_t *p, operator_t op)
{
lvm_set_type(p, LVM_CMP_OP);
memcpy(&p->code[p->end], &op, sizeof(op));
p->end += sizeof(op);
}
void
lvm_set_operand(lvm_instance_t *p, operand_t *op)
{
lvm_set_type(p, LVM_OPERAND);
memcpy(&p->code[p->end], op, sizeof(*op));
p->end += sizeof(*op);
}
void
lvm_set_long(lvm_instance_t *p, long l)
{
operand_t op;
op.type = LVM_LONG;
op.value.l = l;
lvm_set_operand(p, &op);
}
lvm_status_t
lvm_register_variable(char *name, operand_type_t type)
{
variable_id_t id;
variable_t *var;
id = lookup(name);
if(id == LVM_MAX_VARIABLE_ID) {
return VARIABLE_LIMIT_REACHED;
}
var = &variables[id];
if(var->name[0] == '\0') {
strncpy(var->name, name, sizeof(var->name) - 1);
var->name[sizeof(var->name) - 1] = '\0';
var->type = type;
}
return TRUE;
}
lvm_status_t
lvm_set_variable_value(char *name, operand_value_t value)
{
variable_id_t id;
id = lookup(name);
if(id == LVM_MAX_VARIABLE_ID) {
return INVALID_IDENTIFIER;
}
variables[id].value = value;
return TRUE;
}
void
lvm_set_variable(lvm_instance_t *p, char *name)
{
operand_t op;
variable_id_t id;
id = lookup(name);
if(id < LVM_MAX_VARIABLE_ID) {
PRINTF("var id = %d\n", id);
op.type = LVM_VARIABLE;
op.value.id = id;
lvm_set_operand(p, &op);
}
}
void
lvm_clone(lvm_instance_t *dst, lvm_instance_t *src)
{
memcpy(dst, src, sizeof(*dst));
}
static void
create_intersection(derivation_t *result, derivation_t *d1, derivation_t *d2)
{
int i;
for(i = 0; i < LVM_MAX_VARIABLE_ID; i++) {
if(!d1[i].derived && !d2[i].derived) {
continue;
} else if(d1[i].derived && !d2[i].derived) {
result[i].min.l = d1[i].min.l;
result[i].max.l = d1[i].max.l;
} else if(!d1[i].derived && d2[i].derived) {
result[i].min.l = d2[i].min.l;
result[i].max.l = d2[i].max.l;
} else {
/* Both derivations have been made; create an
intersection of the ranges. */
if(d1[i].min.l > d2[i].min.l) {
result[i].min.l = d1[i].min.l;
} else {
result[i].min.l = d2[i].min.l;
}
if(d1[i].max.l < d2[i].max.l) {
result[i].max.l = d1[i].max.l;
} else {
result[i].max.l = d2[i].max.l;
}
}
result[i].derived = 1;
}
#if DEBUG
PRINTF("Created an intersection of D1 and D2\n");
PRINTF("D1: \n");
print_derivations(d1);
PRINTF("D2: \n");
print_derivations(d2);
PRINTF("Result: \n");
print_derivations(result);
#endif /* DEBUG */
}
static void
create_union(derivation_t *result, derivation_t *d1, derivation_t *d2)
{
int i;
for(i = 0; i < LVM_MAX_VARIABLE_ID; i++) {
if(!d1[i].derived && !d2[i].derived) {
continue;
} else if(d1[i].derived && !d2[i].derived) {
result[i].min.l = d1[i].min.l;
result[i].max.l = d1[i].max.l;
} else if(!d1[i].derived && d2[i].derived) {
result[i].min.l = d2[i].min.l;
result[i].max.l = d2[i].max.l;
} else {
/* Both derivations have been made; create a
union of the ranges. */
if(d1[i].min.l > d2[i].min.l) {
result[i].min.l = d2[i].min.l;
} else {
result[i].min.l = d1[i].min.l;
}
if(d1[i].max.l < d2[i].max.l) {
result[i].max.l = d2[i].max.l;
} else {
result[i].max.l = d1[i].max.l;
}
}
result[i].derived = 1;
}
#if DEBUG
PRINTF("Created a union of D1 and D2\n");
PRINTF("D1: \n");
print_derivations(d1);
PRINTF("D2: \n");
print_derivations(d2);
PRINTF("Result: \n");
print_derivations(result);
#endif /* DEBUG */
}
static int
derive_relation(lvm_instance_t *p, derivation_t *local_derivations)
{
operator_t *operator;
node_type_t type;
operand_t operand[2];
int i;
int variable_id;
operand_value_t *value;
derivation_t *derivation;
type = get_type(p);
operator = get_operator(p);
if(IS_CONNECTIVE(*operator)) {
derivation_t d1[LVM_MAX_VARIABLE_ID];
derivation_t d2[LVM_MAX_VARIABLE_ID];
if(*operator != LVM_AND && *operator != LVM_OR) {
return DERIVATION_ERROR;
}
PRINTF("Attempting to infer ranges from a logical connective\n");
memset(d1, 0, sizeof(d1));
memset(d2, 0, sizeof(d2));
if(LVM_ERROR(derive_relation(p, d1)) ||
LVM_ERROR(derive_relation(p, d2))) {
return DERIVATION_ERROR;
}
if(*operator == LVM_AND) {
create_intersection(local_derivations, d1, d2);
} else if(*operator == LVM_OR) {
create_union(local_derivations, d1, d2);
}
return TRUE;
}
for(i = 0; i < 2; i++) {
type = get_type(p);
switch(type) {
case LVM_OPERAND:
get_operand(p, &operand[i]);
break;
default:
return DERIVATION_ERROR;
}
}
if(operand[0].type == LVM_VARIABLE && operand[1].type == LVM_VARIABLE) {
return DERIVATION_ERROR;
}
/* Determine which of the operands that is the variable. */
if(operand[0].type == LVM_VARIABLE) {
if(operand[1].type == LVM_VARIABLE) {
return DERIVATION_ERROR;
}
variable_id = operand[0].value.id;
value = &operand[1].value;
} else {
variable_id = operand[1].value.id;
value = &operand[0].value;
}
if(variable_id >= LVM_MAX_VARIABLE_ID) {
return DERIVATION_ERROR;
}
PRINTF("variable id %d, value %ld\n", variable_id, *(long *)value);
derivation = local_derivations + variable_id;
/* Default values. */
derivation->max.l = LONG_MAX;
derivation->min.l = LONG_MIN;
switch(*operator) {
case LVM_EQ:
derivation->max = *value;
derivation->min = *value;
break;
case LVM_GE:
derivation->min.l = value->l + 1;
break;
case LVM_GEQ:
derivation->min.l = value->l;
break;
case LVM_LE:
derivation->max.l = value->l - 1;
break;
case LVM_LEQ:
derivation->max.l = value->l;
break;
default:
return DERIVATION_ERROR;
}
derivation->derived = 1;
return TRUE;
}
lvm_status_t
lvm_derive(lvm_instance_t *p)
{
return derive_relation(p, derivations);
}
lvm_status_t
lvm_get_derived_range(lvm_instance_t *p, char *name,
operand_value_t *min, operand_value_t *max)
{
int i;
for(i = 0; i < LVM_MAX_VARIABLE_ID; i++) {
if(strcmp(name, variables[i].name) == 0) {
if(derivations[i].derived) {
*min = derivations[i].min;
*max = derivations[i].max;
return TRUE;
}
return DERIVATION_ERROR;
}
}
return INVALID_IDENTIFIER;
}
#if DEBUG
static lvm_ip_t
print_operator(lvm_instance_t *p, lvm_ip_t index)
{
operator_t operator;
struct operator_map {
operator_t op;
char *representation;
};
struct operator_map operator_map[] = {
{LVM_ADD, "+"},
{LVM_SUB, "-"},
{LVM_MUL, "*"},
{LVM_DIV, "/"},
{LVM_GE, ">"},
{LVM_GEQ, ">="},
{LVM_LE, "<"},
{LVM_LEQ, "<="},
{LVM_EQ, "="},
{LVM_NEQ, "<>"},
{LVM_AND, "/\\"},
{LVM_OR, "\\/"},
{LVM_NOT, "!"}
};
int i;
memcpy(&operator, p->code + index, sizeof(operator));
for(i = 0; i < sizeof(operator_map) / sizeof(operator_map[0]); i++) {
if(operator_map[i].op == operator) {
PRINTF("%s ", operator_map[i].representation);
break;
}
}
return index + sizeof(operator_t);
}
static lvm_ip_t
print_operand(lvm_instance_t *p, lvm_ip_t index)
{
operand_t operand;
memcpy(&operand, p->code + index, sizeof(operand));
switch(operand.type) {
case LVM_VARIABLE:
if(operand.value.id >= LVM_MAX_VARIABLE_ID || variables[operand.value.id].name == NULL) {
PRINTF("var(id:%d):?? ", operand.value.id);
} else {
PRINTF("var(%s):%ld ", variables[operand.value.id].name,
variables[operand.value.id].value.l);
}
break;
case LVM_LONG:
PRINTF("long:%ld ", operand.value.l);
break;
default:
PRINTF("?? ");
break;
}
return index + sizeof(operand_t);
}
static lvm_ip_t
print_relation(lvm_instance_t *p, lvm_ip_t index)
{
/* Relational operators are stored as ordinary operators. */
return print_operator(p, index);
}
#endif /* DEBUG */
void
lvm_print_code(lvm_instance_t *p)
{
#if DEBUG
lvm_ip_t ip;
PRINTF("Code: ");
for(ip = 0; ip < p->end;) {
switch(*(node_type_t *)(p->code + ip)) {
case LVM_CMP_OP:
ip = print_relation(p, ip + sizeof(node_type_t));
break;
case LVM_ARITH_OP:
ip = print_operator(p, ip + sizeof(node_type_t));
break;
case LVM_OPERAND:
ip = print_operand(p, ip + sizeof(node_type_t));
break;
default:
PRINTF("Invalid opcode: 0x%x ", p->code[ip]);
ip = p->end;
break;
}
}
putchar('\n');
#endif
}
void
lvm_print_derivations(lvm_instance_t *p)
{
#if DEBUG
print_derivations(derivations);
#endif /* DEBUG */
}
#ifdef TEST
int
main(void)
{
lvm_instance_t p;
unsigned char code[256];
lvm_reset(&p, code, sizeof(code));
lvm_register_variable("z", LVM_LONG);
lvm_set_variable_value("z", (operand_value_t)15L);
lvm_register_variable("y", LVM_LONG);
lvm_set_variable_value("y", (operand_value_t)109L);
/* Infix: 109 = y /\ 20 > 70 - (6 + z * 3) => 109 = 109 /\ 20 > 19 => true */
lvm_set_relation(&p, LVM_AND);
lvm_set_relation(&p, LVM_EQ);
lvm_set_long(&p, 109);
lvm_set_variable(&p, "y");
lvm_set_relation(&p, LVM_GE);
lvm_set_long(&p, 20);
lvm_set_op(&p, LVM_SUB);
lvm_set_long(&p, 70);
lvm_set_op(&p, LVM_ADD);
lvm_set_long(&p, 6);
lvm_set_op(&p, LVM_MUL);
lvm_set_variable(&p, "z");
lvm_set_long(&p, 3);
lvm_print_code(&p);
lvm_execute(&p);
/* Infix: !(9999 + 1 < -1 + 10001) => !(10000 < 10000) => true */
lvm_reset(&p, code, sizeof(code));
lvm_set_relation(&p, LVM_NOT);
lvm_set_relation(&p, LVM_LE);
lvm_set_op(&p, LVM_ADD);
lvm_set_long(&p, 9999);
lvm_set_long(&p, 1);
lvm_set_op(&p, LVM_ADD);
lvm_set_long(&p, -1);
lvm_set_long(&p, 10001);
lvm_print_code(&p);
lvm_execute(&p);
/* Derivation tests */
/* Infix: a = 5 => a:(5,5) */
lvm_reset(&p, code, sizeof(code));
lvm_register_variable("a", LVM_LONG);
lvm_set_relation(&p, LVM_EQ);
lvm_set_variable(&p, "a");
lvm_set_long(&p, 5);
lvm_derive(&p);
lvm_print_derivations(&p);
/* Infix: a < 10 => a:(-oo,9) */
lvm_reset(&p, code, sizeof(code));
lvm_register_variable("a", LVM_LONG);
lvm_set_relation(&p, LVM_LE);
lvm_set_variable(&p, "a");
lvm_set_long(&p, 10);
lvm_derive(&p);
lvm_print_derivations(&p);
/* Infix: a < 100 /\ 10 < a => a:(11,99) */
lvm_reset(&p, code, sizeof(code));
lvm_register_variable("a", LVM_LONG);
lvm_set_relation(&p, LVM_AND);
lvm_set_relation(&p, LVM_LE);
lvm_set_variable(&p, "a");
lvm_set_long(&p, 100);
lvm_set_relation(&p, LVM_GE);
lvm_set_long(&p, 10);
lvm_set_variable(&p, "a");
lvm_derive(&p);
lvm_print_derivations(&p);
/* Infix: a < 100 /\ b > 100 => a:(-oo,99), b:(101,oo) */
lvm_reset(&p, code, sizeof(code));
lvm_register_variable("a", LVM_LONG);
lvm_register_variable("b", LVM_LONG);
lvm_set_relation(&p, LVM_AND);
lvm_set_relation(&p, LVM_LE);
lvm_set_variable(&p, "a");
lvm_set_long(&p, 100);
lvm_set_relation(&p, LVM_GE);
lvm_set_variable(&p, "b");
lvm_set_long(&p, 100);
lvm_derive(&p);
lvm_print_derivations(&p);
/* Infix: a < 100 \/ a < 1000 \/ a < 1902 => a:(-oo,1901) */
lvm_reset(&p, code, sizeof(code));
lvm_register_variable("a", LVM_LONG);
lvm_set_relation(&p, LVM_OR);
lvm_set_relation(&p, LVM_LE);
lvm_set_variable(&p, "a");
lvm_set_long(&p, 100);
lvm_set_relation(&p, LVM_OR);
lvm_set_relation(&p, LVM_LE);
lvm_set_long(&p, 1000);
lvm_set_variable(&p, "a");
lvm_set_relation(&p, LVM_LE);
lvm_set_variable(&p, "a");
lvm_set_long(&p, 1902);
lvm_derive(&p);
lvm_print_derivations(&p);
/* Infix: (a < 100 /\ a < 90 /\ a > 80 /\ a < 105) \/ b > 10000 =>
a:(81,89), b:(10001:oo) */
lvm_reset(&p, code, sizeof(code));
lvm_register_variable("a", LVM_LONG);
lvm_register_variable("b", LVM_LONG);
lvm_set_relation(&p, LVM_OR);
lvm_set_relation(&p, LVM_GE);
lvm_set_variable(&p, "b");
lvm_set_long(&p, 10000);
lvm_set_relation(&p, LVM_AND);
lvm_set_relation(&p, LVM_LE);
lvm_set_variable(&p, "a");
lvm_set_long(&p, 100);
lvm_set_relation(&p, LVM_AND);
lvm_set_relation(&p, LVM_LE);
lvm_set_variable(&p, "a");
lvm_set_long(&p, 90);
lvm_set_relation(&p, LVM_AND);
lvm_set_relation(&p, LVM_GE);
lvm_set_variable(&p, "a");
lvm_set_long(&p, 80);
lvm_set_relation(&p, LVM_LE);
lvm_set_variable(&p, "a");
lvm_set_long(&p, 105);
lvm_derive(&p);
lvm_print_derivations(&p);
printf("Done\n");
return 0;
}
#endif

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