├── LICENSE ├── Makefile ├── README.md ├── imgs ├── drawing.svg ├── nodeMCU-logo.png ├── nodemcu-esp32.png ├── nodemcu-esp8266.png └── nodemcu-linux.png ├── init.lua ├── misc ├── Makefile ├── banner.bw.txt └── banner.co.txt ├── modules ├── base │ └── base.lua ├── file │ └── file.lua ├── gpio │ └── gpio.lua ├── i2c │ └── i2c.lua ├── net │ ├── net-node.lua │ └── net.lua ├── node │ └── node.lua ├── rtctime │ └── rtctime.lua ├── sjson │ └── sjson.lua ├── tmr │ ├── tmr-node.lua │ └── tmr.lua └── uart │ └── uart.lua ├── nodemcu ├── startup.lua └── tests ├── Makefile ├── file01.lua ├── file02.lua ├── file03.lua ├── file04.lua ├── file05.lua ├── file06.lua ├── file07.lua └── test.txt /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 Rene K. Mueller 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | NAME="NodeMCU-Linux" 2 | DATE=`date +%F` 3 | DEBIAN=$(shell grep -qi 'id\s*=\s*raspbian' /etc/os-release && echo "raspbian" || echo "pure") 4 | LUANODE=https://github.com/ignacio/LuaNode # -- original 5 | LUANODE=https://github.com/Spiritdude/LuaNode # -- surely works with small patch 6 | 7 | all:: 8 | @echo "make requirements install deinstall backup" 9 | 10 | requirements:: lua luanode lua_modules luaffi 11 | 12 | lua:: 13 | sudo apt -y install luajit lua5.1 luarocks lua5.1-dev 14 | 15 | libssl:: 16 | ifeq (${DEBIAN},raspbian) 17 | sudo apt -y install libssl1.0-dev 18 | else 19 | sudo apt -y install libssl-dev 20 | endif 21 | 22 | luanode: libssl 23 | sudo apt -y install cmake libboost-dev libboost-system-dev libboost-date-time-dev libboost-thread-dev 24 | rm -rf LuaNode 25 | git clone ${LUANODE} 26 | cd LuaNode/build; cmake ..; make 27 | sudo cp LuaNode/build/luanode /usr/bin/ 28 | rm -rf LuaNode 29 | 30 | lua_modules:: 31 | sudo luarocks install luafilesystem 32 | sudo luarocks install lua-periphery 33 | sudo luarocks install luasocket 34 | sudo luarocks install luabitop 35 | sudo luarocks install lua-struct 36 | sudo luarocks install lunajson 37 | sudo luarocks install luaunit 38 | 39 | luaffi:: 40 | sudo rm -rf luaffifb 41 | git clone https://github.com/facebook/luaffifb 42 | cd luaffifb; sudo luarocks make 43 | sudo rm -rf luaffifb 44 | 45 | install:: 46 | install nodemcu /usr/local/bin/nodemcu 47 | mkdir -p /usr/local/lib/nodemcu 48 | tar cf - modules misc | (cd /usr/local/lib/nodemcu/ && tar xf -) 49 | 50 | deinstall:: 51 | sudo rm -rf /usr/local/bin/nodemcu /usr/local/lib/nodemcu/ 52 | sudo rm -f /usr/bin/luanode 53 | 54 | # -- developer only 55 | 56 | backup:: 57 | cd ..; tar cfz ${NAME}-${DATE}.tar.gz '--exclude=fw/*' ${NAME}; scp ${NAME}-${DATE}.tar.gz backup:Backup/; mv ${NAME}-${DATE}.tar.gz ~/Backup/; 58 | 59 | dist:: 60 | cd ..; rsync -avP ${NAME} "--exclude=fw/*" "--exclude=LuaNode/*" opl1:Projects 61 | cd ..; rsync -avP ${NAME} "--exclude=fw/*" "--exclude=LuaNode/*" npn1:Projects 62 | cd ..; rsync -avP ${NAME} "--exclude=fw/*" "--exclude=LuaNode/*" opz1:Projects 63 | 64 | edit:: 65 | dee4 nodemcu *.lua modules/*/*.lua Makefile README.md 66 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NodeMCU/Linux 2 | 3 | ## Introduction 4 | 5 | 6 | 7 | **NodeMCU/Linux** aims to implement the NodeMCU API as known by [NodeMCU/ESP8266](https://github.com/nodemcu/nodemcu-firmware) or "NodeMCU firmware", to additionally support 8 | 9 | - **Raspberry Pi** (EUR 35) and **RPi Zero** (EUR 5-10) running [Raspbian](https://www.raspberrypi.org/downloads/raspbian/) 10 | - **NanoPi series**, like **NanoPi Neo** (EUR 8-30) with Allwinner H3 running [Armbian](https://armbian.org) 11 | - **Orange Pi**, like **Orange Pi Zero**, **Orange Pi Lite** (EUR 6-30) series with Allwinner H2+ & H3 running [Armbian](https://armbian.org) 12 | - essentially any device which runs a Debian-based Linux distro, and preferably with GPIO, I2C, SPI facility 13 | 14 | The idea is to implement majority of the base modules in Lua itself, with few hooks with FFI (either `luajit` or `luaffifb` module). 15 | 16 | ## Current State 17 | 18 | ### Modules 19 | Check out the [NodeMCU/Linux Wiki](https://github.com/Spiritdude/nodemcu-linux/wiki) with current state of the base modules, an incomplete summary: 20 | - `node`: mostly implemented 21 | - `tmr`: mostly implemented but not much tested 22 | - `file`: basic operations implemented but mostly untested 23 | - `net`: partially implemented but not much tested 24 | - `rtctime`: partially implemented 25 | - `gpio`: partially implemented but entirely untested 26 | - `i2c`: code skeleton, far away to be functional 27 | - `bit`: built-in 28 | - `math`: built-in 29 | - `sjson`: built-in with `lunajson` 30 | 31 | running with Lua 5.1 with the [LuaNode](https://github.com/ignacio/LuaNode) (`luanode`) extension with `ffi` support. 32 | 33 | ### Boards 34 | Board | OS | Success 35 | --- | --- | --- 36 | NanoPi NEO | Armbian 5.38 | BUILDS, BOOTS, STARTUP 37 | Orange Pi Lite | Armbian 5.27 | BUILDS, BOOTS, STARTUP 38 | Orange Pi Zero | Armbian 5.27 | BUILDS, BOOTS, STARTUP 39 | Raspberry Pi Zero | Raspbian 9.1 | BUILDS, BOOTS, STARTUP 40 | 41 | ## Todo 42 | - implement **all** base modules **completely** and document it properly 43 | - thorough tests (automated) 44 | - `u8g2` which needs low-level I2C or SPI interface hardware-near implemented 45 | - include Lua modules which support various I2C/SPI devices 46 | - test example/tests with NodeMCU/8266 and NodeMCU/ESP32 as well 47 | 48 | ## Installation 49 | 50 | ``` 51 | % git clone https://github.com/Spiritdude/nodemcu-linux 52 | % cd nodemcu-linux 53 | % sudo make requirements 54 | % sudo make install 55 | ``` 56 | 57 | ## Usage 58 | 59 | The main executable is `nodemcu`, when executed it "boots" NodeMCU/Linux and executes `init.lua` if it resides in the same directory. 60 | 61 | An example with a NanoPi Neo: 62 | 63 | ``` 64 | % nodemcu 65 | NodeMCU/Linux 0.0.6 powered by Lua 5.1, Device ID: 4310175 / 0x41c49f 66 | armv7l (4 cores, 240-1200MHz) 67 | modules: node tmr file gpio i2c net rtctime sjson bit struct math 68 | cpu freq table [MHz]: 60, 120, 240, 312, 408, 480, 504, 528, 576, 600, 624, 648, 672, 720, 768, 816, 864, 912, 960, 1010, 1060, 1100, 1150, 1200, 1250, 1300, 1340, 1440, 1540 69 | > 70 | ``` 71 | 72 | The `>` is the prompt of the Lua console - abort with CTRL-C. 73 | 74 | ``` 75 | % nodemcu --help 76 | NodeMCU/Linux 0.0.7 USAGE: nodemcu {[options] .. } {[file1] .. } 77 | options: 78 | -v or -vv increase verbosity 79 | --verbose= define verbosity n = 0..10 80 | -h or --help print this usage help 81 | -s or --silent silent 82 | -e or --execute execute rest of arguments as code 83 | --version display version and exit 84 | --package.path=

define or add package path, use '+' to add additional path 85 | 86 | examples: 87 | nodemcu boot and execute init.lua and enter Lua console 88 | nodemcu --version 89 | nodemcu --help 90 | nodemcu -vvv 91 | nodemcu --verbose=3 92 | nodemcu test.lua boot and execute test.lua and exit 93 | nodemcu -e 'table.foreach(_sysinfo,print)' 94 | nodemcu --package.path=+./ 95 | 96 | % nodemcu -v 97 | I [0.000] loading modules ('node' and 'tmr' already loaded) 98 | I [0.001] dofile /usr/local/lib/nodemcu/modules/file/file.lua 99 | I [0.004] dofile /usr/local/lib/nodemcu/modules/gpio/gpio.lua 100 | I [0.007] dofile /usr/local/lib/nodemcu/modules/i2c/i2c.lua 101 | I [0.009] i2c: 3 interface(s) found: /dev/i2c-0 /dev/i2c-1 /dev/i2c-2 102 | I [0.009] dofile /usr/local/lib/nodemcu/modules/net/net-node.lua 103 | I [0.024] dofile /usr/local/lib/nodemcu/modules/rtctime/rtctime.lua 104 | I [0.025] dofile /usr/local/lib/nodemcu/modules/sjson/sjson.lua 105 | I [0.042] modules bit, struct built-in added 106 | I [0.043] module math added 107 | NodeMCU/Linux 0.0.7 powered by Lua 5.1, Device ID: 4310175 / 0x41c49f 108 | armv7l (4 cores, 240-1200MHz) 109 | modules: node tmr file gpio i2c net rtctime sjson bit struct math 110 | cpu freq table [MHz]: 60, 120, 240, 312, 408, 480, 504, 528, 576, 600, 624, 648, 672, 720, 768, 816, 864, 912, 960, 1010, 1060, 1100, 1150, 1200, 1250, 1300, 1340, 1440, 1540 111 | > 112 | ``` 113 | 114 | Within the `nodemcu-linux/` directory you cloned resides a default `init.lua` which executes `startup.lua` which performs some basic tests of various modules: 115 | 116 | ``` 117 | % nodemcu 118 | NodeMCU/Linux 0.0.6 powered by Lua 5.1, Device ID: 4310175 / 0x41c49f 119 | armv7l (4 cores, 240-1200MHz) 120 | modules: node tmr file gpio i2c net rtctime sjson bit struct math 121 | cpu freq table [MHz]: 60, 120, 240, 312, 408, 480, 504, 528, 576, 600, 624, 648, 672, 720, 768, 816, 864, 912, 960, 1010, 1060, 1100, 1150, 1200, 1250, 1300, 1340, 1440, 1540 122 | starting up... 123 | tmr.now() 67997 124 | tmr.time() 0 125 | tmr.uptime() 0.068331003189087 126 | rtctime 2018/03/09 15:57:48 UTC 127 | node.chipid() 4310175 0x41c49f 128 | node.flashid() 9a463503-3ec8-4cb9-aa50-aaaeae3a9e97 129 | node.heap() 150806528 148724KiB 130 | file.list() init.lua(64) misc(4096) nodemcu(6818) LICENSE(1082) .git(4096) ..(4096) fw(4096) imgs(4096) README.md(5334) modules(4096) startup.lua(3169) tests(4096) LuaNode(4096) Makefile(1571) examples(4096) .(4096) 131 | file.stat() with json {"time":{"min":37,"wday":5,"day":9,"yday":67,"year":2018,"sec":55,"hour":15,"mon":3},"is_arch":false,"name":"README.md","is_sys":false,"is_rdonly":false,"is_hidden":false,"is_dir":false,"size":5334} 132 | file.fsinfo() remain 21506.816MiB, used 7921.750MiB, total 29744.812MiB 133 | net-test: connecting to httpbin.org 134 | net-test: basic http server started on port 10080 135 | > net-test: http-received: 136 | | HTTP/1.1 200 OK 137 | | Connection: close 138 | ... 139 | .. 140 | 141 | % cd tests 142 | % nodemcu file01.lua 143 | ``` 144 | 145 | ## Detailed Development 146 | 147 | See my [Spiritude's Public Notebook: NodeMCU Shell Development](https://spiritdude.wordpress.com/2018/02/26/nodemcu-linux/) which I document more fine-grained state of the development with examples. 148 | 149 | 150 | René K. Müller
151 | February 2018 152 | -------------------------------------------------------------------------------- /imgs/drawing.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 21 | 23 | 26 | 30 | 31 | 34 | 38 | 39 | 42 | 46 | 47 | 56 | 65 | 74 | 83 | 92 | 101 | 110 | 119 | 128 | 137 | 146 | 155 | 164 | 173 | 182 | 191 | 200 | 209 | 218 | 227 | 236 | 245 | 254 | 263 | 272 | 281 | 290 | 299 | 308 | 317 | 326 | 335 | 344 | 353 | 362 | 371 | 380 | 389 | 398 | 407 | 416 | 425 | 434 | 443 | 452 | 461 | 470 | 479 | 488 | 497 | 506 | 515 | 524 | 533 | 543 | 544 | 563 | 565 | 566 | 568 | image/svg+xml 569 | 571 | 572 | 573 | 574 | 575 | 579 | 589 | 599 | 609 | 619 | 627 | 629 | 631 | 633 | 635 | 637 | 639 | 641 | 643 | 645 | 647 | 649 | 651 | 653 | 655 | 657 | 659 | 661 | 663 | 665 | 667 | 669 | 671 | 673 | 675 | 677 | 679 | 681 | 683 | 685 | 687 | 689 | 691 | 693 | 695 | 697 | 699 | 701 | 703 | 705 | 707 | 709 | 711 | 713 | 715 | 717 | 719 | 721 | 723 | 725 | 730 | 731 | 732 | 733 | 734 | 735 | 736 | 737 | 738 | 739 | 740 | 741 | 742 | 743 | 744 | 745 | 746 | 747 | 748 | 749 | 750 | 751 | 752 | 753 | 754 | 755 | 756 | 757 | 758 | 759 | 760 | 761 | 762 | 763 | 764 | 765 | 766 | 767 | 768 | 769 | 770 | 771 | 772 | 773 | 774 | 775 | 776 | 777 | 778 | 779 | original pixel 790 | svg 801 | 804 | 806 | 808 | 810 | 812 | 814 | 816 | 818 | 820 | 822 | 824 | 826 | 828 | 830 | 832 | 834 | 836 | 838 | 840 | 842 | 844 | 846 | 848 | 850 | 852 | 854 | 856 | 858 | 860 | 862 | 864 | 866 | 868 | 870 | 872 | 874 | 876 | 878 | 880 | 882 | 884 | 886 | 888 | 890 | 892 | 894 | 896 | 898 | 900 | 905 | 906 | 907 | 908 | 909 | 910 | 911 | 912 | 913 | 914 | 915 | 916 | 917 | 918 | 919 | 920 | 921 | 922 | 923 | 924 | 925 | 926 | 927 | 928 | 929 | 930 | 931 | 932 | 933 | 934 | 935 | 936 | 937 | 938 | 939 | 940 | 941 | 942 | 943 | 944 | 945 | 946 | 947 | 948 | 949 | 950 | 951 | 952 | 953 | 954 | ESP8266 966 | ESP32 978 | 981 | 983 | 985 | 987 | 989 | 991 | 993 | 995 | 997 | 999 | 1001 | 1003 | 1005 | 1007 | 1009 | 1011 | 1013 | 1015 | 1017 | 1019 | 1021 | 1023 | 1025 | 1027 | 1029 | 1031 | 1033 | 1035 | 1037 | 1039 | 1041 | 1043 | 1045 | 1047 | 1049 | 1051 | 1053 | 1055 | 1057 | 1059 | 1061 | 1063 | 1065 | 1067 | 1069 | 1071 | 1073 | 1075 | 1077 | 1082 | 1083 | 1084 | 1085 | 1086 | 1087 | 1088 | 1089 | 1090 | 1091 | 1092 | 1093 | 1094 | 1095 | 1096 | 1097 | 1098 | 1099 | 1100 | 1101 | 1102 | 1103 | 1104 | 1105 | 1106 | 1107 | 1108 | 1109 | 1110 | 1111 | 1112 | 1113 | 1114 | 1115 | 1116 | 1117 | 1118 | 1119 | 1120 | 1121 | 1122 | 1123 | 1124 | 1125 | 1126 | 1127 | 1128 | 1129 | 1130 | 1131 | LINUX 1143 | 1147 | 1150 | 1153 | 1156 | 1159 | 1162 | 1165 | 1168 | 1171 | 1174 | 1177 | 1180 | 1183 | 1186 | 1189 | 1192 | 1195 | 1198 | 1201 | 1204 | 1207 | 1210 | 1213 | 1216 | 1219 | 1222 | 1225 | 1228 | 1231 | 1234 | 1237 | 1240 | 1243 | 1246 | 1249 | 1252 | 1255 | 1258 | 1261 | 1264 | 1267 | 1270 | 1273 | 1276 | 1279 | 1282 | 1285 | 1288 | 1291 | 1296 | 1297 | 1298 | 1299 | 1300 | 1301 | 1302 | 1303 | 1304 | 1305 | 1306 | 1307 | 1308 | 1309 | 1310 | 1311 | 1312 | 1313 | 1314 | 1315 | 1316 | 1317 | 1318 | 1319 | 1320 | 1321 | 1322 | 1323 | 1324 | 1325 | 1326 | 1327 | 1328 | 1329 | 1330 | 1331 | 1332 | 1333 | 1334 | 1335 | 1336 | 1337 | 1338 | 1339 | 1340 | 1341 | 1342 | 1343 | 1344 | 1345 | 1350 | 1352 | 1354 | 1356 | 1358 | 1360 | 1362 | 1364 | 1366 | 1368 | 1370 | 1372 | 1374 | 1376 | 1378 | 1380 | 1382 | 1384 | 1386 | 1388 | 1390 | 1392 | 1394 | 1396 | 1398 | 1400 | 1402 | 1404 | 1406 | 1408 | 1410 | 1412 | 1414 | 1416 | 1418 | 1420 | 1422 | 1424 | 1426 | 1428 | 1430 | 1432 | 1434 | 1436 | 1438 | 1440 | 1442 | 1444 | 1446 | 1451 | 1452 | 1453 | 1454 | 1455 | 1456 | 1457 | 1458 | 1459 | 1460 | 1461 | 1462 | 1463 | 1464 | 1465 | 1466 | 1467 | 1468 | 1469 | 1470 | 1471 | 1472 | 1473 | 1474 | 1475 | 1476 | 1477 | 1478 | 1479 | 1480 | 1481 | 1482 | 1483 | 1484 | 1485 | 1486 | 1487 | 1488 | 1489 | 1490 | 1491 | 1492 | 1493 | 1494 | 1495 | 1496 | 1497 | 1498 | 1499 | 1500 | ESP8266 1514 | 1519 | 1521 | 1523 | 1525 | 1527 | 1529 | 1531 | 1533 | 1535 | 1537 | 1539 | 1541 | 1543 | 1545 | 1547 | 1549 | 1551 | 1553 | 1555 | 1557 | 1559 | 1561 | 1563 | 1565 | 1567 | 1569 | 1571 | 1573 | 1575 | 1577 | 1579 | 1581 | 1583 | 1585 | 1587 | 1589 | 1591 | 1593 | 1595 | 1597 | 1599 | 1601 | 1603 | 1605 | 1607 | 1609 | 1611 | 1613 | 1615 | 1620 | 1621 | 1622 | 1623 | 1624 | 1625 | 1626 | 1627 | 1628 | 1629 | 1630 | 1631 | 1632 | 1633 | 1634 | 1635 | 1636 | 1637 | 1638 | 1639 | 1640 | 1641 | 1642 | 1643 | 1644 | 1645 | 1646 | 1647 | 1648 | 1649 | 1650 | 1651 | 1652 | 1653 | 1654 | 1655 | 1656 | 1657 | 1658 | 1659 | 1660 | 1661 | 1662 | 1663 | 1664 | 1665 | 1666 | 1667 | 1668 | 1669 | 1675 | 1678 | 1681 | 1684 | 1687 | 1690 | 1693 | 1696 | 1699 | 1702 | 1705 | 1708 | 1711 | 1714 | 1717 | 1720 | 1723 | 1726 | 1729 | 1732 | 1735 | 1738 | 1741 | 1744 | 1747 | 1750 | 1753 | 1756 | 1759 | 1762 | 1765 | 1768 | 1771 | 1774 | 1777 | 1780 | 1783 | 1786 | 1789 | 1792 | 1795 | 1798 | 1801 | 1804 | 1807 | 1810 | 1813 | 1816 | 1819 | 1824 | 1825 | 1826 | 1827 | 1828 | 1829 | 1830 | 1831 | 1832 | 1833 | 1834 | 1835 | 1836 | 1837 | 1838 | 1839 | 1840 | 1841 | 1842 | 1843 | 1844 | 1845 | 1846 | 1847 | 1848 | 1849 | 1850 | 1851 | 1852 | 1853 | 1854 | 1855 | 1856 | 1857 | 1858 | 1859 | 1860 | 1861 | 1862 | 1863 | 1864 | 1865 | 1866 | 1867 | 1868 | 1869 | 1870 | 1871 | 1872 | 1873 | 1883 | ESP32 1897 | 1907 | LINUX 1921 | 1922 | 1923 | -------------------------------------------------------------------------------- /imgs/nodeMCU-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Spiritdude/nodemcu-linux/e4fd1ce33bf45ed6118cb56dfb0fec9219b35b70/imgs/nodeMCU-logo.png -------------------------------------------------------------------------------- /imgs/nodemcu-esp32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Spiritdude/nodemcu-linux/e4fd1ce33bf45ed6118cb56dfb0fec9219b35b70/imgs/nodemcu-esp32.png -------------------------------------------------------------------------------- /imgs/nodemcu-esp8266.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Spiritdude/nodemcu-linux/e4fd1ce33bf45ed6118cb56dfb0fec9219b35b70/imgs/nodemcu-esp8266.png -------------------------------------------------------------------------------- /imgs/nodemcu-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Spiritdude/nodemcu-linux/e4fd1ce33bf45ed6118cb56dfb0fec9219b35b70/imgs/nodemcu-linux.png -------------------------------------------------------------------------------- /init.lua: -------------------------------------------------------------------------------- 1 | if file.exists("startup.lua") then 2 | dofile("startup.lua") 3 | end 4 | -------------------------------------------------------------------------------- /misc/Makefile: -------------------------------------------------------------------------------- 1 | BANNER=NodeMCU Linux 2 | 3 | all:: banner.bw.txt banner.co.txt 4 | 5 | banner.bw.txt:: 6 | toilet -f small "${BANNER}" > banner.bw.txt 7 | 8 | banner.co.txt:: 9 | toilet -f small --filter metal "${BANNER}" > banner.co.txt 10 | 11 | 12 | -------------------------------------------------------------------------------- /misc/banner.bw.txt: -------------------------------------------------------------------------------- 1 | _ _ _ __ __ ___ _ _ _ ___ _ _ _ ___ __ 2 | | \| |___ __| |___| \/ |/ __| | | | | | |_ _| \| | | | \ \/ / 3 | | .` / _ \/ _` / -_) |\/| | (__| |_| | | |__ | || .` | |_| |> < 4 | |_|\_\___/\__,_\___|_| |_|\___|\___/ |____|___|_|\_|\___//_/\_\ 5 | 6 | -------------------------------------------------------------------------------- /misc/banner.co.txt: -------------------------------------------------------------------------------- 1 | _ _ _ __ __ ___ _ _ _ ___ _ _ _ ___ __ 2 | | \| |___ __| |___| \/ |/ __| | | | | | |_ _| \| | | | \ \/ / 3 | | .` / _ \/ _` / -_) |\/| | (__| |_| | | |__ | || .` | |_| |> < 4 | |_|\_\___/\__,_\___|_| |_|\___|\___/ |____|___|_|\_|\___//_/\_\ 5 | 6 | -------------------------------------------------------------------------------- /modules/base/base.lua: -------------------------------------------------------------------------------- 1 | -- == Base Module == 2 | -- 3 | -- Copyright (c) 2018 by Rene K. Mueller 4 | -- 5 | -- License: MIT (see LICENSE file) 6 | -- 7 | -- Description: 8 | -- Common NodeMCU functions (re-)defined. 9 | -- 10 | -- History: 11 | -- 2018/03/04: 0.0.2: slowly ramping up functionality: _tasker skeleton of internal scheduler of non-blocking tasks 12 | -- 2018/02/24: 0.0.1: first version: int(), dofile(), print() and basic syslog.* facility 13 | 14 | int = function(i) 15 | return math.floor(i) 16 | end 17 | 18 | -- internal tasker 19 | _tasker = { -- NOTE: not yet useable, needs to be tested 20 | ACTIVE = 0, -- state 21 | INACTIVE = 1, 22 | 23 | LOW = 0, -- prio 24 | MEDIUM = 1, 25 | HIGH = 2, 26 | 27 | _list = { }, -- list of tasks 28 | 29 | run = function() 30 | for p in pairs({_tasker.HIGH,_tasker.MEDIUM,_tasker.LOW}) do 31 | for i,t in pairs(_list) do 32 | local t = _list[i] 33 | if t.prio == p and t.state == _tasker.ACTIVE then 34 | coroutine.resume(t.cf) 35 | end 36 | end 37 | end 38 | end, 39 | new = function(f,p,opts) 40 | p = p or _tasker.MEDIUM 41 | table.insert(_list,{ 42 | prio = p, 43 | state = _tasker.ACTIVE, 44 | --cf = coroutine.create(function() local res = f() coroutine.yield(res) end) 45 | cf = coroutine.create(f) 46 | }) 47 | return #_list 48 | end, 49 | suspend = function(id) 50 | _tasker._list[id].state = _tasker.INACTIVE 51 | end, 52 | resume = function(id) 53 | _tasker._list[id].state = _tasker.ACTIVE 54 | end, 55 | suspend_all = function() 56 | for i,t in pairs(_list) do 57 | _tasker.suspend(i) 58 | end 59 | end, 60 | resume_all = function() 61 | for i,t in pairs(_list) do 62 | _tasker.resume(i) 63 | end 64 | end 65 | } 66 | 67 | _syslog = { -- internal syslog facility 68 | INFO = "INFO", 69 | WARN = "WARN", 70 | ERROR = "ERROR", 71 | FATAL = "FATAL", 72 | _verbose = 0, 73 | print = function(...) 74 | local t = {...} 75 | local i = table.remove(t,1) 76 | if _syslog._verbose == 0 and i == _syslog.INFO then 77 | return 78 | elseif _syslog._verbose <= 1 and i == _syslog.WARN then 79 | return 80 | else 81 | print(i:match("(%w)"),string.format("[%.3f]",tmr.uptime()),unpack(t)) 82 | end 83 | end, 84 | verbose = function(l) 85 | _syslog._verbose = tonumber(l) 86 | end 87 | } 88 | 89 | print = function(...) 90 | local o = "" 91 | for i,v in pairs({...}) do 92 | --io.write(": "..tostring(i)..tostring(v).."\n") 93 | o = o .. (i > 1 and " " or "") 94 | o = o .. tostring(v) 95 | end 96 | io.write(o.."\n") 97 | end 98 | 99 | 100 | -------------------------------------------------------------------------------- /modules/file/file.lua: -------------------------------------------------------------------------------- 1 | -- == File Module == 2 | -- Copyright (c) 2018 by Rene K. Mueller 3 | -- 4 | -- License: MIT (see LICENSE file) 5 | -- 6 | -- Description: 7 | -- basic file operations, alike io with some slight alterations like file.read() 8 | -- 9 | -- History: 10 | -- 2018/02/27: 0.0.3: fh:*() cleaner setup using file.*(self,[...]) and argument checking if file.read() or fh:read() is called 11 | -- 2018/02/27: 0.0.2: file.open(), file.read() and file.close() work, fh:read() and fh:*() do not yet, file.stat() and file.list() work 12 | -- 2018/02/21: 0.0.1: first version, mostly using io.open():xyz convention, but needs to be tested for proper compatibility 13 | 14 | local lfs = require("lfs") 15 | local os = require("os") 16 | 17 | file = { 18 | _VERSION = '0.0.3' 19 | } 20 | 21 | file.chdir = function(d) -- Change current directory (and drive). 22 | return lfs.chdir(d) 23 | end 24 | 25 | file.exists = function(fn) -- Determines whether the specified file exists. 26 | if true then 27 | local st = lfs.attributes(fn) -- better 28 | return st and true or false 29 | else 30 | local f = io.open(fn,"r") -- fallback 31 | if f then io.close(f) end 32 | return f and true or false 33 | end 34 | end 35 | 36 | file.format = function() -- Format the file system. 37 | _syslog.print(_syslog.ERROR,"file.format() not yet implemented") 38 | end 39 | 40 | file.fscfg = function() -- Returns the flash address and physical size of the file system area, in bytes. 41 | local path, total, used, avail, usep, mount = file.fsinfo() 42 | return 0, total * 1024 43 | end 44 | 45 | file.fsinfo = function() -- Return size information for the file system in kbytes 46 | local f = io.popen("df -k .") 47 | _ = f:read("*line") 48 | _ = f:read("*line") 49 | f:close() 50 | local path, total, used, avail, usep, mount = _:match("^(%S+)%s+(%d+)%s+(%d+)%s+(%d+)%s+(%S+)%s+(%S+)"); 51 | return avail, used, total 52 | end 53 | 54 | file.list = function(d) -- Lists all files in the file system. 55 | local fl = { } 56 | for f in lfs.dir(d or ".") do 57 | local st = lfs.attributes((d or ".").."/"..f) 58 | fl[f] = st.size or 0 59 | end 60 | return fl 61 | end 62 | 63 | file.mount = function() -- Mounts a FatFs volume on SD card. 64 | _syslog.print(_syslog.ERROR,"file.mount() not implemented yet") 65 | end 66 | 67 | file.on = function() -- Registers callback functions. 68 | _syslog.print(_syslog.ERROR,"file.on() not implemented yet") 69 | end 70 | 71 | file.open = function(fn,m) 72 | file._fh = io.open(fn,m) 73 | if file._fh then 74 | return { 75 | _fh = file._fh, 76 | read = file.read, 77 | readline = file.readline, 78 | write = file.write, 79 | writeline = file.writeline, 80 | flush = file.flush, 81 | close = file.close, 82 | seek = file.seek 83 | } 84 | end 85 | return nil 86 | end 87 | 88 | file.remove = function(fn) -- Remove a file from the file system. 89 | return os.remove(fn) 90 | end 91 | 92 | file.rename = function(fno,fnn) -- Renames a file. 93 | return os.rename(fno,fnn) 94 | end 95 | 96 | file.stat = function(fn) -- Get attribtues of a file or directory in a table. 97 | local st = lfs.attributes(fn) 98 | local s = { } 99 | if st then 100 | s.size = st.size or null 101 | s.name = fn 102 | s.is_dir = st.mode == 'directory' 103 | s.is_rdonly = false 104 | s.is_hidden = false 105 | s.is_sys = false 106 | s.is_arch = false 107 | s.time = rtctime.epoch2cal(st.modification) 108 | return s 109 | else 110 | return st 111 | end 112 | end 113 | 114 | -- Basic model: In the basic model there is max one file opened at a time. 115 | -- Object model: Files are represented by file objects which are created by file. 116 | 117 | file.close = function(self) 118 | (self and self._fh or file._fh):close() 119 | end 120 | 121 | file.flush = function(self) -- Flushes any pending writes to the file system, ensuring no data is lost on a restart. 122 | (self and self._fh or file._fh):flush() 123 | end 124 | 125 | file.read = function(self,sep) 126 | local fh 127 | if type(self)=='table' then -- called as f:read(sep) 128 | --print("fh:read",type(self),self,sep) 129 | fh = self._fh 130 | else -- called as file.read(sep) 131 | --print("file.read",type(self),self,sep) 132 | fh = file._fh 133 | sep = self 134 | end 135 | if not sep then 136 | return fh:read("*all") 137 | elseif type(sep)=='number' then 138 | return fh:read(sep) 139 | else 140 | local c 141 | local b 142 | repeat 143 | c = fh:read(1) 144 | if c then 145 | b = (b or "") .. (c or '') 146 | end 147 | until c == sep or not c 148 | return b 149 | end 150 | end 151 | 152 | file.readline = function(self) 153 | return (self._fh or file._fh):read("*line") 154 | end 155 | 156 | file.seek = function(...) -- Sets and gets the file position, measured from the beginning of the file, to the position given by offset plus a base specified by the string whence. 157 | local fh = type(arg[1])=='table' and table.remove(arg,1)._fh or file._fh 158 | local w, o = arg[1], arg[2] 159 | return fh:seek(w,o) 160 | end 161 | 162 | file.write = function(self,s) -- Write a string to the open file. 163 | local fh 164 | if type(self)=='table' then 165 | fh = self._fh 166 | else 167 | fh = file._fh 168 | s = self 169 | end 170 | return fh:write(s) 171 | end 172 | 173 | file.writeline = function(self,s) -- Write a string to the open file and append '\n' at the end. 174 | local fh 175 | if type(self)=='table' then 176 | fh = self._fh 177 | else 178 | fh = file._fh 179 | s = self 180 | end 181 | return fh:write(s.."\n") 182 | end 183 | 184 | table.insert(node.modules,'file') 185 | 186 | return file; 187 | -------------------------------------------------------------------------------- /modules/gpio/gpio.lua: -------------------------------------------------------------------------------- 1 | -- == GPIO Module == 2 | -- 3 | -- Copyright (c) 2018 by Rene K. Mueller 4 | -- 5 | -- License: MIT (see LICENSE file) 6 | -- 7 | -- Description: 8 | -- configure GPIO and write to or read from them 9 | -- 10 | -- History: 11 | -- 2018/02/24: 0.0.1: first version, gpio.mode(), read() and write() implemented but untested 12 | 13 | local GPIO = require('periphery').GPIO 14 | 15 | gpio = { 16 | _pin = { }, 17 | OUTPUT = "out", 18 | INPUT = "in", 19 | OPENDRAIN = nil, 20 | INT = 4, 21 | 22 | FLOAT = 0, 23 | PULLUP = 1 24 | } 25 | 26 | gpio.mode = function(pin, mode, pullup) -- Initialize pin to GPIO mode, set the pin in/out direction, and optional internal weak pull-up. 27 | if mode then 28 | _pin[pin] = GPIO(pin,mode) 29 | else 30 | _pin[pin] = GPIO(pin) 31 | end 32 | end 33 | 34 | gpio.config = function(tb) 35 | _syslog.print(_syslog.ERROR,"gpio.config(): not yet implemented") 36 | -- gpio.config({ 37 | -- gpio=x || {x, y, z}, 38 | -- dir=gpio.IN || gpio.OUT || gpio.IN_OUT, 39 | -- opendrain= 0 || 1 -- only applicable to output modes 40 | -- pull= gpio.FLOATING || gpio.PULL_UP || gpio.PULL_DOWN || gpio.PULL_UP_DOWN 41 | --}, ...) 42 | end 43 | 44 | gpio.read = function(pin) -- Read digital GPIO pin value. 45 | if gpio._pin[pin] then 46 | return gpio._pin[pin]:read() 47 | else 48 | _syslog.print(_syslog.ERROR,"gpio.read(): pin "..pin.." not yet configured") 49 | end 50 | end 51 | 52 | gpio.write = function(pin,v) -- Set digital GPIO pin value. 53 | if gpio._pin[pin] then 54 | return gpio._pin[pin]:write(pin,v==1 or false) 55 | else 56 | _syslog.print(_syslog.ERROR,"gpio.write(): pin "..pin.." not yet configured") 57 | end 58 | end 59 | 60 | gpio.serout = function() -- Serialize output based on a sequence of delay-times in s. 61 | _syslog.print(_syslog.ERROR,"gpio.serout(): not yet implemented") 62 | end 63 | 64 | gpio.trig = function() -- Establish or clear a callback function to run on interrupt for a pin. 65 | _syslog.print(_syslog.ERROR,"gpio.trig(): not yet implemented") 66 | end 67 | 68 | table.insert(node.modules,'gpio') 69 | 70 | return gpio; 71 | 72 | -------------------------------------------------------------------------------- /modules/i2c/i2c.lua: -------------------------------------------------------------------------------- 1 | -- == I2C Module == 2 | -- 3 | -- Copyright (c) 2018 by Rene K. Mueller 4 | -- 5 | -- License: MIT (see LICENSE file) 6 | -- 7 | -- Description: 8 | -- 9 | -- History: 10 | -- 2018/02/26: 0.0.1: first version, no idea if it works, coded based on documentation 11 | 12 | local I2C = require('periphery').I2C 13 | 14 | i2c = { 15 | DEV_PREFIX = I2C_DEV_PREFIX or "/dev/i2c-", 16 | --HW0 = "/dev/i2c-0", -- these will be autoset 17 | --HW1 = "/dev/i2c-1", 18 | --... 19 | TRANSMITTER = 0, 20 | RECEIVER = 1 21 | } 22 | 23 | -- we probe the i2c devices and dynamically set i2c.HW[0..x] = i2c.DEV_PREFIX..d (e.g. i2c.HW0 = "/dev/i2c-0") 24 | local i = 0 25 | local o = "" 26 | for d=0,7 do 27 | if not file.exists(i2c.DEV_PREFIX..d) then 28 | break 29 | end 30 | o = o .. (i>0 and " " or "") .. i2c.DEV_PREFIX .. i 31 | i2c['HW'..i] = i2c.DEV_PREFIX .. i 32 | i = i+1 33 | i2c._count = i 34 | end 35 | if i>0 then 36 | _syslog.print(_syslog.INFO,"i2c: "..i.." interface(s) found: "..o) 37 | else 38 | _syslog.print(_syslog.INFO,"i2c: no interface found") 39 | end 40 | 41 | i2c.setup = function(id,sda,scl,sp) -- Initialize the IC module. 42 | i2c._i2c[id] = { } 43 | i2c._i2c[id].handle = I2C(id) 44 | _syslog.print(_syslog.INFO,"i2c "..id.." setup") 45 | -- sda and scl are ignored, we /dev/i2c-* is hardwired (for now) 46 | end 47 | 48 | i2c.address = function(id,adr,dir) -- Setup IC address and read/write mode for the next transfer. 49 | i2c._i2c[id].adr = adr 50 | i2c._i2c[id].dir = dir 51 | _syslog.print(_syslog.INFO,"i2c "..id.." address "..adr.." "..dir) 52 | end 53 | 54 | i2c.start = function(id) -- Send an IC start condition. 55 | -- ??? 56 | end 57 | 58 | i2c.stop = function(id) -- Send an IC stop condition. 59 | i2c._i2c[id].handle:transfer(id,{ 0x00, flags = I2C.I2C_M_STOP }) 60 | end 61 | 62 | i2c.read = function(id,len) -- Read data for variable number of bytes. 63 | local msg = { } 64 | for i=1, len do -- fill place holder(s) 65 | table.insert(msg,0x00) 66 | end 67 | table.insert(msg,'flags',I2C.I2C_M_RD); 68 | i2c._i2c[id].handle:write({ i2c._i2c[id].adr }, tbl) 69 | return msg 70 | end 71 | 72 | i2c.write = function(...) -- Write data to IC bus. 73 | local id = table.remove(arg,1) 74 | -- arg rest of the data 75 | table.foreach(arg,function(k,v) 76 | i2c._ih[id].handle:write({ i2c_i2c[id].adr }, v) 77 | end) 78 | end 79 | 80 | table.insert(node.modules,"i2c") 81 | 82 | return i2c 83 | 84 | -------------------------------------------------------------------------------- /modules/net/net-node.lua: -------------------------------------------------------------------------------- 1 | -- == Net Node Module == 2 | -- 3 | -- Copyright (c) 2018 by Rene K. Mueller 4 | -- 5 | -- License: MIT (see LICENSE file) 6 | -- 7 | -- Description: 8 | -- 9 | -- Notes: 10 | -- - see https://github.com/nodemcu/nodemcu-firmware/blob/master/app/modules/net.c as reference 11 | -- 12 | -- History: 13 | -- 2018/03/05: 0.0.1: first version, just a skeleton 14 | 15 | Net = require('luanode.net') 16 | 17 | net = { 18 | TCP = 0, 19 | UDP = 1 20 | } 21 | 22 | net.createConnection = function() -- Creates a client. 23 | return { 24 | _event = { }, 25 | connect = function(self,port,host) 26 | local me = self 27 | 28 | self._sck = Net.createConnection(port,host) 29 | 30 | self._sck.send = function(self,s) -- wrap c:write() to c:send() 31 | --print('dedicated send()',s) 32 | self:write(s) 33 | end 34 | 35 | self._sck:on("connect",function() 36 | --print("on:connect",self) 37 | _ = self._event.connection and self._event.connection(self._sck) or 0 38 | 39 | local me = self 40 | 41 | self._sck:on("data",function(self,data) 42 | --print("on:receive",self,data) 43 | _ = me._event.receive and me._event.receive(me._sck,data) or 0 44 | end) 45 | 46 | self._sck:on("close",function() 47 | --print("on:close") 48 | _ = me._event.close and me._event.close(me._sck) or 0 49 | end) 50 | 51 | self._sck:on("drain",function() 52 | _ = me._event.sent and me._event.sent(me._sck) or 0 53 | end) 54 | end) 55 | return self 56 | end, 57 | on = function(self,event,func) 58 | self._event[event] = func 59 | --print("==on:",event,func) 60 | end 61 | } 62 | end 63 | 64 | net.createServer = function() -- Creates a server. 65 | return { 66 | _event = { }, 67 | _srv = Net.createServer(function(self,conn) 68 | conn:setEncoding('binary') 69 | local me = self._me 70 | conn:addListener('connect',function(self) 71 | _ = me._event.connection and me._event.connection(conn) 72 | end) 73 | conn:addListener('data',function(self,chunk) 74 | _ = me._event.receive and me._event.receive(conn,chunk) 75 | end) 76 | conn:addListener('close',function(self) 77 | _ = me._event.close and me._event.close(conn) 78 | conn:finish() 79 | end) 80 | conn:addListener('drain',function(self,conn) 81 | _ = me._event.sent and me._event.sent(self) 82 | end) 83 | conn.on = function(self,event,func) 84 | me._event[event] = func 85 | end 86 | conn.send = function(self,s) 87 | self:write(s) 88 | end 89 | conn.close = function(self) 90 | self:finish() 91 | end 92 | me._func(conn) 93 | return me 94 | end), 95 | listen = function(self,port,func) 96 | self._port = port 97 | self._func = func 98 | self._srv._me = self -- point upward 99 | self._srv:listen(port) 100 | end, 101 | close = function(self) 102 | self._srv:close() -- untested 103 | end 104 | } 105 | end 106 | 107 | _ = [[ 108 | net.createUDPSocket = function() -- Creates an UDP socket. 109 | end 110 | 111 | net.multicastJoin = function() -- Join multicast group. 112 | end 113 | 114 | net.multicastLeave = function() -- Leave multicast group. 115 | end 116 | 117 | net.server = { } 118 | net.server:close = function() -- Closes the server. 119 | end 120 | 121 | net.server:listen = function() -- Listen on port from IP address. 122 | end 123 | 124 | net.server:getaddr = function() -- Returns server local address/port. 125 | end 126 | 127 | net.socket = { } 128 | net.socket:close = function() -- Closes socket. 129 | end 130 | 131 | net.socket:connect = function() -- Connect to a remote server. 132 | end 133 | 134 | net.socket:dns = function() -- Provides DNS resolution for a hostname. 135 | end 136 | 137 | net.socket:getpeer = function() -- Retrieve port and ip of remote peer. 138 | end 139 | 140 | net.socket:getaddr = function() -- Retrieve local port and ip of socket. 141 | end 142 | 143 | net.socket:hold = function() -- Throttle data reception by placing a request to block the TCP receive function. 144 | end 145 | 146 | net.socket:on = function() -- Register callback functions for specific events. 147 | end 148 | 149 | net.socket:send = function() -- Sends data to remote peer. 150 | end 151 | 152 | net.socket:ttl = function() -- Changes or retrieves Time-To-Live value on socket. 153 | end 154 | 155 | net.socket:unhold = function() -- Unblock TCP receiving data by revocation of a preceding hold(). 156 | end 157 | 158 | net.udpsocket = { } 159 | net.udpsocket:close = function() -- Closes UDP socket. 160 | end 161 | 162 | net.udpsocket:listen = function() -- Listen on port from IP address. 163 | end 164 | 165 | net.udpsocket:on = function() -- Register callback functions for specific events. 166 | end 167 | 168 | net.udpsocket:send = function() -- Sends data to specific remote peer. 169 | end 170 | 171 | net.udpsocket:dns = function() -- Provides DNS resolution for a hostname. 172 | end 173 | 174 | net.udpsocket:getaddr = function() -- Retrieve local port and ip of socket. 175 | end 176 | 177 | net.udpsocket:ttl = function() -- Changes or retrieves Time-To-Live value on socket. 178 | end 179 | 180 | net.dns.getdnsserver = function() -- Gets the IP address of the DNS server used to resolve hostnames. 181 | end 182 | 183 | net.dns.resolve = function() -- Resolve a hostname to an IP address. 184 | end 185 | 186 | net.dns.setdnsserver = function() -- Sets the IP of the DNS server used to resolve hostnames. 187 | end 188 | ]] 189 | 190 | socket = { 191 | new = function(port) 192 | self = { } 193 | self._socket = Socket.bind("*",port) 194 | return self 195 | end, 196 | connect = function(ip) 197 | end, 198 | on = function(event,conn) 199 | end, 200 | send = function(str) 201 | self._socket:send(str) 202 | end, 203 | close = function(str) 204 | self._socket:close() 205 | end 206 | } 207 | 208 | table.insert(node.modules,"net") 209 | 210 | return net 211 | 212 | -------------------------------------------------------------------------------- /modules/net/net.lua: -------------------------------------------------------------------------------- 1 | -- == Net Module == 2 | -- 3 | -- Copyright (c) 2018 by Rene K. Mueller 4 | -- 5 | -- License: MIT (see LICENSE file) 6 | -- 7 | -- Description: 8 | -- This is the non-functional skeleton for `socket`-based net module, it would require 9 | -- non-blocking/callback implementation - which LuaNode supports out of the box, see net-node.lua 10 | -- 11 | -- Notes: 12 | -- - see https://github.com/nodemcu/nodemcu-firmware/blob/master/app/modules/net.c as reference 13 | -- 14 | -- History: 15 | -- 2018/02/24: 0.0.1: first version, just a skeleton 16 | 17 | local Socket = require('socket') 18 | 19 | net = { 20 | } 21 | 22 | net.createConnection = function() -- Creates a client. 23 | end 24 | 25 | net.createServer = function() -- Creates a server. 26 | return { 27 | newClients = function() 28 | local c = self._server:accept() 29 | if c then 30 | c:timeout(1) 31 | self._server:on("connection",c) 32 | table.insert(self._clients,c) 33 | end 34 | end, 35 | 36 | listen = function(port,func) 37 | self._server = socket.new(port) 38 | self._server:timeout(0.1) 39 | self._port = port 40 | self._clients = { } 41 | self._sendClients = { } 42 | while true do 43 | self:newClients() 44 | local receivingClients, _, error = select(self._clients, nil, .01) 45 | for i, client in receivingClients do 46 | local data, error = client:receive() 47 | if error then 48 | _syslog.print(_syslog.ERROR,"net.listen(): "..tostring(error).." on client "..tostring(client)) 49 | table.remove(self._clients,i) 50 | else 51 | self._server:on("receive",data) 52 | --client.send() 53 | client:close() 54 | table.remove(self._clients,i) 55 | end 56 | end 57 | end 58 | return self 59 | end 60 | } 61 | end 62 | 63 | net.createUDPSocket = function() -- Creates an UDP socket. 64 | end 65 | 66 | net.multicastJoin = function() -- Join multicast group. 67 | end 68 | 69 | net.multicastLeave = function() -- Leave multicast group. 70 | end 71 | 72 | _ = [[ 73 | net.server = { } 74 | net.server:close = function() -- Closes the server. 75 | end 76 | 77 | net.server:listen = function() -- Listen on port from IP address. 78 | end 79 | 80 | net.server:getaddr = function() -- Returns server local address/port. 81 | end 82 | 83 | net.socket = { } 84 | net.socket:close = function() -- Closes socket. 85 | end 86 | 87 | net.socket:connect = function() -- Connect to a remote server. 88 | end 89 | 90 | net.socket:dns = function() -- Provides DNS resolution for a hostname. 91 | end 92 | 93 | net.socket:getpeer = function() -- Retrieve port and ip of remote peer. 94 | end 95 | 96 | net.socket:getaddr = function() -- Retrieve local port and ip of socket. 97 | end 98 | 99 | net.socket:hold = function() -- Throttle data reception by placing a request to block the TCP receive function. 100 | end 101 | 102 | net.socket:on = function() -- Register callback functions for specific events. 103 | end 104 | 105 | net.socket:send = function() -- Sends data to remote peer. 106 | end 107 | 108 | net.socket:ttl = function() -- Changes or retrieves Time-To-Live value on socket. 109 | end 110 | 111 | net.socket:unhold = function() -- Unblock TCP receiving data by revocation of a preceding hold(). 112 | end 113 | 114 | net.udpsocket = { } 115 | net.udpsocket:close = function() -- Closes UDP socket. 116 | end 117 | 118 | net.udpsocket:listen = function() -- Listen on port from IP address. 119 | end 120 | 121 | net.udpsocket:on = function() -- Register callback functions for specific events. 122 | end 123 | 124 | net.udpsocket:send = function() -- Sends data to specific remote peer. 125 | end 126 | 127 | net.udpsocket:dns = function() -- Provides DNS resolution for a hostname. 128 | end 129 | 130 | net.udpsocket:getaddr = function() -- Retrieve local port and ip of socket. 131 | end 132 | 133 | net.udpsocket:ttl = function() -- Changes or retrieves Time-To-Live value on socket. 134 | end 135 | 136 | net.dns.getdnsserver = function() -- Gets the IP address of the DNS server used to resolve hostnames. 137 | end 138 | 139 | net.dns.resolve = function() -- Resolve a hostname to an IP address. 140 | end 141 | 142 | net.dns.setdnsserver = function() -- Sets the IP of the DNS server used to resolve hostnames. 143 | end 144 | ]] 145 | 146 | socket = { 147 | new = function(port) 148 | self = { } 149 | self._socket = Socket.bind("*",port) 150 | return self 151 | end, 152 | connect = function(ip) 153 | end, 154 | on = function(event,conn) 155 | end, 156 | send = function(str) 157 | self._socket:send(str) 158 | end, 159 | close = function(str) 160 | self._socket:close() 161 | end 162 | } 163 | 164 | table.insert(node.modules,"net") 165 | 166 | return net 167 | 168 | -------------------------------------------------------------------------------- /modules/node/node.lua: -------------------------------------------------------------------------------- 1 | -- == Node Module == 2 | -- 3 | -- Copyright (c) 2018 by Rene K. Mueller 4 | -- 5 | -- License: MIT (see LICENSE file) 6 | -- 7 | -- Description: 8 | -- 9 | -- Notes: 10 | -- - _sysinfo.cpu_max_mhz and _sysinfo.cpu_max_mhz contain range for /usr/bin/cpufreq-set (Armbian) 11 | -- - cpufreq-info outputs detailed possible setttings for cpufreq-set 12 | -- 13 | -- History: 14 | -- 2018/02/23: 0.0.1: first version 15 | 16 | node = { 17 | _VERSION = '0.0.2', 18 | _output = print, 19 | modules = { } 20 | } 21 | 22 | if io.open("/usr/bin/cpufreq-info") then -- we don't have file.exists() yet 23 | io.close() 24 | -- probe available cpu frequencies (for node.setcpufreq()) 25 | local f = io.popen("cpufreq-info") 26 | repeat 27 | local t = f:read("*line") 28 | if t and t:match("available frequency steps:") then 29 | for v,u in t:gmatch("([%d%.]+) ([MG]Hz)") do 30 | v = tonumber(v) 31 | if u=='GHz' then 32 | v = v * 1000 33 | end 34 | if not node._cpufreq then 35 | node._cpufreq = { } 36 | end 37 | --node._cpufreq[v] = v 38 | table.insert(node._cpufreq,v) 39 | end 40 | t = nil 41 | end 42 | until t==nil 43 | f:close() 44 | end 45 | 46 | local _getmac = function() 47 | if not node._mac then 48 | local f = io.popen('ifconfig') 49 | local t = f:read("*all") 50 | f:close() 51 | node._mac = t:match("HWaddr ([%:%x]*)") or t:match("ether ([%:%x]*)") 52 | end 53 | return node._mac 54 | end 55 | 56 | node.bootreason = function() -- Returns the boot reason and extended reset info. 57 | return 0,0 58 | end 59 | 60 | node.chipid = function() -- Returns the chip ID (based on last three bytes of MAC address) 61 | local m = _getmac() 62 | local id0,id1,id2 = m:match("(%x%x):(%x%x):(%x%x)$") 63 | return tonumber(id0..id1..id2,16) 64 | end 65 | 66 | node.compile = function(fn) -- Compiles a Lua text file into Lua bytecode, and saves it as . 67 | if file.exists(fn) then 68 | local f = file.open(fn,"r") 69 | local chunk = loadstring(f:read("*all")) 70 | f:close() 71 | local fnn = fn:gsub(".lua$",".lc") 72 | if fnn ~= fn then 73 | f = file.open(fnn,"w") 74 | f:write(string.dump(chunk)) 75 | f:close() 76 | end 77 | end 78 | end 79 | 80 | node.dsleep = function() -- Enters deep sleep mode, wakes up when timed out. 81 | _syslog.print(_syslog.ERROR,"node.dsleep() not yet implemented") 82 | end 83 | 84 | node.flashid = function() -- Returns the flash chip ID. 85 | -- check /dev/disk/by-uuid and blkid to lookup UUID of disk 86 | local getUUID = function(dev) 87 | local f = io.popen("lsblk --output UUID "..dev.." 2>&1") -- lookup UUID of device (with silent fail) 88 | if f then 89 | _ = f:read("*line") -- 'UUID' 90 | _ = f:read("*line") 91 | f:close() 92 | return _ and _:match("(%S+)") or nil -- pass disk UUID 93 | end 94 | --_syslog.print(_syslog.WARN,"failed to determine disk UUID of "..dev) 95 | return nil 96 | end 97 | 98 | local f = io.popen("df -k .") -- what mount point we reside in? 99 | if f then 100 | _ = f:read("*line") 101 | _ = f:read("*line") -- get mount point 102 | f:close() 103 | local m = _ and _:match("(%S+)") or nil 104 | if m then 105 | local id = getUUID(m) 106 | --_syslog.print(_syslog.INFO,"disk UUID of "..m.." is "..tostring(id).." [1]") 107 | if id then 108 | return id 109 | end 110 | end 111 | end 112 | 113 | local f = io.popen("mount") -- fallback, check "/" or "/home" or "/boot" and consider it as main disk 114 | if f then 115 | local t 116 | repeat 117 | t = f:read("*line") 118 | if t then 119 | local dev,on,dir = t:match("(%S+)%s+(%S+)%s+(%S+)") 120 | if dir and (dir=="/" or dir=="/home" or dir=="/boot") then 121 | local id = getUUID(dev) 122 | --_syslog.print(_syslog.INFO,"disk UUID of "..dev.." is "..tostring(id).." [2]") 123 | if id then 124 | f:close() 125 | return id 126 | end 127 | break 128 | end 129 | end 130 | until t==nil 131 | f:close() 132 | end 133 | _syslog.print(_syslog.WARN,"cannot determine flash id of current directory") 134 | return nil 135 | end 136 | 137 | node.flashsize = function() -- Returns the flash chip size in bytes. 138 | local remain, used, total = file.fsinfo() 139 | return total * 1024 140 | end 141 | 142 | node.heap = function() -- Returns the current available heap size in bytes. 143 | local f = io.open('/proc/meminfo') 144 | local t = f:read("*all") 145 | f:close() 146 | local h = t:match("MemFree: +(%d+)") 147 | return h and tonumber(h)*1024 or 0 148 | end 149 | 150 | node.info = function() -- Returns majorVer, minorVer, devVer, chipid, flashid, flashsize, flashmode, flashspeed and architecture 151 | local mav,miv,devv = node._VERSION:match("(%d+).(%d+).(%d)") 152 | return mav, miv, devv, node.chipid(), node.flashid(), 0, 0, 0, 'linux' 153 | end 154 | 155 | node.input = function(s) -- Submits a string to the Lua interpreter. 156 | loadstring(s) 157 | end 158 | 159 | node.output = function(f) -- Redirects the Lua interpreter output to a callback function. 160 | node._output = f 161 | end 162 | 163 | node.restart = function() -- Restarts the system (reboot) 164 | os.execute("reboot") -- likely will fail as we aren't root (very unlikely that we are) 165 | end 166 | 167 | node.restore = function() -- Restore defaults 168 | _syslog.print(_syslog.ERROR,"node.restore() is not implemented yet") 169 | end 170 | 171 | node.setcpufreq = function(f) -- Change the working CPU Frequency. 172 | if file.exists("/usr/bin/cpufreq-set") then 173 | f = tonumber(f) 174 | local mi = tonumber(_sysinfo.cpu_min_mhz) 175 | local mx = tonumber(_sysinfo.cpu_max_mhz) 176 | if f >= mi and f <= mx then 177 | -- we need to check valid settings (within the range) 178 | local cset = false 179 | for i,ff in pairs(node._cpufreq) do 180 | if ff==f then 181 | os.execute("cpufreq-set --freq "..f.."MHz") 182 | cset = true 183 | break 184 | end 185 | end 186 | if not cset then 187 | local o = " " 188 | --table.sort(node._cpufreq) 189 | for i,f in pairs(node._cpufreq) do 190 | o = o .. f .. " " 191 | end 192 | if false then -- being restrictive 193 | print("node.setcpufreq(): ERROR: only following frequencies [MHz] settings possible:") 194 | print(o) 195 | else 196 | local ff -- being generous: find a possible frequency 197 | for i,ff in pairs(node._cpufreq) do 198 | if ff >= tonumber(_sysinfo.cpu_min_mhz) and ff >= f then 199 | os.execute("cpufreq-set --freq "..ff.."MHz") 200 | _syslog.print(_syslog.INFO,"frequency "..f.." [MHz] isn't available, instead "..ff.." [MHz] used") 201 | break 202 | end 203 | end 204 | end 205 | end 206 | else 207 | _syslog.print(_syslog.ERROR,"node.setcpufreq(): range of "..mi.."-"..mx.." is supported: "..f.." out of range") 208 | end 209 | else 210 | _syslog.print(_syslog.ERROR,"this platform does not support node.setcpufreq(), check /usr/bin/cpufreq-set") 211 | end 212 | end 213 | 214 | node.sleep = function() -- Put NodeMCU in light sleep mode to reduce current consumption. 215 | _syslog.print(_syslog.ERROR,"node.dsleep() not yet implemented") 216 | end 217 | 218 | node.stripdebug = function() -- Controls the amount of debug information kept during node. 219 | _syslog.print(_syslog.ERROR,"node.stripdebug() not yet implemented") 220 | end 221 | 222 | node.osprint = function() -- Controls whether the debugging output from the Espressif SDK is printed. 223 | _syslog.print(_syslog.ERROR,"node.osprint() not yet implemented") 224 | end 225 | 226 | node.random = function() -- This behaves like math. 227 | return math.random() 228 | end 229 | 230 | node.egc = { } 231 | node.egc.setmode = function() -- Sets the Emergency Garbage Collector mode. 232 | _syslog.print(_syslog.ERROR,"node.egc.setmode() not yet implemented") 233 | end 234 | 235 | node.task = { } 236 | node.task.post = function() -- Enable a Lua callback or task to post another task request. 237 | _syslog.print(_syslog.ERROR,"node.task.post() not yet implemented") 238 | end 239 | 240 | table.insert(node.modules,'node') 241 | 242 | return node; 243 | 244 | -------------------------------------------------------------------------------- /modules/rtctime/rtctime.lua: -------------------------------------------------------------------------------- 1 | -- == RTCTIME Module == 2 | -- 3 | -- Copyright (c) 2018 by Rene K. Mueller 4 | -- 5 | -- License: MIT License (see LICENSE file) 6 | -- 7 | -- Description: 8 | -- Providing rtctime in case module doesn't exist 9 | -- 10 | -- History: 11 | -- 2018/02/27: 0.0.2: rtctime.dsleep() and rtctime.dsleep_aligned() placeholder 12 | -- 2018/02/10: 0.0.1: moved from rtc/init.lua to here 13 | 14 | local socket = require('socket') 15 | 16 | rtctime = { 17 | t = socket.gettime(), 18 | set = function(t) 19 | rtctime.t = t - tmr.time() 20 | end, 21 | get = function() 22 | return rtctime.t + tmr.time() 23 | end, 24 | epoch2cal = function(t) 25 | local tm = { } 26 | local dc = t % (24*60*60) -- based on gmtime.c 27 | tm.sec = dc % 60 28 | tm.min = int((dc % 3600) / 60) 29 | tm.hour = int(dc / 3600) 30 | local y = 1970 31 | local dno = int(t / (24*60*60)) 32 | local dm = { { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } } 33 | local lp = function(y) return ((y%4) == 0) and not ((y%100)==0 and (y%400)~=0) end 34 | local ys = function(y) return 365 + (lp(y) and 1 or 0) end 35 | tm.wday = (dno + 4) % 7 36 | while(dno >= ys(y)) do 37 | dno = dno - ys(y) 38 | y = y + 1 39 | end 40 | tm.yday = dno 41 | tm.mon = 1 42 | while(dno >= dm[lp(y) and 2 or 1][tm.mon]) do 43 | dno = dno - dm[lp(y) and 2 or 1][tm.mon] 44 | tm.mon = tm.mon+1 45 | end 46 | tm.day = dno+1 47 | tm.year = y 48 | return tm 49 | end, 50 | dsleep = function() -- Puts the device into deep sleep mode, like node. 51 | _syslog.print(_syslog.ERROR,"rctime.dsleep() is not yet implemented") 52 | end, 53 | dsleep_aligned = function() -- For applications where it is necessary to take samples with high regularity, this function is useful. 54 | _syslog.print(_syslog.ERROR,"rctime.dsleep_aligned() is not yet implemented") 55 | end 56 | } 57 | 58 | table.insert(node.modules,"rtctime") 59 | return rtctime 60 | 61 | -------------------------------------------------------------------------------- /modules/sjson/sjson.lua: -------------------------------------------------------------------------------- 1 | -- == SJSON Module == 2 | -- 3 | -- Copyright (c) 2018 by Rene K. Mueller 4 | -- 5 | -- License: MIT License (see LICENSE file) 6 | -- 7 | -- Description: 8 | -- Providing JSON encode/decode facility 9 | -- Notes: 10 | -- Uses lunajson package for now, a true C JSON (sjson) might come later 11 | -- 12 | -- History: 13 | -- 2018/02/27: 0.0.1: sjson.encode() and sjson.decode() work, sjson.decoder()/.encoder() does not yet 14 | 15 | local JSON = require("lunajson") 16 | 17 | sjson = { 18 | encoder = function(tbl,opts) 19 | _syslog.print(_syslog.ERROR,"sjson.encoder() not yet implemented") 20 | return {} 21 | end, 22 | encode = function(tbl,opts) 23 | return JSON.encode(tbl) 24 | end, 25 | decoder = function(opts) 26 | _syslog.print(_syslog.ERROR,"sjson.encoder() not yet implemented") 27 | return {} 28 | end, 29 | decode = function(str,opts) 30 | return JSON.decode(str) 31 | end 32 | } 33 | 34 | table.insert(node.modules,'sjson') 35 | return sjson 36 | -------------------------------------------------------------------------------- /modules/tmr/tmr-node.lua: -------------------------------------------------------------------------------- 1 | -- == Timer(Tmr)-Node Module == 2 | -- 3 | -- Copyright (c) 2018 by Rene K. Mueller 4 | -- 5 | -- License: MIT (see LICENSE file) 6 | -- 7 | -- Description: 8 | -- - tmr.now() is only a 31 bit microsecond counter (compatible with NodeMCU/ESP8266) 9 | -- - tmr.time() is integer only 10 | -- - tmr.uptime() is high precision uptime (not backward compatible with NodeMCU/ESP8266) 11 | -- 12 | -- Notes: 13 | -- - *this* is the LuaNode version of tmr.lua 14 | -- - lalarm module only provides POSIX alarm() interface with integer seconds delays, but we need milli- or microseconds 15 | -- - socket.gettime() gives high precision time 16 | -- - https://github.com/davisdude/Timer uses coroutines 17 | -- 18 | -- History: 19 | -- 2018/03/05: 0.0.1: based on tmr.lua (0.0.3) with LuaNode setInterval/Timeout() usage (not yet fully tested) 20 | 21 | local socket = require("socket") 22 | local bit = require('bit') 23 | 24 | tmr = { 25 | _VERSION = '0.0.1', 26 | _start = socket.gettime(), 27 | 28 | ALARM_SINGLE = 0, 29 | ALARM_AUTO = 1, 30 | ALARM_SEMI = 2, 31 | 32 | _ACTIVE = 0, 33 | _INACTIVE = 1, 34 | _SUSPENDED = 2, -- only formerly _ACTIVE instances can be _SUSPENDED 35 | 36 | _list = { }, 37 | _last = 0 38 | } 39 | 40 | tmr.now = function() -- Returns the system counter, which counts in microseconds. 41 | local t = socket.gettime() - tmr._start 42 | return bit.band(int((t - int(t)) * 1000000) + int(t) * 1000000,0x7ffffff) -- only 31 bits are good 43 | end 44 | 45 | tmr.time = function() -- Returns the system uptime, in seconds. 46 | return int(tmr.uptime()) 47 | end 48 | 49 | tmr.uptime = function() -- Returns the system uptime, in seconds with microseconds precision 50 | return socket.gettime() - tmr._start 51 | end 52 | 53 | tmr.create = function() -- Creates a dynamic timer object. 54 | local t = { 55 | _state = tmr._INACTIVE, 56 | 57 | alarm = function(self,t,m,func) -- This is a convenience function combining tmr. 58 | self:register(t,m,func) 59 | self:start() 60 | end, 61 | 62 | interval = function(self,t) -- Changes a registered timer's expiry interval. 63 | self._interval = t / 1000 64 | self._timeout = tmr.uptime() + self._interval 65 | end, 66 | 67 | register = function(self,t,m,func) -- Configures a timer and registers the callback function to call on expiry. 68 | self:interval(t) 69 | self._mode = m 70 | self._func = func 71 | end, 72 | 73 | resume = function(self) -- Resume an individual timer. 74 | self._state = tmr._ACTIVE 75 | self._timeout = tmr.uptime() + self._interval -- recalculate _timeout 76 | _ = self._mode==tmr.ALARM_AUTO and self._ref:setInterval(self._func,self._interval*1000,self) or self._ref:setTimeout(self._func,self._interval*1000,self) 77 | end, 78 | 79 | start = function(self) -- Starts or restarts a previously configured timer. 80 | self._state = tmr._ACTIVE 81 | if self._mode==tmr.ALARM_SINGLE then 82 | self._ref = setTimeout(self._func,self._interval*1000,self) 83 | elseif self._mode==tmr.ALARM_AUTO then 84 | self._ref = setInterval(self._func,self._interval*1000,self) 85 | end 86 | end, 87 | 88 | state = function(self) -- Checks the state of a timer. 89 | return self._state == tmr._ACTIVE, self._mode 90 | end, 91 | 92 | stop = function(self) -- Stops a running timer, but does not unregister it. 93 | self._state = tmr._INACTIVE 94 | _ = self._mode==tmr.ALARM_AUTO and clearInterval(self._ref) or clearTimeout(self._ref) 95 | end, 96 | 97 | suspend = function(self) -- Suspend an armed timer. 98 | self._state = tmr._SUSPENDED 99 | _ = self._mode==tmr.ALARM_AUTO and clearInterval(self._ref) or clearTimeout(self._ref) 100 | end, 101 | 102 | unregister = function(self) -- Stops the timer (if running) and unregisters the associated callback. 103 | self._state = tmr._INACTIVE 104 | _ = self._mode==tmr.ALARM_AUTO and clearInterval(self._ref) or clearTimeout(self._ref) 105 | table.remove(tmr._list,self._id) 106 | end 107 | } 108 | 109 | tmr._last = tmr._last + 1 110 | t._id = tmr._last 111 | 112 | table.insert(tmr._list,tmr._last,t) 113 | 114 | return t 115 | end 116 | 117 | tmr.delay = function() -- Busyloops the processor for a specified number of microseconds. 118 | _syslog.print(_syslog.ERROR,"tmr.delay() no longer supported, change your code to adapt asynchronous programming style") 119 | return 120 | end 121 | 122 | tmr.resume_all = function() -- Resume all timers. 123 | for i,tm in pairs(tmr._list) do 124 | if tm._state == tmr._SUSPENDED then 125 | tm:resume() 126 | end 127 | end 128 | end 129 | 130 | tmr.suspend_all = function() -- Suspend all currently armed timers. 131 | for i,tm in pairs(tmr._list) do 132 | if tm._state == tmr._ACTIVE then 133 | tm:suspend() 134 | end 135 | end 136 | end 137 | 138 | tmr.softwd = function() -- Provides a simple software watchdog, which needs to be re-armed or disabled before it expires, or the system will be restarted. 139 | _syslog.print(_syslog.ERROR,"tmr.softwd() not yet implemented") 140 | end 141 | 142 | tmr.wdclr = function() -- Feed the system watchdog. 143 | _syslog.print(_syslog.ERROR,"tmr.wdclr() not yet implemented") 144 | end 145 | 146 | table.insert(node.modules,'tmr') 147 | 148 | return tmr; 149 | 150 | -------------------------------------------------------------------------------- /modules/tmr/tmr.lua: -------------------------------------------------------------------------------- 1 | -- == Timer(Tmr) Module == 2 | -- 3 | -- Copyright (c) 2018 by Rene K. Mueller 4 | -- 5 | -- License: MIT (see LICENSE file) 6 | -- 7 | -- Description: 8 | -- - tmr.now() is only a 31 bit microsecond counter (compatible with NodeMCU/ESP8266) 9 | -- - tmr.time() is integer only 10 | -- - tmr.uptime() is high precision uptime (not backward compatible with NodeMCU/ESP8266) 11 | -- 12 | -- Notes: 13 | -- - lalarm module only provides POSIX alarm() interface with integer seconds delays, but we need milli- or microseconds 14 | -- - socket.gettime() gives high precision time 15 | -- - https://github.com/davisdude/Timer uses coroutines 16 | -- 17 | -- History: 18 | -- 2018/03/04: 0.0.3: tmr.create():* and tmr.suspend_all() and tmr.resume_all() implemented with coroutine backend in mind 19 | -- 2018/02/25: 0.0.2: tmr.now(), time() and uptime() working 20 | -- 2018/02/21: 0.0.1: first version, just a skeleton 21 | 22 | local socket = require("socket") 23 | local bit = require('bit') 24 | --local timer = loadfile("modules/tmr/timer.lua")() -- sample code doesn't work .. after wait(1) never reaches next statement 25 | 26 | tmr = { 27 | _VERSION = '0.0.3', 28 | _start = socket.gettime(), 29 | 30 | ALARM_SINGLE = 0, 31 | ALARM_AUTO = 1, 32 | ALARM_SEMI = 2, 33 | 34 | _ACTIVE = 0, 35 | _INACTIVE = 1, 36 | _SUSPENDED = 2, -- only formerly _ACTIVE instances can be _SUSPENDED 37 | 38 | _list = { }, 39 | _last = 0 40 | } 41 | 42 | tmr.now = function() -- Returns the system counter, which counts in microseconds. 43 | local t = socket.gettime() - tmr._start 44 | return bit.band(int((t - int(t)) * 1000000) + int(t) * 1000000,0x7ffffff) -- only 31 bits are good 45 | end 46 | 47 | tmr.time = function() -- Returns the system uptime, in seconds. 48 | return int(tmr.uptime()) 49 | end 50 | 51 | tmr.uptime = function() -- Returns the system uptime, in seconds with microseconds precision 52 | return socket.gettime() - tmr._start 53 | end 54 | 55 | tmr.create = function() -- Creates a dynamic timer object. 56 | local t = { 57 | _state = tmr._INACTIVE, 58 | 59 | alarm = function(self,t,m,func) -- This is a convenience function combining tmr. 60 | self:register(t,m,func) 61 | self:start() 62 | end, 63 | 64 | interval = function(self,t) -- Changes a registered timer's expiry interval. 65 | self._interval = t / 1000 66 | self._timeout = tmr.uptime() + self._interval 67 | end, 68 | 69 | register = function(self,t,m,func) -- Configures a timer and registers the callback function to call on expiry. 70 | self:interval(t) 71 | self._mode = m 72 | self._func = func 73 | end, 74 | 75 | resume = function(self) -- Resume an individual timer. 76 | self._state = tmr._ACTIVE 77 | end, 78 | 79 | start = function(self) -- Starts or restarts a previously configured timer. 80 | self._state = tmr._ACTIVE 81 | end, 82 | 83 | state = function(self) -- Checks the state of a timer. 84 | return self._state == tmr._ACTIVE, self._mode 85 | end, 86 | 87 | stop = function(self) -- Stops a running timer, but does not unregister it. 88 | self._state = tmr._INACTIVE 89 | end, 90 | 91 | suspend = function(self) -- Suspend an armed timer. 92 | self._state = tmr._SUSPENDED 93 | end, 94 | 95 | unregister = function(self) -- Stops the timer (if running) and unregisters the associated callback. 96 | self._state = tmr._INACTIVE 97 | table.remove(tmr._list,self._id) 98 | end 99 | } 100 | 101 | tmr._last = tmr._last + 1 102 | t._id = tmr._last 103 | 104 | table.insert(tmr._list,tmr._last,t) 105 | 106 | return t 107 | end 108 | 109 | tmr._run_all = function() 110 | while true do 111 | local t = tmr.uptime() 112 | for i,tm in pairs(tmr._list) do 113 | --print(tm._id,tm._state,tm._interval,tm._timeout,t) 114 | if tm._state == tmr._ACTIVE and t >= tm._timeout then 115 | tm._func(tm) 116 | if tm._mode == tmr.ALARM_SINGLE then 117 | tm._state = tmr._INACTIVE 118 | elseif tm._mode == tmr.ALARM_AUTO then 119 | tm._timeout = tmr.uptime() + tm._interval 120 | end 121 | end 122 | end 123 | coroutine.yield() 124 | end 125 | end 126 | 127 | tmr.delay = function() -- Busyloops the processor for a specified number of microseconds. 128 | _syslog.print(_syslog.ERROR,"tmr.delay() no longer supported, change your code to adapt asynchronous programming style") 129 | return 130 | end 131 | 132 | tmr.resume_all = function() -- Resume all timers. 133 | for i,tm in pairs(tmr._list) do 134 | if tm._state == tmr._SUSPENDED then 135 | tm:resume() 136 | end 137 | end 138 | end 139 | 140 | tmr.suspend_all = function() -- Suspend all currently armed timers. 141 | for i,tm in pairs(tmr._list) do 142 | if tm._state == tmr._ACTIVE then 143 | tm:suspend() 144 | end 145 | end 146 | end 147 | 148 | tmr.softwd = function() -- Provides a simple software watchdog, which needs to be re-armed or disabled before it expires, or the system will be restarted. 149 | _syslog.print(_syslog.ERROR,"tmr.softwd() not yet implemented") 150 | end 151 | 152 | tmr.wdclr = function() -- Feed the system watchdog. 153 | _syslog.print(_syslog.ERROR,"tmr.wdclr() not yet implemented") 154 | end 155 | 156 | table.insert(node.modules,'tmr') 157 | 158 | return tmr; 159 | -------------------------------------------------------------------------------- /modules/uart/uart.lua: -------------------------------------------------------------------------------- 1 | -- == UART Module == 2 | -- 3 | -- Copyright (c) 2018 by Rene K. Mueller 4 | -- 5 | -- License: MIT (see LICENSE file) 6 | -- 7 | -- Description: 8 | -- 9 | -- Notes: 10 | -- - not yet sure which backend to use 11 | -- - https://github.com/vsergeev/lua-periphery (already used for gpio and i2c) 12 | -- - https://github.com/edartuz/lua-serial 13 | -- - or luanode internals posix stream to properly support uart.on() callback (non-blocking is essential) 14 | -- 15 | -- History: 16 | -- 2018/03/10: 0.0.1: first version, just a skeleton, not yet functional 17 | 18 | local Serial = require('periphery').Serial 19 | 20 | uart = { 21 | _VERSION = '0.0.1', 22 | _devices = { }, 23 | DEV_PREFIX = UART_DEV_PREFIX or "tty", 24 | PARITY_NONE = "none", 25 | PARITY_ODD = "odd", 26 | PARITY_EVENT = "even", 27 | STOP_BITS_1 = 1, 28 | STOP_BITS_1_5 = 1, 29 | STOP_BITS_2 = 2, 30 | FLOWCTRL_NONE = 0, 31 | FLOWCTRL_CTS = 1, 32 | FLOWCTRL_RTS = 2, 33 | } 34 | 35 | -- we determine the uart devices and dynamically set uart.UART[0..x] = uart.DEV_PREFIX..d (e.g. uart.UART0 = "/dev/ttyUSB0") 36 | for f in lfs.dir("/dev/") do 37 | if f:match("^"..uart.DEV_PREFIX) then 38 | if not f:match(uart.DEV_PREFIX.."%d*$") then 39 | table.insert(uart._devices,"/dev/"..f) 40 | end 41 | end 42 | end 43 | 44 | table.sort(uart._devices) 45 | 46 | local i = 0 47 | local o = "" 48 | for j,d in pairs(uart._devices) do 49 | local dev = d 50 | o = o .. (i>0 and " " or "") .. dev 51 | uart['UART'..i] = dev 52 | i = i+1 53 | uart._count = i 54 | end 55 | if i>0 then 56 | _syslog.print(_syslog.INFO,"uart: "..i.." interface(s) found: "..o) 57 | else 58 | _syslog.print(_syslog.INFO,"uart: no interface found") 59 | end 60 | 61 | -- -------------------------------------------------------------------------------------------------------------------------- 62 | 63 | uart.alt = function(on) -- Change UART pin assignment. 64 | _syslog.print(_syslog.WARN,"uart.alt() not supported, use uart.setup(id,datab,par,stopb,{tx=pintx,rx=pinrx,...})") 65 | end 66 | 67 | uart.on = function(id, method, sep, func, input) -- Sets the callback function to handle UART events. 68 | if id=='data' then 69 | _syslog.print(_syslog.WARN,"uart.on(): please use as first argument the UART id, using id = uart.UART0") 70 | input = func 71 | func = sep 72 | sep = method 73 | method = id 74 | id = uart.UART0 75 | end 76 | if not (uart.config[id] and uart.config[id].on) then 77 | _syslog.print(_syslog.ERROR,"uart.on(): please first uart.setup() the device") 78 | os.exit(-1) 79 | end 80 | 81 | if func then -- setup data receive callback 82 | uart.config[id].on[method] = { func = func, sep = sep, input = input } 83 | else 84 | uart.config[id].on[method] = { } 85 | end 86 | end 87 | 88 | uart.setup = function(id, baud, databits, parity, stopbits, echo) -- (Re-)configures the communication parameters of the UART. 89 | if type(id)=='number' then 90 | _syslog.print(_syslog.WARN,"uart.setup(): please use as first argument the UART id, using id = uart.UART0") 91 | -- uart.setup(baud, databits, parity, stopbits, echo) 92 | -- uart.setup(id, baud, databits, parity, stopbits, echo) 93 | echo = stopbits 94 | stopbits = parity 95 | parity = databits 96 | databits = baud 97 | baud = id 98 | id = uart.UART0 99 | end 100 | if type(echo)=='table' then -- configure new pins 101 | -- NOTE: if the board allows configure GPIO as UART interface, then it has to happen here: 102 | -- echo.tx int. TX pin. Required 103 | -- rx int. RX pin. Required 104 | -- cts in. CTS pin. Optional 105 | -- rts in. RTS pin. Optional 106 | -- tx_inverse boolean. Inverse TX pin. Default: false 107 | -- rx_inverse boolean. Inverse RX pin. Default: false 108 | -- cts_inverse boolean. Inverse CTS pin. Default: false 109 | -- rts_inverse boolean. Inverse RTS pin. Default: false 110 | -- flow_control int. Combination of uart.FLOWCTRL_NONE, uart.FLOWCTRL_CTS, uart.FLOWCTRL_RTS. Default: uart.FLOWCTRL_NONE 111 | 112 | echo = nil 113 | end 114 | uart.config = uart.config or { } 115 | uart.config[id] = uart.config[id] or { } 116 | uart.config[id].baud = baud 117 | uart.config[id].databits = databits 118 | uart.config[id].stopbits = stopbits 119 | uart.config[id].parity = parity 120 | 121 | -- here insert hardware near setup the uart truly 122 | -- set uart.config[id].dev to support :write(s) 123 | 124 | uart.config[id].on = { } 125 | end 126 | 127 | uart.getconfig = function(id) -- Returns the current configuration parameters of the UART. 128 | local c = uart.config[id] 129 | return c.baud, c.databits, c.parity, c.stopbits 130 | end 131 | 132 | uart.write = function(id, s) -- Write string or byte to the UART. 133 | uart.config[id].dev:write(s) 134 | end 135 | 136 | table.insert(node.modules,"uart") 137 | 138 | return uart 139 | 140 | -------------------------------------------------------------------------------- /nodemcu: -------------------------------------------------------------------------------- 1 | #!/usr/bin/luanode 2 | --#!/usr/bin/lua5.1 3 | --#!/usr/bin/luajit 4 | 5 | -- == NodeMCU/Linux == 6 | -- 7 | -- Copyright (c) 2018 by Rene K. Mueller 8 | -- 9 | -- License: MIT (see LICENSE file) 10 | -- 11 | -- Description: 12 | -- see http://github.com/Spiritdude/nodemcu-linux 13 | -- Notes: 14 | -- luajit has built-in ffi(), but function(...) arg isn't set, but needs `arg = {...}` 15 | -- lua5.1 requires 3rd party luaffifb for ffi(), and it does function(...) arg set 16 | -- 17 | -- History: 18 | -- 2018/03/11: 0.0.7: '-e' added to execute strings 19 | -- 2018/03/09: 0.0.6: parse command-line arguments (options etc) 20 | -- 2018/03/05: 0.0.5: luanode tests: timers work fine, luanode.net not yet used 21 | -- 2018/02/27: 0.0.3: 'nodemcu' takes arguments and are executed 22 | -- 2018/02/25: 0.0.2: dofile() redefined so ./ and /usr/local/lib/nodemcu paths are considered (easier dev process) 23 | -- 2018/02/24: 0.0.1: first version: node, tmr, file, gpio and rtctime with very reduced functionality 24 | 25 | APPNAME = "NodeMCU/Linux" 26 | VERSION = '0.0.7' 27 | 28 | dofile = function(fn) -- we redefine so we extend it in manners we need 29 | for i,k in pairs({'./','/usr/local/lib/nodemcu/'}) do 30 | local fnf 31 | if io.open(k..fn) then 32 | fnf = k..fn 33 | end 34 | if arg[-1]:match("luanode") and io.open(k..fn:gsub("%.lua","-node.lua")) then 35 | fnf = k..fn:gsub("%.lua","-node.lua") 36 | end 37 | if fnf then 38 | if tmr and _syslog then -- might not be available yet 39 | _syslog.print(_syslog.INFO,"dofile "..fnf) 40 | end 41 | local f = assert(loadfile(fnf)) 42 | return f() 43 | end 44 | end 45 | _syslog.print(_syslog.ERROR,"file "..fn.." not found to execute") 46 | return nil 47 | end 48 | 49 | dofile("modules/base/base.lua") 50 | 51 | local opts = { } 52 | local k2f = { v = 'verbose', h = 'help', s = 'silent', e = 'execute' } 53 | local _arg = { } 54 | 55 | for k,v in pairs(arg) do -- parse all command line arguments (including -1 and 0) 56 | if k > 0 then 57 | if v:match("^%-(%w+)$") then -- single char switch 58 | local kx = v:match("^%-(%w+)$") 59 | for i=1,kx:len() do 60 | local a = kx:sub(i,i) 61 | opts[k2f[a] or a] = (opts[k2f[a] or a] or 0) + 1 62 | end 63 | elseif v:match("^%-%-([%w%.]+)$") then -- long switch 64 | local kx, vx = v:match("^%-%-([%w%.]+)$") 65 | opts[kx] = opts[kx] or 1 66 | elseif v:match("^%-%-([%w%.]+)=(.*)$") then -- key=value assignment 67 | local kx, vx = v:match("^%-%-([%w%.]+)=(.*)$") 68 | opts[kx] = vx 69 | else 70 | table.insert(_arg,v) 71 | end 72 | else 73 | _arg[k] = v 74 | end 75 | end 76 | 77 | arg = _arg 78 | 79 | if opts.help then 80 | print(APPNAME .. " " .. VERSION .. [[ USAGE: nodemcu {[options] .. } {[file1] .. } 81 | options: 82 | -v or -vv increase verbosity 83 | --verbose= define verbosity n = 0..10 84 | -h or --help print this usage help 85 | -s or --silent silent 86 | -e or --execute execute rest of arguments as code 87 | --version display version and exit 88 | --package.path=

define or add package path, use '+' to add additional path 89 | 90 | examples: 91 | nodemcu boot and execute init.lua and enter Lua console 92 | nodemcu --version 93 | nodemcu --help 94 | nodemcu -vvv 95 | nodemcu --verbose=3 96 | nodemcu test.lua boot and execute test.lua and exit 97 | nodemcu -e 'table.foreach(_sysinfo,print)' 98 | nodemcu --package.path=+./ 99 | ]]) 100 | os.exit() 101 | elseif opts.version then 102 | print(APPNAME,VERSION) 103 | os.exit() 104 | end 105 | 106 | if opts.verbose and opts.verbose > 3 then 107 | print("opts:") 108 | table.foreach(opts,function(k,v) print("\t",k,'=',v) end) 109 | print("arg:") 110 | table.foreach(arg,function(k,v) print("\t",k,'=',v) end) 111 | end 112 | 113 | _syslog.verbose(opts.verbose or 0) 114 | 115 | -- Lua modules 116 | dofile("modules/node/node.lua") 117 | dofile("modules/tmr/tmr.lua") 118 | _syslog.print(_syslog.INFO,"loading modules ('node' and 'tmr' already loaded)") -- only at this point we have functionality for _syslog.print() 119 | --dofile("modules/wifi/wifi.lua") 120 | dofile("modules/file/file.lua") 121 | dofile("modules/gpio/gpio.lua") 122 | dofile("modules/i2c/i2c.lua") 123 | dofile("modules/net/net.lua") 124 | dofile("modules/uart/uart.lua") 125 | dofile("modules/rtctime/rtctime.lua") 126 | dofile("modules/sjson/sjson.lua") 127 | 128 | -- inherent built-in modules 129 | bit = require("bit") 130 | table.insert(node.modules,"bit") 131 | 132 | struct = require("struct") 133 | table.insert(node.modules,"struct") 134 | 135 | --ffi = require("ffi") 136 | --table.insert(node.modules,"ffi") 137 | 138 | _syslog.print(_syslog.INFO,"modules bit, struct built-in added") 139 | 140 | if math then 141 | table.insert(node.modules,"math") 142 | _syslog.print(_syslog.INFO,"module math added") 143 | end 144 | 145 | if opts['package.path'] then 146 | local p = opts['package.path'] 147 | if p:match("^%+") then 148 | package.path = package.path .. ":" .. p:match("^%+(.*)") 149 | else 150 | package.path = p 151 | end 152 | _syslog.print(_syslog.INFO,"package.path: "..package.path) 153 | end 154 | 155 | -- gather CPU info 156 | _sysinfo = { } 157 | local f = io.popen("lscpu") 158 | repeat 159 | local t = f:read("*line") 160 | if t then 161 | local k, v = t:match("([^:]+):%s+(.+)$") 162 | k = k:lower() 163 | k = k:gsub("%(s%)","s") 164 | k = k:gsub(" ","_") 165 | _sysinfo[k] = v 166 | end 167 | until t==nil 168 | f:close() 169 | 170 | -- gather OS info 171 | local f = io.open('/etc/os-release') 172 | repeat 173 | local t = f:read("*line") 174 | if t then 175 | local k, v = t:match("([%w_]+)=(.*)$") 176 | if v:match('"(.+)"') then 177 | v = v:gsub('"(.+)"',function(a) return a end) 178 | end 179 | _sysinfo['os_'..string.lower(k)] = v 180 | end 181 | until t==nil 182 | f:close() 183 | 184 | --table.foreach(_sysinfo,print) 185 | 186 | local bnr = "" 187 | if false then 188 | local bfn = 'misc/banner.co.txt' 189 | if file.exists(bfn) then 190 | local f = file.open(bfn,"r") 191 | bnr = bnr .. f:read('*all') 192 | f:close() 193 | end 194 | end 195 | 196 | bnr = bnr .. APPNAME.." "..VERSION.." powered by ".._VERSION..", "..string.format("Device ID: %d / 0x%x",node.chipid(),node.chipid()) 197 | bnr = bnr .. "\n " .. _sysinfo.architecture..(_sysinfo.model_name and " ".._sysinfo.model_name or "").." (".._sysinfo.cpus.." core(s)"..(_sysinfo.cpu_max_mhz and ", "..int(_sysinfo.cpu_min_mhz).."-"..int(_sysinfo.cpu_max_mhz).."MHz" or "")..")" 198 | bnr = bnr .. "\n " .. "modules: " 199 | for i,v in ipairs(node.modules) do 200 | bnr = bnr .. (i>1 and " " or "") .. v 201 | end 202 | 203 | if not opts.silent then 204 | print(bnr) 205 | if node._cpufreq then 206 | print(" cpu freq table [MHz]: "..table.concat(node._cpufreq,", ")) 207 | end 208 | end 209 | 210 | if file.exists("init.lua") then 211 | _syslog.print(_syslog.INFO,"execute init.lua") 212 | dofile("init.lua") 213 | else 214 | _syslog.print(_syslog.WARN,"cannot open 'init.lua'") 215 | end 216 | 217 | 218 | if false then 219 | local _tmr = coroutine.create(tmr._run_all) 220 | while true do -- wasteful tmr.create():* testing 221 | coroutine.resume(_tmr) 222 | end 223 | end 224 | 225 | --table.foreach(arg,print) 226 | if #arg >= 1 then 227 | for i=1,#arg do 228 | if opts.execute then 229 | f = loadstring(arg[i]) 230 | else 231 | f = dofile(arg[i]) 232 | end 233 | if type(f)=='function' then 234 | f() 235 | end 236 | end 237 | else 238 | --print("== Lua console started (CTRL-C will exit console)") 239 | if arg[-1]:match("luanode") then 240 | require("luanode.tty") 241 | local stdin = luanode.tty.ReadStream(Stdio.stdinFD) 242 | local stdout = luanode.tty.WriteStream(Stdio.stdoutFD) 243 | stdout:write("> ") 244 | stdin:on("data",function(self,s) 245 | local f = loadstring(s) 246 | local r 247 | if type(f)=='function' then 248 | r = f() 249 | else 250 | r = f 251 | end 252 | stdout:write("> ") 253 | end) 254 | stdin:resume() 255 | process:loop() 256 | else 257 | while true do 258 | io.write("> ") 259 | local s = io.read() 260 | local f = loadstring(s) 261 | local r 262 | if type(f)=='function' then 263 | r = f() 264 | else 265 | r = f 266 | end 267 | if r then 268 | r = tostring(r) 269 | if r:match("%n$") then 270 | io.write("> "..r) 271 | else 272 | io.write(": "..r.."\n") 273 | end 274 | end 275 | end 276 | end 277 | end 278 | -------------------------------------------------------------------------------- /startup.lua: -------------------------------------------------------------------------------- 1 | print("starting up...") 2 | 3 | print("tmr.now()",tmr.now()) 4 | print("tmr.time()",tmr.time()) 5 | print("tmr.uptime()",tmr.uptime()) 6 | local tm = rtctime.epoch2cal(rtctime.get()) 7 | local tz = 'UTC' 8 | print("rtctime",string.format("%04d/%02d/%02d %02d:%02d:%02d %s",tm["year"],tm["mon"],tm["day"],tm["hour"],tm["min"],tm["sec"],tz)) 9 | 10 | print("node.chipid()",node.chipid(),string.format("0x%x",node.chipid())) 11 | print("node.flashid()",node.flashid()) 12 | print("node.heap()",node.heap(),string.format("%dKiB",node.heap()/1014)) 13 | 14 | local o = "" 15 | for f,s in pairs(file.list()) do 16 | o = o .. f .. "("..s..") " 17 | end 18 | print("file.list()",o) 19 | 20 | print("file.stat() with json",sjson.encode(file.stat("README.md"))) 21 | local remain, used, total = file.fsinfo() 22 | print("file.fsinfo()",string.format("remain %.3fMiB, used %.3fMiB, total %.3fMiB",remain/1024,used/1024,total/1024)) 23 | 24 | if true then -- brief tmr.* testing 25 | local n = 1 26 | tmr.create():alarm(1*1000,tmr.ALARM_AUTO,function(t) 27 | print("tmr-test: ping",tmr.uptime(),n) 28 | if n==5 then 29 | tmr.suspend_all() 30 | tmr.create():alarm(3*1000,tmr.ALARM_SINGLE,function(t) 31 | print("tmr-test: once",tmr.uptime()) 32 | end) 33 | end 34 | n = n + 1 35 | end) 36 | tmr.create():alarm(1.5*1000,tmr.ALARM_AUTO,function(t) 37 | print("tmr-test: pong",tmr.uptime()) 38 | t:unregister() 39 | end) 40 | end 41 | 42 | if not _sysinfo.architecture:match("^arm") then 43 | local ffi = require("ffi") -- testing ffi (disabled for ARM-based CPU, as luaffifb seems broken there) 44 | if ffi then 45 | ffi.cdef[[ 46 | int printf(const char *fmt, ...); 47 | ]] 48 | ffi.C.printf("ffi-test: Hello %s!\n", "world") 49 | end 50 | end 51 | 52 | if net and net.createConnection then 53 | local host = "httpbin.org" 54 | print("net-test: connecting to",host) 55 | local srv = net.createConnection(net.TCP, 0) 56 | srv:on("receive",function(sck,c) 57 | print("net-test: http-received:") 58 | c:gsub("([^\n]+)\n",function(s) print("|",s) end) 59 | end) 60 | srv:on("connection", function(sck, c) 61 | -- 'Connection: close' rather than 'Connection: keep-alive' to have server 62 | -- initiate a close of the connection after final response (frees memory 63 | -- earlier here), https://tools.ietf.org/html/rfc7230#section-6.6 64 | sck:send("GET /get HTTP/1.1\r\nHost: httpbin.org\r\nConnection: close\r\nAccept: */*\r\n\r\n") 65 | end) 66 | srv:connect(80,host) 67 | end 68 | 69 | if net and net.createServer then 70 | local port = 10080 71 | print("net-test: basic http server started on port",port) 72 | local sv = net.createServer(net.TCP, 30) 73 | if sv then 74 | sv:listen(port,function(conn) 75 | conn:on("receive",function(sck,data) 76 | print("net-test: web server received") 77 | data:gsub("([^\n]+)\n",function(s) print("|",s) end) 78 | end) 79 | conn:on("sent",function(sck) 80 | print("net-test: web-server 'sent'-event received, now closing",sck,conn) 81 | sck:close() 82 | end) 83 | conn:send("HTTP/1.0 200 OK\r\nConnection: close\r\n\r\nHello world! "..tmr.uptime()) 84 | end) 85 | end 86 | end 87 | 88 | if file.exists("cpu/main.lua") then -- NodeMCU Shell arround, if so run `cpu` command 89 | dofile("cpu/main.lua")("cpu") 90 | end 91 | 92 | 93 | -------------------------------------------------------------------------------- /tests/Makefile: -------------------------------------------------------------------------------- 1 | SOURCES=$(wildcard *.lua) 2 | TESTS=$(patsubst %.lua, %.lua.test, $(SOURCES)) 3 | 4 | all:: ${TESTS} 5 | 6 | %.lua.test: %.lua 7 | @nodemcu -s $< || echo "$< failed" 8 | 9 | -------------------------------------------------------------------------------- /tests/file01.lua: -------------------------------------------------------------------------------- 1 | file.open("test.txt") 2 | l1 = file.read("\n") 3 | l2 = file.read("\n") 4 | file.close() 5 | 6 | assert(l1=="line 1\n") 7 | assert(l2=="line 2\n") 8 | 9 | os.exit(0) 10 | 11 | -------------------------------------------------------------------------------- /tests/file02.lua: -------------------------------------------------------------------------------- 1 | fh = file.open("test.txt") 2 | l1 = fh:read("\n") 3 | l2 = fh:read("\n") 4 | fh:close() 5 | 6 | assert(l1=="line 1\n") 7 | assert(l2=="line 2\n") 8 | 9 | os.exit(0) 10 | 11 | -------------------------------------------------------------------------------- /tests/file03.lua: -------------------------------------------------------------------------------- 1 | for i,v in pairs(file.list()) do 2 | print(i,v) 3 | end 4 | 5 | --assert(l1=="line 1\n") 6 | 7 | os.exit(0) 8 | 9 | -------------------------------------------------------------------------------- /tests/file04.lua: -------------------------------------------------------------------------------- 1 | luaunit = require('luaunit') 2 | 3 | table.foreach({'test.txt','..'},function(i,fn) 4 | print(fn) 5 | print(sjson.encode(file.stat(fn))) 6 | end) 7 | 8 | --assert(l1=="line 1\n") 9 | 10 | os.exit(0) 11 | 12 | -------------------------------------------------------------------------------- /tests/file05.lua: -------------------------------------------------------------------------------- 1 | file.open("test.txt") 2 | t = file.read() 3 | file.close() 4 | 5 | assert(t=="line 1\nline 2\nline 3\nline 4\nline 5\n") 6 | 7 | os.exit(0) 8 | 9 | -------------------------------------------------------------------------------- /tests/file06.lua: -------------------------------------------------------------------------------- 1 | fh = file.open("test.txt") 2 | t = fh:read() 3 | fh:close() 4 | 5 | assert(t,"line 1\nline 2\nline 3\nline 4\nline 5\n") 6 | 7 | os.exit(0) 8 | 9 | -------------------------------------------------------------------------------- /tests/file07.lua: -------------------------------------------------------------------------------- 1 | file.remove("test2.txt") 2 | 3 | fh = file.open("test.txt") 4 | t = fh:read() 5 | fh:close() 6 | 7 | fw = file.open("test2.txt","w") 8 | fw:write(t) 9 | fw:close() 10 | 11 | fh = file.open("test2.txt") 12 | t0 = fh:read() 13 | fh:close() 14 | 15 | assert(t0=="line 1\nline 2\nline 3\nline 4\nline 5\n") 16 | 17 | os.exit(0) 18 | 19 | -------------------------------------------------------------------------------- /tests/test.txt: -------------------------------------------------------------------------------- 1 | line 1 2 | line 2 3 | line 3 4 | line 4 5 | line 5 6 | --------------------------------------------------------------------------------