243 Commits

Author SHA1 Message Date
Martin Preuss
d7e4ee4cca fixed bugs on big systems. 2025-09-08 22:06:09 +02:00
Martin Preuss
5866a55067 use com2wi in S03 2025-09-08 22:05:52 +02:00
Martin Preuss
40282486f6 added com2wi2 (will become com2wi later). 2025-09-08 22:05:28 +02:00
Martin Preuss
2b2c41867f com2w: fixed a bug (on CRC error the buffer is not yet allocated). 2025-09-08 22:04:53 +02:00
Martin Preuss
679865f68b Fixed some bugs. 2025-09-08 22:04:15 +02:00
Martin Preuss
5b459ef5ac Merge branch 'mp-2025_07-improvr_uart_hw2' 2025-09-07 12:56:19 +02:00
Martin Preuss
9685e45861 incremented version number. 2025-09-07 11:50:33 +02:00
Martin Preuss
fbda9cf228 n28 has no motion sensor. 2025-09-07 11:50:21 +02:00
Martin Preuss
552cd5b2ae moved device info files to their respective device folders. 2025-09-07 11:50:11 +02:00
Martin Preuss
3926b01b13 remove old stats from device info files. 2025-09-07 11:49:39 +02:00
Martin Preuss
ffaa27ca24 aqhome-data: limit number of open files. 2025-09-07 11:48:55 +02:00
Martin Preuss
9a1c5799aa decreased verbosity. 2025-09-07 11:48:35 +02:00
Martin Preuss
85e66c352d n26, n27: use slow blinking LED_SIMPLE module instead of LED_ACTIVITY. 2025-09-07 01:44:05 +02:00
Martin Preuss
d1b67a4819 devicestate: add valuename for improved filtering. 2025-09-07 01:43:31 +02:00
Martin Preuss
51d55128a9 Decreased verbosity. 2025-09-07 01:43:13 +02:00
Martin Preuss
52a5078706 added flash instructions for n28 and t04. 2025-09-06 00:43:05 +02:00
Martin Preuss
2d15091b0c avr: updated uartfd0 (copied from uartfd1) 2025-09-06 00:42:49 +02:00
Martin Preuss
81598881d1 uartfd1: fixed typos. 2025-09-06 00:42:20 +02:00
Martin Preuss
4a74a9990f minor fixes. 2025-09-06 00:41:58 +02:00
Martin Preuss
6c1e8a45ff com2wn: only trigger led if LED_ACTIVITY is defined. 2025-09-06 00:41:17 +02:00
Martin Preuss
808a7c4258 com2w: check COM_ACCEPT_ALL_DEST 2025-09-06 00:40:42 +02:00
Martin Preuss
80ca6e0ac5 avr: added t04. 2025-09-06 00:40:09 +02:00
Martin Preuss
0989543b1b avr: accept all dest addresses on routers/forwarders. 2025-09-06 00:39:50 +02:00
Martin Preuss
36195f88e0 r05: added missing defs. 2025-09-06 00:39:08 +02:00
Martin Preuss
3b916facf4 avr: added device n28. 2025-09-06 00:38:43 +02:00
Martin Preuss
9d59bf31e6 avr: reduce number of stats messages. 2025-09-06 00:38:26 +02:00
Martin Preuss
a256c6261c decreased verbosity. 2025-09-06 00:38:08 +02:00
Martin Preuss
21830321f7 dataclient: fixed a typo. 2025-09-06 00:37:57 +02:00
Martin Preuss
eb27d223fb aqhome-tool: added command "devicestate". 2025-09-06 00:37:39 +02:00
Martin Preuss
33cae3ab26 incremented version. 2025-09-06 00:36:54 +02:00
Martin Preuss
1612065d8a uartfd1: add comment. 2025-09-01 23:25:39 +02:00
Martin Preuss
eac37b9927 com2w: increase buffer size. 2025-09-01 23:25:20 +02:00
Martin Preuss
b95a4fa872 avr: more work on com2wn module. 2025-09-01 23:25:02 +02:00
Martin Preuss
81669a5442 avr: first try irq driven com2w interface.
probably doesn't work like that but that's for later.
2025-09-01 23:24:15 +02:00
Martin Preuss
3270a71cb0 com2w: simplified module. 2025-09-01 23:23:30 +02:00
Martin Preuss
5ae7f821e4 s03: more work. 2025-09-01 23:22:57 +02:00
Martin Preuss
224aca5c41 n21: don't send ADC values. 2025-09-01 23:22:21 +02:00
Martin Preuss
9600be78a5 more work on m644p. 2025-09-01 23:21:52 +02:00
Martin Preuss
246d00a220 avr: allow for big flash roms. 2025-09-01 23:21:25 +02:00
Martin Preuss
809439fd26 aqhome-nodes: handle new statistics messages. 2025-09-01 23:20:38 +02:00
Martin Preuss
86741a0e50 t03: use forwarder app. 2025-08-25 14:13:08 +02:00
Martin Preuss
41843cbab9 avr: added forwarder app.
simpler than router app, just for forwarding messages between interfaces.
mainly used by t03.
2025-08-25 14:12:58 +02:00
Martin Preuss
64854506e5 fixed recvstats message handling. 2025-08-25 14:11:57 +02:00
Martin Preuss
463385a296 increment version. 2025-08-25 11:26:19 +02:00
Martin Preuss
b97fc98bc1 t03: use led_activity 2025-08-25 11:26:08 +02:00
Martin Preuss
9b86aea7ed use led_activity. 2025-08-25 11:25:53 +02:00
Martin Preuss
5bfb49d9e1 simplified code, remove unused code. 2025-08-25 11:25:30 +02:00
Martin Preuss
32a0ad5eae added comments, removed unused code. 2025-08-25 11:24:53 +02:00
Martin Preuss
65593f95ad use led_activity module. 2025-08-25 11:23:35 +02:00
Martin Preuss
ec3774c7a5 fixed a bug in field order 2025-08-25 11:22:54 +02:00
Martin Preuss
4bd462ea59 use new led modules. 2025-08-25 10:25:53 +02:00
Martin Preuss
77d3a201f5 improved uartfd1. 2025-08-25 10:24:48 +02:00
Martin Preuss
127525e3ec buffer: gather statistics 2025-08-25 10:23:47 +02:00
Martin Preuss
465e750e3c avr: activate new led modules. 2025-08-25 10:22:50 +02:00
Martin Preuss
6e062d3f60 improve stats reporting
now again use three messages to transmit stats (much more efficient than
sending single values, also more acurate).
2025-08-25 10:22:05 +02:00
Martin Preuss
7efaf720cc com2w: work in COM2W_Run, not in COM2W_Every100ms 2025-08-25 10:20:13 +02:00
Martin Preuss
66ac029a69 avr: introduce two more led modules.
- led_signal: blink 8 status bits (short=0, long=1)
- led_activity: blink for a short period after a trigger, e.g. for network
  activity
2025-08-25 10:19:23 +02:00
Martin Preuss
bae188ddee t03: added firmware using new uart_fd module. 2025-08-23 00:10:01 +02:00
Martin Preuss
87788fa93c avr: wait for free CLK line on error when in flash mode 2025-08-23 00:09:39 +02:00
Martin Preuss
5013bb8e29 avr network app: only respond to ping request if dest address matches. 2025-08-23 00:09:01 +02:00
Martin Preuss
08d420a871 avr: added module uart_fd
This module uses hardware UART of MCUs in full duplex mode.
2025-08-23 00:08:25 +02:00
Martin Preuss
3a7951be16 com2w: added some recovery code, improved protocol.
after sending a byte wait for the same time when sending the last bit as
with the other bits (otherwise the last bit might get lost by slower
devices).
2025-08-20 23:27:17 +02:00
Martin Preuss
6a3f6be8a9 avr: improved timing for sending bytes via com2w interface.
Still quite different among devices but that's just prove that the protocol
works without prior synchronisation.
2025-08-20 20:48:55 +02:00
Martin Preuss
6c3926c1e4 avr: added a note regarding timing. 2025-08-19 23:36:58 +02:00
Martin Preuss
8467408fd8 avr: increased waittimes for com2w (too fast for 1MHz nodes). 2025-08-19 23:36:44 +02:00
Martin Preuss
6bb499740d avr: use 16bit for brightness (actually, ADC only presents 10 bit). 2025-08-19 23:03:43 +02:00
Martin Preuss
fc073696f8 avr: tweaked timing for com2w transport layer. 2025-08-19 23:03:15 +02:00
Martin Preuss
102996f69f avr: switched most devices to com2w interface now. 2025-08-18 18:47:13 +02:00
Martin Preuss
f9d721ac02 removed unused device nodes.
- n00
- n06
- n11
- n12
- n15
- n17
- n18
- n19
- n20
- n22
- n23
- r02
- r04
- x03
2025-08-18 18:46:46 +02:00
Martin Preuss
6bf8f03fbb added node S03. 2025-08-18 18:07:34 +02:00
Martin Preuss
fa404a5fcf increase number of runs to 10 (was: 2) 2025-08-18 18:06:56 +02:00
Martin Preuss
3592a745d3 introduce app "hub" 2025-08-18 18:06:35 +02:00
Martin Preuss
7c01aa255f split wait for 50us (value 50000 doesn't work for 20MHz nodes). 2025-08-18 18:05:12 +02:00
Martin Preuss
d9e7d4df81 com2w*: allow for higher frequencies than 8MHz
to be used with node S03, which is run at 20MHz.
2025-08-18 18:04:30 +02:00
Martin Preuss
9b1badb310 avr: introduce reference counter for network buffers. 2025-08-18 18:03:24 +02:00
Martin Preuss
0dc4a3a952 avr: started adding COM2WN module. 2025-08-04 22:28:49 +02:00
Martin Preuss
32bd3bc2f9 added r06 again. 2025-08-04 22:26:26 +02:00
Martin Preuss
3f2ae9dd03 Revert "started working on mqtt support in aqhome-nodes"
This reverts commit 7fbc616ce4.
2025-08-03 00:56:46 +02:00
Martin Preuss
7fbc616ce4 started working on mqtt support in aqhome-nodes 2025-08-03 00:56:22 +02:00
Martin Preuss
8c13f9fdf7 avr: started adding com2wn module (multiple com2w devices in one) 2025-08-03 00:55:52 +02:00
Martin Preuss
6c5dc21f6a avr: started adding r06 device. 2025-08-03 00:55:21 +02:00
Martin Preuss
eb1392f3fd avr: fixed apidoc. 2025-08-03 00:54:45 +02:00
Martin Preuss
2b25fed2cd com2w1: follow changes in com2w0. 2025-07-21 18:25:17 +02:00
Martin Preuss
66b298d977 guard some changes on network data with CLI. 2025-07-20 23:15:46 +02:00
Martin Preuss
1091ec1dee read CLK and DATA early. 2025-07-20 23:15:22 +02:00
Martin Preuss
d0b5f84316 adapt com2w1 from com2w0 (the latter is proven to work now). 2025-07-20 00:15:13 +02:00
Martin Preuss
ae1e4c3e37 avr: bootloader works with new com2w code. 2025-07-20 00:05:53 +02:00
Martin Preuss
78cbbf334e com2w: alloc buffer after receiving message. receiving works now. 2025-07-19 17:47:52 +02:00
Martin Preuss
c8c12bb892 t03: use router app. 2025-07-19 17:08:39 +02:00
Martin Preuss
ae1853ba62 enable sending messages. 2025-07-19 17:08:19 +02:00
Martin Preuss
e8423ae97f check dest address for ping requests. 2025-07-19 17:07:52 +02:00
Martin Preuss
597504fb08 t03: added test firmware. 2025-07-19 15:02:46 +02:00
Martin Preuss
59a0962420 added com2w0 2025-07-19 15:02:31 +02:00
Martin Preuss
323a5b76be sync test. 2025-07-19 09:48:56 +02:00
Martin Preuss
b7234a6da2 start using new COM module in n27, r05 and t03. 2025-07-19 09:47:23 +02:00
Martin Preuss
4a5ba97b85 increase buffer size. 2025-07-19 09:46:06 +02:00
Martin Preuss
bdd710fc5c avr: started working on new SPI-like COM protocol.
use a clock and a data line to introduce synchronisation into the
protocol to be able to work with the wide range of mcu speeds (no need for
exact timing, no need for exact calibration).
2025-07-19 09:42:02 +02:00
Martin Preuss
535a695c50 limit number of loops inside main_runLoop. 2025-07-12 20:16:34 +02:00
Martin Preuss
357ffe4e17 comonuartX: handle multiple messages in one run.
still there are NOBUF errors...
2025-07-12 20:09:40 +02:00
Martin Preuss
349b4a929a use in instead of inr (SREG is always inside normal io space). 2025-07-12 20:08:54 +02:00
Martin Preuss
a06d245345 r05: disable OWI and ds18b20 modules (no time to handle those), increase buffers 2025-07-12 20:08:30 +02:00
Martin Preuss
83225c453d Use uid to determine initial wait time after re-enum request. 2025-07-12 20:07:33 +02:00
Martin Preuss
fa94190345 fixed message description for FLASH_DATA. 2025-07-12 00:22:18 +02:00
Martin Preuss
68aa3beab8 incremented firmware version. 2025-07-12 00:21:59 +02:00
Martin Preuss
3cd23d5f60 reduce size of net buffers from 32 to 28.
allows us to use 10 buffers in R05.
2025-07-12 00:21:47 +02:00
Martin Preuss
003f53b0b7 added READMEs 2025-07-11 23:25:19 +02:00
Martin Preuss
c1ea4212f2 Moved NET_BUFFERS_SIZE to network/defs.asm
- Changing this value requires changing the routine NET_Buffer_Locate
  so its best to keep both in one module.
- Redefining it in the individual devices doesn't change the code in
  NET_Buffer_Locate
2025-07-11 23:25:10 +02:00
Martin Preuss
3283a38981 Release 0.9.12. 2025-07-11 23:22:09 +02:00
Martin Preuss
3054274da5 Fixed stats value list. 2025-07-11 23:21:50 +02:00
Martin Preuss
2d84198e54 incremented version. 2025-07-07 21:53:59 +02:00
Martin Preuss
f3544f5e93 fixed apidoc. 2025-07-07 21:53:48 +02:00
Martin Preuss
04c02b5e33 fixed a bug. 2025-07-07 21:53:35 +02:00
Martin Preuss
b709b7e624 updated node doc. 2025-07-07 21:45:36 +02:00
Martin Preuss
8c397dd6b2 added missing includes. 2025-07-07 21:45:19 +02:00
Martin Preuss
6e923ce075 improved apidoc. 2025-07-07 21:44:54 +02:00
Martin Preuss
245d44c05d improved "run" code. 2025-07-07 21:44:40 +02:00
Martin Preuss
4f497fc41a removed unneeded code. 2025-07-07 21:43:41 +02:00
Martin Preuss
e32e8e7c13 use Eeprom_WriteByteIfChanged 2025-07-07 21:43:28 +02:00
Martin Preuss
f3020562bf incremented firmware version. 2025-07-07 16:06:31 +02:00
Martin Preuss
fbb710a4e3 added TLV code for EEPROM (not used for now). 2025-07-07 16:06:18 +02:00
Martin Preuss
cbd498150f avr: fully implemented router functionality in network and router app. 2025-07-07 16:05:53 +02:00
Martin Preuss
691ee3c71b incremented firmware version. 2025-07-06 20:22:38 +02:00
Martin Preuss
280d5828bf fixed a possible problem.
don't reboot on broadcast request.
2025-07-06 20:22:19 +02:00
Martin Preuss
6ecc1721b0 r05: no longer use APPS_NETWORK.
Implemented some of those features in APP_ROUTER like PING, REBOOT.
2025-07-06 20:21:51 +02:00
Martin Preuss
1824e8ccdf Increment packets-out counter. 2025-07-06 20:01:35 +02:00
Martin Preuss
0a45e38939 make router functionality of r05 an app. 2025-07-06 18:21:48 +02:00
Martin Preuss
81b008af0c r05, comOnUart0 and comOnUart1 work! 2025-07-06 17:19:59 +02:00
Martin Preuss
439e787d37 added values to device description file for t03. 2025-07-06 14:40:14 +02:00
Martin Preuss
d242d63c2e disable irqs when releasing buffer. 2025-07-06 14:39:48 +02:00
Martin Preuss
86982d0000 reuse code. 2025-07-06 14:39:16 +02:00
Martin Preuss
f549ef5d27 added debug output. 2025-07-06 14:39:02 +02:00
Martin Preuss
fe2eaafb7b add devices r04 and r05 to flash script. 2025-07-06 14:38:43 +02:00
Martin Preuss
caf149fe8b minor changes to unify modules. 2025-07-06 14:38:21 +02:00
Martin Preuss
c540135705 fixed a bug. 2025-07-06 14:37:10 +02:00
Martin Preuss
734c237666 sort counters to simplify stats app. 2025-07-06 14:36:50 +02:00
Martin Preuss
85d445ec61 t03: use comonuart0 from uart_hw2 2025-07-06 14:35:52 +02:00
Martin Preuss
b059f4a56e added node r05. 2025-07-06 14:35:22 +02:00
Martin Preuss
b56ab22117 more work on test firmware for r04 2025-07-06 14:34:55 +02:00
Martin Preuss
5bda393b10 added valueids for stats from 2nd network device. 2025-07-06 12:24:46 +02:00
Martin Preuss
4ad508eca6 n27: removed driver for SGP30 (is now used externally in n26). 2025-07-06 12:23:49 +02:00
Martin Preuss
7f856dacf6 use uart_hw2 now. 2025-07-06 12:22:50 +02:00
Martin Preuss
b6ba56a564 added code to read calibration data. 2025-07-06 12:22:22 +02:00
Martin Preuss
f930b846c2 uart_hw2: basically works, but skips messages. 2025-07-06 12:21:41 +02:00
Martin Preuss
fc5394a5c9 avr: added comonuart0
works fine so far.
2025-07-03 22:12:40 +02:00
Martin Preuss
725ff96425 added test firmware for r04. 2025-07-03 22:11:27 +02:00
Martin Preuss
bfd0cd77a9 comonuart1: fixed bit names, disable IRQ when starting to write. 2025-07-03 00:16:29 +02:00
Martin Preuss
548a7634b7 comonuart1: undid some of the latest changes. 2025-07-02 00:10:46 +02:00
Martin Preuss
3e4637e174 comonuart1: try to solve timing/irq problem. 2025-07-02 00:08:01 +02:00
Martin Preuss
20b7c3f50d avr: new uart_hw2 module comonuart1 works now. 2025-07-01 00:52:44 +02:00
Martin Preuss
9206341032 started working on improved UART_HW module. 2025-06-30 21:29:05 +02:00
Martin Preuss
6383d18e0e avr: added comonuart1. 2025-06-29 22:32:43 +02:00
Martin Preuss
cfc7dc6320 avr: added r04. 2025-06-29 22:31:51 +02:00
Martin Preuss
72acef3aaf Increased max message size. 2025-06-26 00:31:11 +02:00
Martin Preuss
850975a85b avr: fixed a bug (was not calling sysOnEveryDay) 2025-06-26 00:18:33 +02:00
Martin Preuss
68ee246216 aqhome-tool: added some commands
- getFirstData
- getLastData
- getPeriodData
2025-06-26 00:18:05 +02:00
Martin Preuss
2ba802bb06 More work on dataclient library. 2025-06-26 00:16:52 +02:00
Martin Preuss
688129cf08 added n27 to flash script. 2025-06-25 00:09:41 +02:00
Martin Preuss
81721ab7eb avr: added device n27. 2025-06-25 00:05:47 +02:00
Martin Preuss
bf50871208 n25: use pin PA5 to select led strip driver to use
does nothing for now but will later allow to select between SK6812 and SPI.
2025-06-25 00:05:19 +02:00
Martin Preuss
cbf88e05fe added "mode" argument to "getdata" command. 2025-06-25 00:03:58 +02:00
Martin Preuss
a808450fa2 add dataclient sublib to be used by multiple tools. 2025-06-25 00:03:09 +02:00
Martin Preuss
033b7cd4db getvalues: allow for filtering value list on server. 2025-06-25 00:02:32 +02:00
Martin Preuss
cd6a918533 s_getvalues: allow for filtering output list. 2025-06-25 00:01:52 +02:00
Martin Preuss
63ebcbadc9 added msg "getValues" 2025-06-25 00:01:12 +02:00
Martin Preuss
b2802a37aa added AQH_Storage_GetFirstNDataPoints() 2025-06-25 00:00:39 +02:00
Martin Preuss
1e95b317bf incremented firmware version. 2025-06-23 19:28:35 +02:00
Martin Preuss
78fec171f9 sgp30: measure every second, increases accuracy. 2025-06-23 19:28:20 +02:00
Martin Preuss
8bfaabcf27 read 10bit brightness (instead of 8 bit). 2025-06-23 19:27:53 +02:00
Martin Preuss
2b08847cf7 added n26. 2025-06-23 19:21:58 +02:00
Martin Preuss
9ea722607f avr: added brightness sensor 2025-06-23 19:21:49 +02:00
Martin Preuss
409155f0d0 increment version. 2025-06-17 00:14:14 +02:00
Martin Preuss
026a943648 avr: adde stats messages to device definitions. 2025-06-17 00:13:52 +02:00
Martin Preuss
aeb6df5685 avr: reduce number of messages send.
- sensor report interval 60->120s
- stats report interval 11-31 mins
2025-06-17 00:13:33 +02:00
Martin Preuss
06886e0094 aqhome-cgi: more work 2025-06-16 23:31:53 +02:00
Martin Preuss
b498a445b2 added missing files, n26, increased firmware version. 2025-06-16 23:31:38 +02:00
Martin Preuss
876e9ac7b9 avr: added n14 back in. 2025-06-16 23:29:56 +02:00
Martin Preuss
a89b875872 avr: added NET_Interface_ResetStats and call it daily. 2025-06-16 23:29:18 +02:00
Martin Preuss
0ce70e48b1 avr: added type "light" 2025-06-16 23:27:37 +02:00
Martin Preuss
bf583a1aa5 avr: incremented version. 2025-06-15 17:47:45 +02:00
Martin Preuss
1533f82ec4 n21: send value of door sensor every minute
this will allow us to determine the best value for a adc limit to detect
open/closed windows and doors.
2025-06-15 17:47:34 +02:00
Martin Preuss
8a43bc252f tcrt1000: decreased limit from 170 to 100 2025-06-15 17:46:06 +02:00
Martin Preuss
ffcc5c0d9f some minor work on gui/win modules. 2025-06-12 23:30:36 +02:00
Martin Preuss
c51043d72a more work on aqhome-cgi. 2025-06-12 23:30:08 +02:00
Martin Preuss
2cb534df85 more work on GUI module. 2025-06-05 22:48:22 +02:00
Martin Preuss
4f30623f2d increased stacksize. 2025-06-05 22:47:54 +02:00
Martin Preuss
b9ac7c65fa include CCS811 in reportsensors app. 2025-06-05 22:46:55 +02:00
Martin Preuss
cef487fb3a re-enabled node n19. adapted n20. 2025-06-05 22:45:52 +02:00
Martin Preuss
284539fd52 re-enabled CCS811 module, adapted to latest changes. 2025-06-05 22:44:27 +02:00
Martin Preuss
08a1313ba5 More work on layout code. 2025-06-04 23:56:42 +02:00
Martin Preuss
7349014bd6 Make coordinates 16 bit again. 2025-06-04 23:56:29 +02:00
Martin Preuss
12cfe2ff4b Fixed a bug.
Need to make room for stack.
2025-06-04 23:56:04 +02:00
Martin Preuss
ae1892f196 share code. 2025-06-03 00:00:14 +02:00
Martin Preuss
dbad237826 fixed a typo. 2025-06-02 23:41:38 +02:00
Martin Preuss
56e222a97e split WID_SIGNAL_GETMINSIZE into two signals. 2025-06-02 23:38:08 +02:00
Martin Preuss
888792a201 more work on gui code. 2025-06-02 23:29:50 +02:00
Martin Preuss
41867ed01a added missing files. 2025-06-02 23:29:06 +02:00
Martin Preuss
b82e0d02df avr: more general approach to fonts. 2025-06-02 21:13:44 +02:00
Martin Preuss
cd1fce313e avr: added some unit tests for LIST and TREE. 2025-06-02 21:13:01 +02:00
Martin Preuss
5153bd2f69 removed signal "CREATED".
unsure when this would be called:
- when the basic object is called?
- when the last derived object is called?
better remove it to avoid complications...
2025-06-02 01:04:49 +02:00
Martin Preuss
d4ad6844e3 ili9341: save X 2025-06-02 01:03:23 +02:00
Martin Preuss
4f610c68a2 added more fonts and corresponding code. disabled test code in ili9341 module. 2025-06-02 01:03:03 +02:00
Martin Preuss
3582659018 added more bigcall/bigjmp as we go along with larger code. 2025-06-02 01:02:06 +02:00
Martin Preuss
1f537849a9 incremented version. 2025-06-01 22:39:56 +02:00
Martin Preuss
e6d0118ff3 n25: fixed interrupt table (was still for t85!). 2025-06-01 22:38:48 +02:00
Martin Preuss
ff7d47e155 avr: adapted to latest changes. 2025-06-01 22:38:17 +02:00
Martin Preuss
8ae50a8d60 fixed flashnode.sh for n24, added n25 2025-06-01 22:37:46 +02:00
Martin Preuss
08411d9430 NET_IFACE_OFFS_HANDLED_LOW no longer exists. 2025-06-01 22:37:26 +02:00
Martin Preuss
9bd3182bd5 simplified timer and sleep setup code for AtTiny84. 2025-06-01 22:36:59 +02:00
Martin Preuss
c45eb6cca2 fixed blinkled fn: always switch LED port to output. 2025-06-01 22:36:26 +02:00
Martin Preuss
b229b39ab8 c02: started working on AtMEGA 644P based node. 2025-06-01 19:26:31 +02:00
Martin Preuss
87b5e01581 add missing include. 2025-06-01 19:25:57 +02:00
Martin Preuss
8188f33345 uart_bitbang2: introduced macros for ATTN irq setup. 2025-06-01 19:25:47 +02:00
Martin Preuss
7403b6650b n24: include common/calls.asm 2025-06-01 19:25:16 +02:00
Martin Preuss
6bbf2ba788 c01: use UART_BitBang_PcintIsr 2025-06-01 19:24:49 +02:00
Martin Preuss
06b0ed8551 c01: fixed include. 2025-06-01 19:24:13 +02:00
Martin Preuss
ada19028e0 dont use old constant. 2025-06-01 19:23:38 +02:00
Martin Preuss
1e5de0da23 flash: use 16-bit counters. 2025-06-01 19:22:04 +02:00
Martin Preuss
bb14dd4c22 introduce macros bigjmp and bigcall for intermodule calls/jmps
translates to rjmp/rcall on MCUs with up to 8K flash and to jmp/call
on others.
2025-06-01 19:18:25 +02:00
Martin Preuss
188e7da379 incremented firmware version. 2025-06-01 00:21:52 +02:00
Martin Preuss
982c9bd649 re-enable some modules and apps on n20. 2025-06-01 00:21:37 +02:00
Martin Preuss
fec37bd221 disable debug code. 2025-06-01 00:21:21 +02:00
Martin Preuss
120e3e1e6b uart bitbang2 now also works on c01! 2025-06-01 00:21:07 +02:00
Martin Preuss
8d1661d8e4 improved output from "getdevices" command. 2025-06-01 00:20:28 +02:00
Martin Preuss
18f61f4d63 added include for debug functions (commented-out). 2025-05-31 15:37:31 +02:00
Martin Preuss
be74442e7f receiving works again. 2025-05-31 15:36:52 +02:00
Martin Preuss
061119819f sending works again with n20. 2025-05-31 14:20:05 +02:00
Martin Preuss
0b8cb929b7 split uart_bitbang2 into multiple files. 2025-05-30 17:03:35 +02:00
Martin Preuss
f1c858e3a7 Use n20 for bugfixing. 2025-05-30 15:25:42 +02:00
Martin Preuss
3fc7eff424 add n20. 2025-05-29 22:54:25 +02:00
Martin Preuss
36050b14c5 wait for one bitlength between bytes. disable collision detection. 2025-05-29 21:04:30 +02:00
Martin Preuss
fa6acd8e52 added aqua_n25.xml 2025-05-29 20:27:07 +02:00
Martin Preuss
dbf7f76baa moved versions to a dedicated file shared by all nodes. 2025-05-29 20:26:43 +02:00
Martin Preuss
18be337160 minor change 2025-05-29 20:25:49 +02:00
Martin Preuss
0bd6ef8db4 fix defs for n24. 2025-05-29 20:25:34 +02:00
Martin Preuss
7fb1722c70 added missing .include 2025-05-29 20:25:07 +02:00
Martin Preuss
279d92e338 prepared removal of defs_all.asm 2025-05-29 20:24:41 +02:00
Martin Preuss
a26dd6f2a5 added stats vars to n21 2025-05-29 20:24:13 +02:00
Martin Preuss
785e4ef28c added n16 2025-05-29 20:23:53 +02:00
Martin Preuss
dff347bcb7 created pong message. 2025-05-29 20:23:44 +02:00
Martin Preuss
619ac1564e debug: send rxstats every minute. 2025-05-29 20:20:43 +02:00
Martin Preuss
581eeff996 apps/network: implemented ping request. 2025-05-29 15:47:21 +02:00
Martin Preuss
b4e747c3db added header. 2025-05-29 15:44:30 +02:00
Martin Preuss
04dec73988 added device n25 2025-05-29 15:43:33 +02:00
383 changed files with 28707 additions and 11171 deletions

5
0BUILD
View File

@@ -2,7 +2,7 @@
<gwbuild>
<project name="aqhome" version="0.0.10" so_current="0" so_age="0" so_revision="10" write_config_h="TRUE">
<project name="aqhome" version="0.0.14" so_current="0" so_age="0" so_revision="14" write_config_h="TRUE">
<setVar name="package">$(project_name)</setVar>
<setVar name="version">
$(project_vmajor).$(project_vminor).$(project_vpatchlevel)
@@ -51,6 +51,8 @@
<setVar name="pkgincludedir">$(includedir)/aqhome/$(package)</setVar>
<setVar name="pkgdatadir">$(datadir)/$(package)</setVar>
<setVar name="httpdatadir">/var/www</setVar>
<option id="enable_testcode" type="string">
<default>TRUE</default>
<choices>TRUE FALSE</choices>
@@ -126,6 +128,7 @@
</option>
<checkheaders>
signal.h
sys/stat.h

View File

@@ -3,7 +3,7 @@
<gwbuild>
<target type="Program" name="aqhome-cgi" install="$(sbindir)" >
<target type="Program" name="aqhome-cgi" install="$(libdir)/cgi-bin" >
<includes type="c" >
$(gwenhywfar_cflags)
@@ -55,6 +55,7 @@
<libraries>
$(gwenhywfar_libs)
-lm
$(aqcgi_libs)
</libraries>
<subdirs>
@@ -117,6 +118,7 @@
<useTargets>
aqhome
aqhcgi_service
aqhcgi_modules
</useTargets>
<libraries>
@@ -127,6 +129,7 @@
<subdirs>
service
modules
</subdirs>

23
apps/aqhome-cgi/README Normal file
View File

@@ -0,0 +1,23 @@
Modules:
- login
- signup
- main
- devices
- list
- show?name="nodes/12345678"
- add
- edit
- delete
- values
- rooms
- users
- list
- show?alias="admin"
- add
- edit
- delete
- dashboards

View File

@@ -1,11 +1,181 @@
#include "./service_file.h"
#include "aqhome-cgi/modules/mroot.h"
#include "aqhome/aqhome.h"
#include <aqcgi/cgi.h>
#include <aqcgi/request.h>
#include <gwenhywfar/gwenhywfar.h>
#include <gwenhywfar/nogui.h>
#include <gwenhywfar/logger.h>
#include <gwenhywfar/text.h>
#include <gwenhywfar/debug.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#define AQHOME_CGI_LOGFILE "/var/www/aqhome-cgi/log/aqhome-cgi.log"
#define AQHOME_CGI_DEFAULT_STATIC_FILES "0-build/services/static"
#define AQHOME_CGI_DEFAULT_RUNTIME_FILES "0-build/services/runtime"
void _handleRequest(AQCGI_REQUEST *rq, const char *sPathStaticFiles, const char *sPathRuntimeFiles);
int _handlePath(AQH_SERVICE *sv, AQCGI_REQUEST *rq, const char *sPathStaticFiles);
AQH_MODULE *_loadModule(AQH_SERVICE *sv, AQCGI_REQUEST *rq, AQH_MODULE *mParent, const char *sModuleName);
static void logStart(void);
int main(int argc, char **argv)
{
GWEN_GUI *gui;
AQCGI_REQUEST *rq;
GWEN_Init();
gui=GWEN_NoGui_new();
GWEN_Gui_SetGui(gui);
logStart();
GWEN_Logger_Open(GWEN_LOGDOMAIN, "gwenhywfar", AQHOME_CGI_LOGFILE, GWEN_LoggerType_File, GWEN_LoggerFacility_Daemon);
GWEN_Logger_Open(AQH_LOGDOMAIN, "aqhome", AQHOME_CGI_LOGFILE, GWEN_LoggerType_File, GWEN_LoggerFacility_Daemon);
GWEN_Logger_Open(AQCGI_LOGDOMAIN, "aqcgi", AQHOME_CGI_LOGFILE, GWEN_LoggerType_File, GWEN_LoggerFacility_Daemon);
GWEN_Logger_Open(NULL, "aqhome-cgi", AQHOME_CGI_LOGFILE, GWEN_LoggerType_File, GWEN_LoggerFacility_Daemon);
GWEN_Logger_SetLevel(GWEN_LOGDOMAIN, GWEN_LoggerLevel_Debug);
GWEN_Logger_SetLevel(AQCGI_LOGDOMAIN, GWEN_LoggerLevel_Debug);
GWEN_Logger_SetLevel(NULL, GWEN_LoggerLevel_Debug);
DBG_ERROR(NULL, "Init CGI");
AQCGI_Init();
GWEN_Logger_Close(GWEN_LOGDOMAIN);
GWEN_Logger_Open(GWEN_LOGDOMAIN, "gwenhywfar", AQHOME_CGI_LOGFILE, GWEN_LoggerType_File, GWEN_LoggerFacility_Daemon);
GWEN_Logger_SetLevel(GWEN_LOGDOMAIN, GWEN_LoggerLevel_Debug);
GWEN_Logger_SetLevel(AQCGI_LOGDOMAIN, GWEN_LoggerLevel_Debug);
GWEN_Logger_SetLevel(NULL, GWEN_LoggerLevel_Debug);
rq=AQCGI_ReadRequest();
if (rq) {
const char *sPathStaticFiles;
const char *sPathRuntimeFiles;
sPathStaticFiles=getenv("AQHOME_STATIC_FILES");
if (!(sPathStaticFiles && *sPathStaticFiles))
sPathStaticFiles=AQHOME_CGI_DEFAULT_STATIC_FILES;
sPathRuntimeFiles=getenv("AQHOME_RUNTIME_FILES");
if (!(sPathRuntimeFiles && *sPathRuntimeFiles))
sPathRuntimeFiles=AQHOME_CGI_DEFAULT_RUNTIME_FILES;
_handleRequest(rq, sPathStaticFiles, sPathRuntimeFiles);
}
else {
fprintf(stdout, "Content-type: text/plain\n\n");
fprintf(stdout, "Error: No Request!\n");
return 0;
}
AQCGI_Fini();
return 0;
}
void _handleRequest(AQCGI_REQUEST *rq, const char *sPathStaticFiles, const char *sPathRuntimeFiles)
{
AQH_SERVICE *sv;
int rv;
sv=AQH_ServiceFiles_new(sPathRuntimeFiles);
rv=_handlePath(sv, rq, sPathStaticFiles);
if (rv<0) {
}
AQH_Service_free(sv);
}
int _handlePath(AQH_SERVICE *sv, AQCGI_REQUEST *rq, const char *sPathStaticFiles)
{
AQH_MODULE *mRoot;
AQH_MODULE *mParent;
const GWEN_STRINGLIST *sl;
mRoot=AQH_ModRoot_new(sv, sPathStaticFiles);
mParent=mRoot;
sl=AQCGI_Request_GetStringlistPath(rq);
if (sl) {
GWEN_STRINGLISTENTRY *se;
se=GWEN_StringList_FirstEntry(sl);
while(se) {
GWEN_STRINGLISTENTRY *seNext;
const char *s;
seNext=GWEN_StringListEntry_Next(se);
s=GWEN_StringListEntry_Data(se);
if (s && *s) {
if (seNext) {
AQH_MODULE *m;
DBG_ERROR(NULL, "Entry: %s (%s)", s, seNext?"not last":"last");
m=AQH_ModService_LoadSubModule(mParent, rq, s);
if (m==NULL) {
AQH_Module_free(mRoot);
AQCGI_SendResponseWithStatus(rq, 404, "Not found");
return GWEN_ERROR_GENERIC;
}
mParent=m;
}
else {
int rv;
/* last, let module handle remaining part */
rv=AQH_ModService_HandleRequest(mParent, rq, s);
if (rv<0) {
DBG_INFO(NULL, "here (%d)", rv);
AQH_Module_free(mRoot);
return rv;
}
break;
}
}
se=seNext;
}
AQH_Module_free(mRoot);
return 0;
}
else {
AQH_Module_free(mRoot);
return GWEN_ERROR_GENERIC;
}
}
void logStart()
{
FILE *f;
f=fopen(AQHOME_CGI_LOGFILE, "a+");
if (f!=NULL) {
fprintf(f, "Started.\n");
fclose(f);
}
}

View File

@@ -0,0 +1,84 @@
<?xml?>
<gwbuild>
<target type="ConvenienceLibrary" name="aqhcgi_modules" >
<includes type="c" >
$(gwenhywfar_cflags)
-I$(topsrcdir)
-I$(topbuilddir)
-I$(topsrcdir)/apps
-I$(topbuilddir)/apps
-I$(builddir)
-I$(srcdir)
</includes>
<includes type="tm2" >
--include=$(builddir)
--include=$(srcdir)
</includes>
<define name="BUILDING_AQHOME" />
<setVar name="local/cflags">$(visibility_cflags)</setVar>
<setVar name="tm2flags-INACTIVE" >
--api=AQHOME_API
</setVar>
<setVar name="local/typefiles" >
</setVar>
<setVar name="local/built_sources" >
</setVar>
<setVar name="local/built_headers_pub">
</setVar>
<setVar name="local/built_headers_priv" >
</setVar>
<headers dist="false" install="$(pkgincludedir)/service" >
$(local/built_headers_pub)
</headers>
<headers dist="true" install="$(pkgincludedir)/service" >
mservice.h
mroot.h
</headers>
<headers dist="true" >
mservice_p.h
mroot_p.h
</headers>
<sources>
$(local/typefiles)
mservice.c
mroot.c
</sources>
<extradist>
</extradist>
<useTargets>
</useTargets>
<subdirs>
static
</subdirs>
</target>
</gwbuild>

View File

@@ -0,0 +1,246 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
****************************************************************************/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "./mroot_p.h"
#include "aqhome-cgi/service/module.h"
#include <gwenhywfar/debug.h>
#include <gwenhywfar/timestamp.h>
/* ------------------------------------------------------------------------------------------------
* defs and enums
* ------------------------------------------------------------------------------------------------
*/
/* ------------------------------------------------------------------------------------------------
* global vars
* ------------------------------------------------------------------------------------------------
*/
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static AQH_MODULE *_loadSubModule(AQH_MODULE *m, AQCGI_REQUEST *rq, const char *sModuleName);
static int _handleRequest(AQH_MODULE *m, AQCGI_REQUEST *rq, const char *sLastPathElem);
static int _handleRqLogin(AQH_MODULE *m, AQCGI_REQUEST *rq);
static int _handleRqLoginPost(AQH_MODULE *m, AQCGI_REQUEST *rq);
static AQH_USER *_getAndCheckUser(AQH_MODULE *m, AQCGI_REQUEST *rq);
/* ------------------------------------------------------------------------------------------------
* code
* ------------------------------------------------------------------------------------------------
*/
AQH_MODULE *AQH_ModRoot_new(AQH_SERVICE *sv, const char *baseFolder)
{
AQH_MODULE *m;
m=AQH_ModService_new(sv, baseFolder);
AQH_ModService_SetHandleRequestFn(m, _handleRequest);
AQH_ModService_SetLoadSubModuleFn(m, _loadSubModule);
return m;
}
AQH_MODULE *_loadSubModule(AQH_MODULE *m, AQCGI_REQUEST *rq, const char *sModuleName)
{
return NULL;
}
int _handleRequest(AQH_MODULE *m, AQCGI_REQUEST *rq, const char *sLastPathElem)
{
if (strcasecmp(sLastPathElem, "login")==0)
return _handleRqLogin(m, rq);
else if (strcasecmp(sLastPathElem, "signup")==0) {
AQCGI_SendResponseWithStatus(rq, 501, "Not Implemented");
return GWEN_ERROR_NOT_IMPLEMENTED;
}
else if (strcasecmp(sLastPathElem, "confirm")==0) {
AQCGI_SendResponseWithStatus(rq, 501, "Not Implemented");
return GWEN_ERROR_NOT_IMPLEMENTED;
}
else {
AQCGI_SendResponseWithStatus(rq, 404, "Not Found");
return GWEN_ERROR_NOT_IMPLEMENTED;
}
}
int _handleRqLogin(AQH_MODULE *m, AQCGI_REQUEST *rq)
{
int rv;
if (AQCGI_Request_GetRequestMethod(rq)==AQCGI_REQUEST_METHOD_GET)
rv=AQH_ModService_RespondWithFile(m, rq, "en", "login.html");
else if (AQCGI_Request_GetRequestMethod(rq)==AQCGI_REQUEST_METHOD_POST)
rv=_handleRqLoginPost(m, rq);
else {
DBG_ERROR(NULL, "Invalid request method %d", AQCGI_Request_GetRequestMethod(rq));
AQCGI_SendResponseWithStatus(rq, 405, "Method No Allowed");
return GWEN_ERROR_INVALID;
}
if (rv<0) {
DBG_INFO(NULL, "here (%d)", rv);
return rv;
}
return 0;
}
int _handleRqLoginPost(AQH_MODULE *m, AQCGI_REQUEST *rq)
{
AQH_SERVICE *sv;
AQH_USER *user;
AQH_SESSION *session;
GWEN_BUFFER *dbuf;
GWEN_TIMESTAMP *ts;
int rv;
DBG_ERROR(NULL, "Handling request");
sv=AQH_ModService_GetService(m);
user=_getAndCheckUser(m, rq);
if (user==NULL) {
DBG_INFO(NULL, "here");
return GWEN_ERROR_GENERIC;
}
ts=GWEN_Timestamp_NowInLocalTime();
AQH_User_SetTimestampLastLogin(user, ts);
DBG_ERROR(NULL, "Saving user");
rv=AQH_Service_SaveUser(sv, user);
if (rv<0) {
DBG_ERROR(NULL, "Error saving user \"%s\"", AQH_User_GetAlias(user));
AQCGI_SendResponseWithStatus(rq, 500, "Internal Error");
AQH_User_free(user);
return rv;
}
/* generate session */
DBG_ERROR(NULL, "Generating session");
dbuf=GWEN_Buffer_new(0, 64, 0, 1);
AQCGI_GenerateSessionId(dbuf);
session=AQH_Session_new();
AQH_Session_SetTimestampCreation(session, ts);
AQH_Session_SetTimestampLastAccess(session, ts);
AQH_Session_SetUid(session, GWEN_Buffer_GetStart(dbuf));
GWEN_Buffer_free(dbuf);
AQH_Session_SetUserAlias(session, AQH_User_GetAlias(user));
rv=AQH_Service_AddSession(sv, session);
if (rv<0) {
DBG_ERROR(NULL, "Error adding session for user \"%s\"", AQH_User_GetAlias(user));
AQCGI_SendResponseWithStatus(rq, 500, "Internal Error");
AQH_Session_free(session);
AQH_User_free(user);
return GWEN_ERROR_INTERNAL;
}
/* add Set-Cookie header */
dbuf=GWEN_Buffer_new(0, 256, 0, 1);
GWEN_Buffer_AppendArgs(dbuf, "Set-Cookie: session=%s; max-age=3600", AQH_Session_GetUid(session));
AQCGI_Request_AddResponseHeaderData(rq, GWEN_Buffer_GetStart(dbuf));
/* finish */
AQCGI_SendResponseWithStatus(rq, 200, "Ok");
AQH_Session_free(session);
AQH_User_free(user);
return 0;
}
AQH_USER *_getAndCheckUser(AQH_MODULE *m, AQCGI_REQUEST *rq)
{
GWEN_DB_NODE *dbPost;
dbPost=AQCGI_Request_GetDbPostBody(rq);
if (dbPost) {
AQH_SERVICE *sv;
const char *sUserName;
const char *sPasswd;
AQH_USER *user;
const char *hashedPaswd;
GWEN_BUFFER *buf;
sv=AQH_ModService_GetService(m);
sUserName=GWEN_DB_GetCharValue(dbPost, "userid", 0, NULL);
sPasswd=GWEN_DB_GetCharValue(dbPost, "password", 0, NULL);
if (!(sUserName && *sUserName && sPasswd && *sPasswd)) {
DBG_ERROR(NULL, "Either user name or password missing");
AQCGI_SendResponseWithStatus(rq, 400, "Bad Request");
return NULL;
}
DBG_ERROR(NULL, "Loading user \"%s\" (%p)", sUserName, sv);
user=AQH_Service_LoadUser(sv, sUserName);
if (user==NULL) {
DBG_ERROR(NULL, "User \"%s\" not found", sUserName);
AQCGI_SendResponseWithStatus(rq, 403, "Forbidden");
return NULL;
}
DBG_ERROR(NULL, "Loaded user \"%s\"", sUserName);
if (AQH_User_GetState(user)!=AQH_UserState_Active) {
DBG_ERROR(NULL, "User \"%s\" not active", sUserName);
AQCGI_SendResponseWithStatus(rq, 403, "Forbidden");
AQH_User_free(user);
return NULL;
}
hashedPaswd=AQH_User_GetHashedPassword(user);
if (!(hashedPaswd && *hashedPaswd)) {
DBG_ERROR(NULL, "User \"%s\" has no hashed password", sUserName);
AQCGI_SendResponseWithStatus(rq, 403, "Forbidden");
AQH_User_free(user);
return NULL;
}
buf=GWEN_Buffer_new(0, 256, 0, 1);
AQCGI_HashMd256ToBuffer(sPasswd, buf);
DBG_ERROR(NULL, "Hashed password: [%s]", GWEN_Buffer_GetStart(buf));
if (strcasecmp(GWEN_Buffer_GetStart(buf), hashedPaswd)!=0) {
DBG_ERROR(NULL, "Bad password for user \"%s\"", sUserName);
AQCGI_SendResponseWithStatus(rq, 403, "Forbidden");
GWEN_Buffer_free(buf);
AQH_User_free(user);
return NULL;
}
GWEN_Buffer_free(buf);
DBG_ERROR(NULL, "User \"%s\" accepted", sUserName);
return user;
}
else {
DBG_ERROR(NULL, "No POST data");
AQCGI_SendResponseWithStatus(rq, 400, "Bad Request");
return NULL;
}
}

View File

@@ -0,0 +1,27 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
****************************************************************************/
#ifndef AQHOME_CGI_MROOT_H
#define AQHOME_CGI_MROOT_H
#include <aqhome-cgi/modules/mservice.h>
#include <aqcgi/request.h>
#include <gwenhywfar/buffer.h>
AQH_MODULE *AQH_ModRoot_new(AQH_SERVICE *sv, const char *baseFolder);
AQH_MODULE *AQH_ModRoot_LoadSubModule(AQH_MODULE *m, AQCGI_REQUEST *rq, const char *sModuleName);
int AQH_ModRoot_HandleRequest(AQH_MODULE *m, AQCGI_REQUEST *rq, const char *sLastPathElem);
#endif

View File

@@ -0,0 +1,18 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
****************************************************************************/
#ifndef AQHOME_CGI_MROOT_P_H
#define AQHOME_CGI_MROOT_P_H
#include "aqhome-cgi/modules/mroot.h"
#endif

View File

@@ -0,0 +1,291 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
****************************************************************************/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "./mservice_p.h"
#include "aqhome-cgi/service/module.h"
#include <gwenhywfar/debug.h>
/* ------------------------------------------------------------------------------------------------
* defs and enums
* ------------------------------------------------------------------------------------------------
*/
#define AQH_MOD_SERVICE_HEADERFILE "header.html"
#define AQH_MOD_SERVICE_FOOTERFILE "footer.html"
/* ------------------------------------------------------------------------------------------------
* global vars
* ------------------------------------------------------------------------------------------------
*/
GWEN_INHERIT(AQH_MODULE, AQH_MOD_SERVICE)
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static void GWENHYWFAR_CB _freeData(void *bp, void *p);
/* ------------------------------------------------------------------------------------------------
* code
* ------------------------------------------------------------------------------------------------
*/
AQH_MODULE *AQH_ModService_new(AQH_SERVICE *sv, const char *baseFolder)
{
AQH_MODULE *m;
AQH_MOD_SERVICE *xm;
m=AQH_Module_new();
GWEN_NEW_OBJECT(AQH_MOD_SERVICE, xm);
GWEN_INHERIT_SETDATA(AQH_MODULE, AQH_MOD_SERVICE, m, xm, _freeData);
xm->service=sv;
xm->baseFolder=(baseFolder && *baseFolder)?strdup(baseFolder):NULL;
return m;
}
void _freeData(GWEN_UNUSED void *bp, void *p)
{
AQH_MOD_SERVICE *xm;
xm=(AQH_MOD_SERVICE*) p;
free(xm->baseFolder);
GWEN_FREE_OBJECT(xm);
}
AQH_SERVICE *AQH_ModService_GetService(const AQH_MODULE *m)
{
if (m) {
AQH_MOD_SERVICE *xm;
xm=GWEN_INHERIT_GETDATA(AQH_MODULE, AQH_MOD_SERVICE, m);
if (xm) {
return xm->service;
}
}
return NULL;
}
const char *AQH_ModService_GetBaseFolder(const AQH_MODULE *m)
{
if (m) {
AQH_MOD_SERVICE *xm;
xm=GWEN_INHERIT_GETDATA(AQH_MODULE, AQH_MOD_SERVICE, m);
if (xm) {
return xm->baseFolder;
}
}
return NULL;
}
void AQH_ModService_SetHandleRequestFn(AQH_MODULE *m, AQH_MODSERVICE_HANDLEREQUEST_FN fn)
{
if (m) {
AQH_MOD_SERVICE *xm;
xm=GWEN_INHERIT_GETDATA(AQH_MODULE, AQH_MOD_SERVICE, m);
if (xm) {
xm->handleRequestFn=fn;
}
}
}
void AQH_ModService_SetLoadSubModuleFn(AQH_MODULE *m, AQH_MODSERVICE_LOADSUBMODULE_FN fn)
{
if (m) {
AQH_MOD_SERVICE *xm;
xm=GWEN_INHERIT_GETDATA(AQH_MODULE, AQH_MOD_SERVICE, m);
if (xm) {
xm->loadSubModuleFn=fn;
}
}
}
int AQH_ModService_AddHeader(AQH_MODULE *m, const char *lang, GWEN_BUFFER *dbuf)
{
if (m && dbuf) {
AQH_MODULE *mParent;
mParent=AQH_Module_Tree2_GetParent(m);
if (mParent) {
int rv;
rv=AQH_ModService_AddHeader(mParent, lang, dbuf);
if (rv<0) {
DBG_INFO(NULL, "here (%d)", rv);
return rv;
}
}
return AQH_ModService_ReadStaticFile(m, lang, AQH_MOD_SERVICE_HEADERFILE, dbuf);
}
DBG_ERROR(NULL, "Argument missing");
return GWEN_ERROR_INVALID;
}
int AQH_ModService_AddFooter(AQH_MODULE *m, const char *lang, GWEN_BUFFER *dbuf)
{
if (m && dbuf) {
AQH_MODULE *mParent;
int rv;
rv=AQH_ModService_ReadStaticFile(m, lang, AQH_MOD_SERVICE_FOOTERFILE, dbuf);
if (rv<0) {
DBG_INFO(NULL, "here (%d)", rv);
return rv;
}
mParent=AQH_Module_Tree2_GetParent(m);
if (mParent) {
int rv;
rv=AQH_ModService_AddFooter(mParent, lang, dbuf);
if (rv<0) {
DBG_INFO(NULL, "here (%d)", rv);
return rv;
}
}
return 0;
}
else {
DBG_ERROR(NULL, "Argument missing");
return GWEN_ERROR_INVALID;
}
}
int AQH_ModService_RespondWithFile(AQH_MODULE *m, AQCGI_REQUEST *rq, const char *lang, const char *sFilename)
{
GWEN_BUFFER *buf;
int rv;
buf=GWEN_Buffer_new(0, 256, 0, 1);
rv=AQH_ModService_AddHeader(m, lang, buf);
if (rv<0) {
AQCGI_SendResponseWithStatus(rq, 500, "Internal error");
GWEN_Buffer_free(buf);
return GWEN_ERROR_INTERNAL;
}
rv=AQH_ModService_ReadStaticFile(m, lang, sFilename, buf);
if (rv<0) {
AQCGI_SendResponseWithStatus(rq, 500, "Internal error");
GWEN_Buffer_free(buf);
return GWEN_ERROR_INTERNAL;
}
rv=AQH_ModService_AddFooter(m, lang, buf);
if (rv<0) {
AQCGI_SendResponseWithStatus(rq, 500, "Internal error");
GWEN_Buffer_free(buf);
return GWEN_ERROR_INTERNAL;
}
AQCGI_Request_SetBufferResponseBody(rq, buf);
AQCGI_Request_AddResponseHeaderData(rq, "Content-type: text/html");
AQCGI_SendResponseWithStatus(rq, 200, "Ok");
return 0;
}
int AQH_ModService_HandleRequest(AQH_MODULE *m, AQCGI_REQUEST *rq, const char *sLastPathElem)
{
if (m) {
AQH_MOD_SERVICE *xm;
xm=GWEN_INHERIT_GETDATA(AQH_MODULE, AQH_MOD_SERVICE, m);
if (xm && xm->handleRequestFn)
return xm->handleRequestFn(m, rq, sLastPathElem);
}
return GWEN_ERROR_NOT_IMPLEMENTED;
}
AQH_MODULE *AQH_ModService_LoadSubModule(AQH_MODULE *m, AQCGI_REQUEST *rq, const char *sModuleName)
{
if (m) {
AQH_MOD_SERVICE *xm;
xm=GWEN_INHERIT_GETDATA(AQH_MODULE, AQH_MOD_SERVICE, m);
if (xm && xm->loadSubModuleFn)
return xm->loadSubModuleFn(m, rq, sModuleName);
}
return NULL;
}
int AQH_ModService_ReadStaticFile(AQH_MODULE *m, const char *lang, const char *filename, GWEN_BUFFER *dbuf)
{
if (m && filename && dbuf) {
AQH_MOD_SERVICE *xm;
xm=GWEN_INHERIT_GETDATA(AQH_MODULE, AQH_MOD_SERVICE, m);
if (xm) {
GWEN_BUFFER *fbuf;
int rv;
fbuf=GWEN_Buffer_new(0, 256, 0, 1);
GWEN_Buffer_AppendString(fbuf, xm->baseFolder);
GWEN_Buffer_AppendString(fbuf, GWEN_DIR_SEPARATOR_S);
GWEN_Buffer_AppendString(fbuf, (lang && *lang)?lang:"en");
GWEN_Buffer_AppendString(fbuf, GWEN_DIR_SEPARATOR_S);
GWEN_Buffer_AppendString(fbuf, filename);
DBG_ERROR(NULL, "Reading file \"%s\"", GWEN_Buffer_GetStart(fbuf));
rv=GWEN_SyncIo_Helper_ReadFile(GWEN_Buffer_GetStart(fbuf), dbuf);
if (rv<0) {
DBG_ERROR(NULL, "Read(%s): %d", GWEN_Buffer_GetStart(fbuf), rv);
GWEN_Buffer_free(fbuf);
return rv;
}
GWEN_Buffer_free(fbuf);
return 0;
}
}
DBG_ERROR(NULL, "Any arg is missing (or is not a AQH_MOD_SERVICE object)");
return GWEN_ERROR_INTERNAL;
}

View File

@@ -0,0 +1,46 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
****************************************************************************/
#ifndef AQHOME_CGI_MSERVICE_H
#define AQHOME_CGI_MSERVICE_H
#include <aqhome-cgi/service/module.h>
#include <aqhome-cgi/service/service.h>
#include <aqcgi/request.h>
#include <gwenhywfar/buffer.h>
typedef int (*AQH_MODSERVICE_HANDLEREQUEST_FN)(AQH_MODULE *m, AQCGI_REQUEST *rq, const char *sLastPathElem);
typedef AQH_MODULE* (*AQH_MODSERVICE_LOADSUBMODULE_FN)(AQH_MODULE *m, AQCGI_REQUEST *rq, const char *sModuleName);
AQH_MODULE *AQH_ModService_new(AQH_SERVICE *sv, const char *baseFolder);
AQH_SERVICE *AQH_ModService_GetService(const AQH_MODULE *m);
const char *AQH_ModService_GetBaseFolder(const AQH_MODULE *m);
int AQH_ModService_AddHeader(AQH_MODULE *m, const char *lang, GWEN_BUFFER *dbuf);
int AQH_ModService_AddFooter(AQH_MODULE *m, const char *lang, GWEN_BUFFER *dbuf);
AQH_MODULE *AQH_ModService_LoadSubModule(AQH_MODULE *m, AQCGI_REQUEST *rq, const char *sModuleName);
int AQH_ModService_HandleRequest(AQH_MODULE *m, AQCGI_REQUEST *rq, const char *sLastPathElem);
int AQH_ModService_RespondWithFile(AQH_MODULE *m, AQCGI_REQUEST *rq, const char *lang, const char *sFilename);
int AQH_ModService_ReadStaticFile(AQH_MODULE *m, const char *lang, const char *filename, GWEN_BUFFER *dbuf);
void AQH_ModService_SetHandleRequestFn(AQH_MODULE *m, AQH_MODSERVICE_HANDLEREQUEST_FN fn);
void AQH_ModService_SetLoadSubModuleFn(AQH_MODULE *m, AQH_MODSERVICE_LOADSUBMODULE_FN fn);
#endif

View File

@@ -0,0 +1,27 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
****************************************************************************/
#ifndef AQHOME_CGI_MSERVICE_P_H
#define AQHOME_CGI_MSERVICE_P_H
#include "aqhome-cgi/modules/mservice.h"
typedef struct AQH_MOD_SERVICE AQH_MOD_SERVICE;
struct AQH_MOD_SERVICE {
AQH_SERVICE *service;
char *baseFolder;
AQH_MODSERVICE_HANDLEREQUEST_FN handleRequestFn;
AQH_MODSERVICE_LOADSUBMODULE_FN loadSubModuleFn;
};
#endif

View File

@@ -1,11 +1,7 @@
<?xml?>
<gwbuild>
<subdirs>
uart
ccs811
en
</subdirs>
</gwbuild>

View File

@@ -0,0 +1,13 @@
<?xml?>
<gwbuild>
<data dist="true" install="$(httpdatadir)/aqhome-cgi/static/en">
header.html
footer.html
login.html
</data>
</gwbuild>

View File

@@ -0,0 +1,4 @@
</body>
</html>

View File

@@ -0,0 +1,17 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<!-- copyright (c) 2025 by martin@libchipcard.de -->
<meta name="generator" content="FTE 1.1" />
<meta name="revised" content="martin,2025-06-12" />
<meta name="keywords" content="" />
<meta name="description" content="" />
<meta name="author" content="martin" />
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1" />
<title>AqHome</title>
</head>
<body>

View File

@@ -0,0 +1,25 @@
<div class="main">
<h1>AqHome</h1>
<h3>Enter your login credentials</h3>
<form action="login" method="post">
<label for="userid">Username:</label>
<input type="text" id="userid" name="userid" placeholder="Enter your Username" required>
<label for="password">Password:</label>
<input type="password" id="password" name="password" placeholder="Enter your Password" required>
<div class="wrap">
<button type="submit">Submit</button>
</div>
</form>
<p>Not registered?
<a href="signup" style="text-decoration: none;">
Create an account
</a>
</p>
</div>
</body>
</html>

View File

@@ -54,6 +54,10 @@
<descr>Waiting for approval by admin</descr>
</item>
<item name="active">
<descr>User active</descr>
</item>
</enum>
</enums>

View File

@@ -70,6 +70,8 @@ static int _addGroupLocked(AQH_SERVICE *sv, const char *groupName, const char *s
static int _deleteGroup(AQH_SERVICE *sv, const char *groupName, const char *subGroupName);
static GWEN_STRINGLIST *_listGroup(AQH_SERVICE *sv, const char *groupName);
//static void _logGroup(const char *groupName, const char *subGroupName, GWEN_DB_NODE *db);
/* ------------------------------------------------------------------------------------------------
@@ -169,6 +171,9 @@ int _saveUser(AQH_SERVICE *sv, AQH_USER *user)
GWEN_DB_Group_free(db);
return rv;
}
//_logGroup(AQH_SERVICE_FILE_GROUP_USERS, s, db);
rv=_saveGroupLocked(sv, AQH_SERVICE_FILE_GROUP_USERS, s, db);
if (rv<0) {
DBG_ERROR(NULL, "here (%d)", rv);
@@ -414,6 +419,7 @@ GWEN_STRINGLIST *_listSessions(AQH_SERVICE *sv)
GWEN_DB_NODE *_loadGroupLocked(AQH_SERVICE *sv, const char *groupName, const char *subGroupName)
{
DBG_ERROR(NULL, "Lock and load group %s/%s", groupName, subGroupName);
if (sv && groupName && subGroupName) {
AQH_SERVICE_FILE *xs;
@@ -422,17 +428,20 @@ GWEN_DB_NODE *_loadGroupLocked(AQH_SERVICE *sv, const char *groupName, const cha
GWEN_DB_NODE *db=NULL;
int rv;
DBG_ERROR(NULL, "Locking group %s/%s", groupName, subGroupName);
rv=GWEN_ConfigMgr_LockGroup(xs->configMgr, groupName, subGroupName);
if (rv<0) {
DBG_ERROR(NULL, "Error locking group \"%s/%s\": %d", groupName, subGroupName, rv);
return NULL;
}
DBG_ERROR(NULL, "Loading group %s/%s", groupName, subGroupName);
rv=GWEN_ConfigMgr_GetGroup(xs->configMgr, groupName, subGroupName, &db);
if (rv<0) {
DBG_ERROR(NULL, "Error reading group \"%s/%s\": %d", groupName, subGroupName, rv);
GWEN_ConfigMgr_UnlockGroup(xs->configMgr, groupName, subGroupName);
return NULL;
}
DBG_ERROR(NULL, "Unlocking group %s/%s", groupName, subGroupName);
rv=GWEN_ConfigMgr_UnlockGroup(xs->configMgr, groupName, subGroupName);
if (rv<0) {
DBG_ERROR(NULL, "Error unlocking group \"%s/%s\": %d", groupName, subGroupName, rv);
@@ -442,6 +451,9 @@ GWEN_DB_NODE *_loadGroupLocked(AQH_SERVICE *sv, const char *groupName, const cha
return db;
}
}
else {
DBG_ERROR(NULL, "Missing argument");
}
return NULL;
}
@@ -454,25 +466,26 @@ int _saveGroupLocked(AQH_SERVICE *sv, const char *groupName, const char *subGrou
xs=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQH_SERVICE_FILE, sv);
if (xs) {
GWEN_DB_NODE *db=NULL;
int rv;
DBG_ERROR(NULL, "Locking group %s/%s", groupName, subGroupName);
rv=GWEN_ConfigMgr_LockGroup(xs->configMgr, groupName, subGroupName);
if (rv<0) {
DBG_ERROR(NULL, "Error locking group \"%s/%s\": %d", groupName, subGroupName, rv);
return rv;
}
DBG_ERROR(NULL, "Writing group %s/%s", groupName, subGroupName);
rv=GWEN_ConfigMgr_SetGroup(xs->configMgr, groupName, subGroupName, db);
if (rv<0) {
DBG_ERROR(NULL, "Error writing group \"%s/%s\": %d", groupName, subGroupName, rv);
return rv;
}
DBG_ERROR(NULL, "Unlocking group %s/%s", groupName, subGroupName);
rv=GWEN_ConfigMgr_UnlockGroup(xs->configMgr, groupName, subGroupName);
if (rv<0) {
DBG_ERROR(NULL, "Error unlocking group \"%s/%s\": %d", groupName, subGroupName, rv);
GWEN_DB_Group_free(db);
return rv;
}
@@ -491,7 +504,6 @@ int _addGroupLocked(AQH_SERVICE *sv, const char *groupName, const char *subGroup
xs=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQH_SERVICE_FILE, sv);
if (xs) {
GWEN_DB_NODE *db=NULL;
int rv;
rv=GWEN_ConfigMgr_HasGroup(xs->configMgr, groupName, subGroupName);
@@ -515,7 +527,6 @@ int _addGroupLocked(AQH_SERVICE *sv, const char *groupName, const char *subGroup
rv=GWEN_ConfigMgr_UnlockGroup(xs->configMgr, groupName, subGroupName);
if (rv<0) {
DBG_ERROR(NULL, "Error unlocking group \"%s/%s\": %d", groupName, subGroupName, rv);
GWEN_DB_Group_free(db);
return rv;
}
@@ -577,3 +588,24 @@ GWEN_STRINGLIST *_listGroup(AQH_SERVICE *sv, const char *groupName)
}
#if 0
void _logGroup(const char *groupName, const char *subGroupName, GWEN_DB_NODE *db)
{
if (db) {
GWEN_BUFFER *dbuf;
dbuf=GWEN_Buffer_new(0, 256, 0, 1);
GWEN_DB_WriteToBuffer(db, dbuf, GWEN_DB_FLAGS_DEFAULT);
DBG_ERROR(NULL, "Group %s/%s:\n%s", groupName?groupName:"<empty>", subGroupName?subGroupName:"<empty>",
GWEN_Buffer_GetStart(dbuf));
GWEN_Buffer_free(dbuf);
}
else {
DBG_ERROR(NULL, "Group %s/%s empty", groupName?groupName:"<empty>", subGroupName?subGroupName:"<empty>");
}
}
#endif

View File

@@ -13,6 +13,7 @@
#include "aqhome-cgi/service/service.h"
AQH_SERVICE *AQH_ServiceFiles_new(const char *baseFolder);

View File

@@ -1,6 +1,6 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
@@ -33,8 +33,8 @@
* ------------------------------------------------------------------------------------------------
*/
#define AQHOMEDATA_HANDLEGETDATAPOINTS_MAXTABLEENTRIES 2048
#define AQHOMEDATA_HANDLEGETDATAPOINTS_MAXDATAPOINTS 1024
#define AQHOMEDATA_HANDLEGETDATAPOINTS_MAXTABLEENTRIES 1024
#define AQHOMEDATA_HANDLEGETDATAPOINTS_MAXDATAPOINTS 512
/* ------------------------------------------------------------------------------------------------
@@ -44,13 +44,15 @@
static int _getAndSendDataPoints(AQH_STORAGE *storage, AQH_OBJECT *ep,
const AQH_VALUE *value,
int mode,
uint64_t tsBegin, uint64_t tsEnd, uint64_t num, uint32_t refMsgId);
static int _getAndSendDataPointsNoNum(AQH_STORAGE *storage, AQH_OBJECT *ep, const AQH_VALUE *value, uint64_t tsBegin, uint64_t tsEnd,
static int _getAndSendDataPointsPeriod(AQH_STORAGE *storage, AQH_OBJECT *ep, const AQH_VALUE *value,
uint64_t tsBegin, uint64_t tsEnd, uint64_t num,
uint32_t refMsgId);
static int _getAndSendDataPointsWithNum(AQH_STORAGE *storage, AQH_OBJECT *ep, const AQH_VALUE *value, uint64_t num, uint32_t refMsgId);
static int _getAndSendDataPointsFirst(AQH_STORAGE *storage, AQH_OBJECT *ep, const AQH_VALUE *value, uint64_t num, uint32_t refMsgId);
static int _getAndSendDataPointsLast(AQH_STORAGE *storage, AQH_OBJECT *ep, const AQH_VALUE *value, uint64_t num, uint32_t refMsgId);
static void _sendDataPointsResponse(AQH_OBJECT *ep, const AQH_VALUE *value, const uint64_t *tablePtr,
uint32_t refMsgId);
static void _getAndSendLastDatapoint(AQH_STORAGE *storage, AQH_OBJECT *ep, const AQH_VALUE *value, uint32_t refMsgId);
@@ -79,16 +81,18 @@ void AqHomeDataServer_HandleGetDataPoints(AQH_OBJECT *o, AQH_OBJECT *ep, const A
uint64_t tsBegin;
uint64_t tsEnd;
uint64_t numRequested;
int mode;
tsBegin=AQH_Tag16_GetTagDataAsUint64(tagList, AQH_MSGDATA_GETDATA_TAGS_BEGIN, 0);
tsEnd=AQH_Tag16_GetTagDataAsUint64(tagList, AQH_MSGDATA_GETDATA_TAGS_END, 0);
numRequested=AQH_Tag16_GetTagDataAsUint64(tagList, AQH_MSGDATA_GETDATA_TAGS_NUM, 0);
mode=AQH_Tag16_GetTagDataAsUint64(tagList, AQH_MSGDATA_GETDATA_TAGS_MODE, AQH_MSGDATA_GETDATA_MODE_FIRST);
value=AQH_Storage_GetValueByNameForSystem(xo->storage, valueName);
if (value) {
int resultCode;
resultCode=_getAndSendDataPoints(xo->storage, ep, value, tsBegin, tsEnd, numRequested, refMsgId);
resultCode=_getAndSendDataPoints(xo->storage, ep, value, mode, tsBegin, tsEnd, numRequested, refMsgId);
AqHomeDataServer_SendResponseResultToEndpoint(ep, refMsgId, resultCode);
}
else {
@@ -114,30 +118,31 @@ void AqHomeDataServer_HandleGetDataPoints(AQH_OBJECT *o, AQH_OBJECT *ep, const A
int _getAndSendDataPoints(AQH_STORAGE *storage, AQH_OBJECT *ep,
const AQH_VALUE *value,
int mode,
uint64_t tsBegin, uint64_t tsEnd, uint64_t num,
uint32_t refMsgId)
{
if (num==0)
return _getAndSendDataPointsNoNum(storage, ep, value, tsBegin, tsEnd, refMsgId);
else if (num==1) {
_getAndSendLastDatapoint(storage, ep, value, refMsgId);
return AQH_MSGDATA_RESULT_SUCCESS;
switch(mode) {
case AQH_MSGDATA_GETDATA_MODE_FIRST: return _getAndSendDataPointsFirst(storage, ep, value, num, refMsgId);
case AQH_MSGDATA_GETDATA_MODE_PERIOD: return _getAndSendDataPointsPeriod(storage, ep, value, tsBegin, tsEnd, num, refMsgId);
default:
case AQH_MSGDATA_GETDATA_MODE_LAST: return _getAndSendDataPointsLast(storage, ep, value, num, refMsgId);
}
else
return _getAndSendDataPointsWithNum(storage, ep, value, num, refMsgId);
}
int _getAndSendDataPointsNoNum(AQH_STORAGE *storage, AQH_OBJECT *ep,
const AQH_VALUE *value, uint64_t tsBegin, uint64_t tsEnd,
uint32_t refMsgId)
int _getAndSendDataPointsPeriod(AQH_STORAGE *storage, AQH_OBJECT *ep,
const AQH_VALUE *value, uint64_t tsBegin, uint64_t tsEnd, uint64_t num,
uint32_t refMsgId)
{
uint64_t valueId;
uint64_t *tablePtr;
valueId=AQH_Value_GetId(value);
tablePtr=AQH_Storage_GetDataPoints(storage, valueId, tsBegin, tsEnd, AQHOMEDATA_HANDLEGETDATAPOINTS_MAXTABLEENTRIES);
if (num==0 || num>AQHOMEDATA_HANDLEGETDATAPOINTS_MAXTABLEENTRIES)
num=AQHOMEDATA_HANDLEGETDATAPOINTS_MAXTABLEENTRIES;
tablePtr=AQH_Storage_GetDataPoints(storage, valueId, tsBegin, tsEnd, num);
if (tablePtr) {
_sendDataPointsResponse(ep, value, tablePtr, refMsgId);
free(tablePtr);
@@ -151,9 +156,9 @@ int _getAndSendDataPointsNoNum(AQH_STORAGE *storage, AQH_OBJECT *ep,
int _getAndSendDataPointsWithNum(AQH_STORAGE *storage, AQH_OBJECT *ep,
const AQH_VALUE *value, uint64_t num,
uint32_t refMsgId)
int _getAndSendDataPointsLast(AQH_STORAGE *storage, AQH_OBJECT *ep,
const AQH_VALUE *value, uint64_t num,
uint32_t refMsgId)
{
uint64_t valueId;
uint64_t *tablePtr;
@@ -175,6 +180,30 @@ int _getAndSendDataPointsWithNum(AQH_STORAGE *storage, AQH_OBJECT *ep,
int _getAndSendDataPointsFirst(AQH_STORAGE *storage, AQH_OBJECT *ep,
const AQH_VALUE *value, uint64_t num,
uint32_t refMsgId)
{
uint64_t valueId;
uint64_t *tablePtr;
if (num>AQHOMEDATA_HANDLEGETDATAPOINTS_MAXDATAPOINTS)
num=AQHOMEDATA_HANDLEGETDATAPOINTS_MAXDATAPOINTS;
valueId=AQH_Value_GetId(value);
tablePtr=AQH_Storage_GetFirstNDataPoints(storage, valueId, num);
if (tablePtr) {
_sendDataPointsResponse(ep, value, tablePtr, refMsgId);
free(tablePtr);
return AQH_MSGDATA_RESULT_SUCCESS;
}
else {
DBG_INFO(NULL, "No matching datapoints for value \"%s\"", AQH_Value_GetNameForSystem(value));
return AQH_MSGDATA_RESULT_ERROR_NODATA;
}
}
void _sendDataPointsResponse(AQH_OBJECT *ep,
const AQH_VALUE *value, const uint64_t *tablePtr,
uint32_t refMsgId)
@@ -193,36 +222,3 @@ void _sendDataPointsResponse(AQH_OBJECT *ep,
void _getAndSendLastDatapoint(AQH_STORAGE *storage, AQH_OBJECT *ep,
const AQH_VALUE *value, uint32_t refMsgId)
{
int rv;
uint64_t timestamp=0;
double data=0.0;
rv=AQH_Storage_GetLastDataPoint(storage, AQH_Value_GetId(value), &timestamp, &data);
if (rv<0) {
int resultCode;
switch(rv) {
case GWEN_ERROR_INVALID: resultCode=AQH_MSGDATA_RESULT_ERROR_INVALID; break;
case GWEN_ERROR_NO_DATA: resultCode=AQH_MSGDATA_RESULT_ERROR_NODATA; break;
default: resultCode=AQH_MSGDATA_RESULT_ERROR_GENERIC; break;
}
AqHomeDataServer_SendResponseResultToEndpoint(ep, refMsgId, resultCode);
}
else {
AQH_MESSAGE *outMsg;
outMsg=AQH_IpcdMessageMultiData_newForOne(AQH_MSGTYPE_IPC_DATA_GETDATA_RSP,
AQH_Endpoint_GetNextMessageId(ep), refMsgId,
value, timestamp, data);
AQH_Endpoint_AddMsgOut(ep, outMsg);
}
}

View File

@@ -13,13 +13,16 @@
#include "./s_getvalues.h"
#include "./server_p.h"
#include "aqhome/aqhome.h"
#include "aqhome/ipc2/endpoint.h"
#include "aqhome/msg/ipc/m_ipc.h"
#include "aqhome/msg/ipc/data/m_ipcd.h"
#include "aqhome/msg/ipc/data/m_ipcd_getvalues.h"
#include "aqhome/msg/ipc/data/m_ipcd_values.h"
#include "aqhome/msg/ipc/m_ipc_result.h"
#include "aqhome/msg/ipc/m_ipc_tag16.h"
#include <gwenhywfar/text.h>
#include <gwenhywfar/debug.h>
@@ -38,7 +41,10 @@
* ------------------------------------------------------------------------------------------------
*/
static void _sendValueList(AQH_OBJECT *ep, const AQH_VALUE_LIST *vl, uint32_t flags, uint32_t refMsgId);
static AQH_VALUE_LIST *_getMatchingValueList(AQHOME_SERVER *xo, const GWEN_TAG16_LIST *tagList);
static void _sendValueList(AQH_OBJECT *ep, const AQH_VALUE_LIST *vl, uint32_t refMsgId);
static void _sendValueListMsg(AQH_OBJECT *ep, const AQH_VALUE_LIST *vl, uint32_t flags, uint32_t refMsgId);
static int _valueMatches(const AQH_VALUE *v, const char *deviceName, int modality);
@@ -47,64 +53,100 @@ static void _sendValueList(AQH_OBJECT *ep, const AQH_VALUE_LIST *vl, uint32_t fl
* ------------------------------------------------------------------------------------------------
*/
void AqHomeDataServer_HandleGetValues(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg, GWEN_UNUSED const GWEN_TAG16_LIST *tagList)
void AqHomeDataServer_HandleGetValues(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList)
{
AQHOME_SERVER *xo;
xo=AqHomeDataServer_GetServerData(o);
if (xo) {
const AQH_VALUE_LIST *origValueList;
AQH_VALUE_LIST *valueList;
uint32_t refMsgId;
refMsgId=AQH_IpcMessage_GetMsgId(msg);
DBG_INFO(NULL, "HandleGetValues");
origValueList=AQH_Storage_GetValueList(xo->storage);
if (origValueList) {
DBG_INFO(NULL, "Have a list of %d values", AQH_Value_List_GetCount(origValueList));
if (AQH_Value_List_GetCount(origValueList)<AQHOMEDATA_VALUESPERMSG) {
DBG_INFO(NULL, "Sending all entries in one message");
_sendValueList(ep, origValueList, AQH_MSGDATA_VALUES_FLAGS_LASTMSG, refMsgId);
}
else {
AQH_VALUE_LIST *tmpValueList;
const AQH_VALUE *v;
DBG_INFO(NULL, "Sending entries in multiple messages");
tmpValueList=AQH_Value_List_new();
v=AQH_Value_List_First(origValueList);
while(v) {
const AQH_VALUE *next;
AQH_VALUE *copyOfValue;
next=AQH_Value_List_Next(v);
copyOfValue=AQH_Value_dup(v);
AQH_Value_List_Add(copyOfValue, tmpValueList);
if (AQH_Value_List_GetCount(tmpValueList)>=AQHOMEDATA_VALUESPERMSG) {
DBG_INFO(NULL, "Sending %d values", AQH_Value_List_GetCount(tmpValueList));
_sendValueList(ep, tmpValueList, next?0:AQH_MSGDATA_VALUES_FLAGS_LASTMSG, refMsgId);
AQH_Value_List_Clear(tmpValueList);
}
v=next;
}
if (AQH_Value_List_GetCount(tmpValueList)) {
DBG_INFO(NULL, "Sending %d values", AQH_Value_List_GetCount(tmpValueList));
_sendValueList(ep, tmpValueList, AQH_MSGDATA_VALUES_FLAGS_LASTMSG, refMsgId); /* send remaining */
}
AQH_Value_List_free(tmpValueList);
}
valueList=_getMatchingValueList(xo, tagList);
if (valueList) {
_sendValueList(ep, valueList, refMsgId);
AQH_Value_List_free(valueList);
}
else {
/* empty list */
_sendValueList(ep, NULL, AQH_MSGDATA_VALUES_FLAGS_LASTMSG, refMsgId);
_sendValueListMsg(ep, NULL, AQH_MSGDATA_VALUES_FLAGS_LASTMSG, refMsgId);
}
}
}
void _sendValueList(AQH_OBJECT *ep, const AQH_VALUE_LIST *vl, uint32_t flags, uint32_t refMsgId)
AQH_VALUE_LIST *_getMatchingValueList(AQHOME_SERVER *xo, const GWEN_TAG16_LIST *tagList)
{
const AQH_VALUE_LIST *origValueList;
AQH_VALUE_LIST *tmpValueList=NULL;
char *deviceName;
int modality;
deviceName=tagList?AQH_Tag16_GetTagDataAsNewString(tagList, AQH_MSGDATA_GETVALUES_TAGS_DEVICENAME, NULL):NULL;
modality=tagList?AQH_Tag16_GetTagDataAsUint64(tagList, AQH_MSGDATA_GETVALUES_TAGS_MODALITY, 0):0;
origValueList=AQH_Storage_GetValueList(xo->storage);
if (origValueList) {
const AQH_VALUE *v;
tmpValueList=AQH_Value_List_new();
v=AQH_Value_List_First(origValueList);
while(v) {
if (_valueMatches(v, deviceName, modality)) {
AQH_VALUE *copyOfValue;
copyOfValue=AQH_Value_dup(v);
AQH_Value_List_Add(copyOfValue, tmpValueList);
}
v=AQH_Value_List_Next(v);
}
if (AQH_Value_List_GetCount(tmpValueList)<1) {
AQH_Value_List_free(tmpValueList);
tmpValueList=NULL;
}
}
free(deviceName);
return tmpValueList;
}
void _sendValueList(AQH_OBJECT *ep, const AQH_VALUE_LIST *vl, uint32_t refMsgId)
{
AQH_VALUE_LIST *tmpValueList;
const AQH_VALUE *v;
tmpValueList=AQH_Value_List_new();
v=AQH_Value_List_First(vl);
while(v) {
const AQH_VALUE *next;
AQH_VALUE *copyOfValue;
next=AQH_Value_List_Next(v);
copyOfValue=AQH_Value_dup(v);
AQH_Value_List_Add(copyOfValue, tmpValueList);
if (AQH_Value_List_GetCount(tmpValueList)>=AQHOMEDATA_VALUESPERMSG) {
DBG_INFO(NULL, "Sending %d values", AQH_Value_List_GetCount(tmpValueList));
_sendValueListMsg(ep, tmpValueList, next?0:AQH_MSGDATA_VALUES_FLAGS_LASTMSG, refMsgId);
AQH_Value_List_Clear(tmpValueList);
}
v=next;
}
if (AQH_Value_List_GetCount(tmpValueList)) {
DBG_INFO(NULL, "Sending %d values", AQH_Value_List_GetCount(tmpValueList));
_sendValueListMsg(ep, tmpValueList, AQH_MSGDATA_VALUES_FLAGS_LASTMSG, refMsgId); /* send remaining */
}
AQH_Value_List_free(tmpValueList);
}
void _sendValueListMsg(AQH_OBJECT *ep, const AQH_VALUE_LIST *vl, uint32_t flags, uint32_t refMsgId)
{
AQH_MESSAGE *msg;
@@ -115,3 +157,27 @@ void _sendValueList(AQH_OBJECT *ep, const AQH_VALUE_LIST *vl, uint32_t flags, ui
int _valueMatches(const AQH_VALUE *v, const char *deviceName, int modality)
{
if (modality!=AQH_ValueModality_Unknown) {
int valModality;
valModality=AQH_Value_GetModality(v);
if (valModality!=modality)
return 0;
}
if (deviceName && *deviceName) {
const char *s;
s=AQH_Value_GetDeviceNameForSystem(v);
if (s && *s && GWEN_Text_ComparePattern(s, deviceName, 0)==-1)
return 0;
}
return 1;
}

View File

@@ -54,7 +54,7 @@ void AQH_NodeServer_HandleGetNodes(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESS
AQH_Endpoint_GetNextMessageId(ep), AQH_IpcMessage_GetMsgId(msg),
niNext?0:AQH_MSGNODE_GETDEVICES_RSP_FLAGS_LASTMSG, ni);
AQH_Endpoint_AddMsgOut(ep, outMsg);
DBG_ERROR(NULL, "Messages in clients out queue: %d", AQH_Message_List_GetCount(AQH_Endpoint_GetMsgOutList(ep)));
DBG_DEBUG(NULL, "Messages in clients out queue: %d", AQH_Message_List_GetCount(AQH_Endpoint_GetMsgOutList(ep)));
ni=niNext;
}
}

View File

@@ -41,6 +41,7 @@
#include <aqhome/msg/node/m_value.h>
#include <aqhome/msg/node/m_recvstats.h>
#include <aqhome/msg/node/m_sendstats.h>
#include <aqhome/msg/node/m_memstats.h>
#include <aqhome/data/value.h>
#include <gwenhywfar/args.h>
@@ -122,7 +123,9 @@ static void _forwardTtyMsgToBroker(AQH_OBJECT *o, AQH_NODE_SERVER *xo, const AQH
static void _forwardValueMessageToBroker(AQH_OBJECT *o, AQH_NODE_SERVER *xo, const AQH_MESSAGE *msg);
static void _forwardDataFromSendStatsMsgToBroker(AQH_NODE_SERVER *xo, const AQH_MESSAGE *msg);
static void _forwardDataFromRecvStatsMsgToBroker(AQH_NODE_SERVER *xo, const AQH_MESSAGE *msg);
static void _forwardDataFromMemStatsMsgToBroker(AQH_NODE_SERVER *xo, const AQH_MESSAGE *msg);
static void _forwardTtyMsgToClients(AQH_NODE_SERVER *xo, const AQH_MESSAGE *msg);
static void _publishIntWithIdx(AQH_NODE_SERVER *xo, uint32_t uid, const char *vPath, int idx, int vModality, const char *vUnits, int v);
static void _publishInt(AQH_NODE_SERVER *xo, uint32_t uid, const char *vPath, int vModality, const char *vUnits, int v);
static void _publishDouble(AQH_NODE_SERVER *xo, uint32_t uid, const char *vPath, int vModality, const char *vUnits, double v);
static void _setDeviceName(AQH_VALUE *value, uint32_t uid);
@@ -827,9 +830,11 @@ void _forwardTtyMsgToBroker(AQH_OBJECT *o, AQH_NODE_SERVER *xo, const AQH_MESSAG
code=AQH_NodeMessage_GetMsgType(msg);
switch(code) {
case AQH_MSG_TYPE_VALUE_REPORT: _forwardValueMessageToBroker(o, xo, msg); break;
case AQH_MSG_TYPE_VALUE_REPORT: _forwardValueMessageToBroker(o, xo, msg); break;
case AQH_MSG_TYPE_COMSENDSTATS: _forwardDataFromSendStatsMsgToBroker(xo, msg); break;
case AQH_MSG_TYPE_COMRECVSTATS: _forwardDataFromRecvStatsMsgToBroker(xo, msg); break;
case AQH_MSG_TYPE_MEMSTATS: _forwardDataFromMemStatsMsgToBroker(xo, msg); break;
default: break;
}
}
@@ -898,24 +903,21 @@ void _forwardDataFromSendStatsMsgToBroker(AQH_NODE_SERVER *xo, const AQH_MESSAGE
packetsOutInt=AQH_SendStatsMessage_GetPacketsOut(msg);
if (packetsOutInt) {
uint32_t uid;
double packetsOut;
double collisions;
double busy;
double collisionsPercentage=0.0;
double busyPercentage=0.0;
int devNum;
uid=AQH_SendStatsMessage_GetUid(msg);
packetsOut=/*(double)*/ packetsOutInt;
collisions=/*(double)*/ AQH_SendStatsMessage_GetCollisions(msg);
busy=/*(double)*/ AQH_SendStatsMessage_GetBusyErrors(msg);
devNum=AQH_SendStatsMessage_GetInterface(msg);
collisionsPercentage=collisions*100.0/packetsOut;
busyPercentage=busy*100.0/packetsOut;
_publishInt( xo, uid, "net/packetsOut", 0, NULL, packetsOutInt);
_publishInt( xo, uid, "net/collisions", 0, NULL, (int) AQH_SendStatsMessage_GetCollisions(msg));
_publishDouble(xo, uid, "net/collisionsPercent", 0, "%", collisionsPercentage);
_publishDouble(xo, uid, "net/busyPercent", 0, "%", busyPercentage);
if (devNum==0) {
_publishInt(xo, uid, "net/packetsOut", 0, NULL, packetsOutInt);
_publishInt(xo, uid, "net/collisions", 0, NULL, (int) AQH_SendStatsMessage_GetCollisions(msg));
_publishInt(xo, uid, "net/busy", 0, NULL, (int) AQH_SendStatsMessage_GetBusyErrors(msg));
}
else {
_publishIntWithIdx(xo, uid, "net/packetsOut", devNum, 0, NULL, packetsOutInt);
_publishIntWithIdx(xo, uid, "net/collisions", devNum, 0, NULL, (int) AQH_SendStatsMessage_GetCollisions(msg));
_publishIntWithIdx(xo, uid, "net/busy", devNum, 0, NULL, (int) AQH_SendStatsMessage_GetBusyErrors(msg));
}
}
}
@@ -928,30 +930,56 @@ void _forwardDataFromRecvStatsMsgToBroker(AQH_NODE_SERVER *xo, const AQH_MESSAGE
packetsInInt=AQH_RecvStatsMessage_GetPacketsIn(msg);
if (packetsInInt) {
uint32_t uid;
double packetsIn;
double crcErrors;
double ioErrors;
double crcErrorsPercentage=0.0;
double ioErrorsPercentage=0.0;
int devNum;
uid=AQH_SendStatsMessage_GetUid(msg);
packetsIn=/*(double)*/ packetsInInt;
crcErrors=/*(double)*/AQH_RecvStatsMessage_GetCrcErrors(msg);
ioErrors=/*(double)*/AQH_RecvStatsMessage_GetIoErrors(msg);
uid=AQH_RecvStatsMessage_GetUid(msg);
devNum=AQH_RecvStatsMessage_GetInterface(msg);
crcErrorsPercentage=crcErrors*100.0/packetsIn;
ioErrorsPercentage=ioErrors*100.0/packetsIn;
_publishInt( xo, uid, "net/packetsIn", 0, NULL, packetsInInt);
_publishInt( xo, uid, "net/crcerrors", 0, NULL, (int) AQH_RecvStatsMessage_GetCrcErrors(msg));
_publishInt( xo, uid, "net/ioerrors", 0, NULL, (int) AQH_RecvStatsMessage_GetIoErrors(msg));
_publishDouble(xo, uid, "net/crcerrorsPercent", 0, "%", crcErrorsPercentage);
_publishDouble(xo, uid, "net/ioerrorsPercent", 0, "%", ioErrorsPercentage);
if (devNum==0) {
_publishInt(xo, uid, "net/packetsIn", 0, NULL, packetsInInt);
_publishInt(xo, uid, "net/crcErrors", 0, NULL, (int) AQH_RecvStatsMessage_GetCrcErrors(msg));
_publishInt(xo, uid, "net/ioErrors", 0, NULL, (int) AQH_RecvStatsMessage_GetIoErrors(msg));
_publishInt(xo, uid, "net/nobufferErrors", 0, NULL, (int) AQH_RecvStatsMessage_GetNoBufferErrors(msg));
_publishInt(xo, uid, "net/msgSizeErrors", 0, NULL, (int) AQH_RecvStatsMessage_GetMsgSizeErrors(msg));
_publishInt(xo, uid, "net/missed", 0, NULL, (int) AQH_RecvStatsMessage_GetMissed(msg));
}
else {
_publishIntWithIdx(xo, uid, "net/packetsIn", devNum, 0, NULL, packetsInInt);
_publishIntWithIdx(xo, uid, "net/crcErrors", devNum, 0, NULL, (int) AQH_RecvStatsMessage_GetCrcErrors(msg));
_publishIntWithIdx(xo, uid, "net/ioErrors", devNum, 0, NULL, (int) AQH_RecvStatsMessage_GetIoErrors(msg));
_publishIntWithIdx(xo, uid, "net/nobufferErrors", devNum, 0, NULL, (int) AQH_RecvStatsMessage_GetNoBufferErrors(msg));
_publishIntWithIdx(xo, uid, "net/msgSizeErrors", devNum, 0, NULL, (int) AQH_RecvStatsMessage_GetMsgSizeErrors(msg));
_publishIntWithIdx(xo, uid, "net/missed", devNum, 0, NULL, (int) AQH_RecvStatsMessage_GetMissed(msg));
}
}
}
void _forwardDataFromMemStatsMsgToBroker(AQH_NODE_SERVER *xo, const AQH_MESSAGE *msg)
{
uint32_t uid;
uid=AQH_MemStatsMessage_GetUid(msg);
_publishInt( xo, uid, "mem/buffersUsed", 0, NULL, AQH_MemStatsMessage_GetBuffersUsed(msg));
_publishInt( xo, uid, "mem/maxBuffersUsed", 0, NULL, AQH_MemStatsMessage_GetMaxBuffersUsed(msg));
}
void _publishIntWithIdx(AQH_NODE_SERVER *xo, uint32_t uid, const char *vPath, int idx, int vModality, const char *vUnits, int v)
{
GWEN_BUFFER *tbuf;
tbuf=GWEN_Buffer_new(0, 256, 0, 1);
GWEN_Buffer_AppendArgs(tbuf, "%s%d", vPath, idx);
_publishInt(xo, uid, GWEN_Buffer_GetStart(tbuf), vModality, vUnits, v);
GWEN_Buffer_free(tbuf);
}
void _publishInt(AQH_NODE_SERVER *xo, uint32_t uid, const char *vPath, int vModality, const char *vUnits, int v)
{
_publishDouble(xo, uid, vPath, vModality, vUnits, /*(double)*/ v);

View File

@@ -37,9 +37,13 @@
getdevices.h
adddata.h
getdatapoints.h
getfirstdata.h
getlastdata.h
getperioddata.h
setdata.h
moddevice.h
watch.h
devicestate.h
</headers>
<sources>
@@ -49,9 +53,13 @@
getdevices.c
adddata.c
getdatapoints.c
getfirstdata.c
getlastdata.c
getperioddata.c
setdata.c
moddevice.c
watch.c
devicestate.c
</sources>
<useTargets>

View File

@@ -0,0 +1,265 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
****************************************************************************/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "./devicestate.h"
#include "../utils.h"
#include "aqhome/aqhome.h"
#include "aqhome/dataclient/client.h"
#include "aqhome/msg/ipc/m_ipc.h"
#include "aqhome/msg/ipc/m_ipc_result.h"
#include "aqhome/msg/ipc/data/m_ipcd.h"
#include "aqhome/msg/ipc/data/m_ipcd_getdata.h"
#include "aqhome/msg/ipc/data/m_ipcd_multidata.h"
#include "aqhome/dataclient/client.h"
#include <gwenhywfar/args.h>
#include <gwenhywfar/i18n.h>
#include <gwenhywfar/debug.h>
#include <gwenhywfar/text.h>
#include <gwenhywfar/timestamp.h>
#include <ctype.h>
/* ------------------------------------------------------------------------------------------------
* defs
* ------------------------------------------------------------------------------------------------
*/
#define I18S(msg) msg
#define I18N(msg) GWEN_I18N_Translate(PACKAGE, msg)
#define A_ARG GWEN_ARGS_FLAGS_HAS_ARGUMENT
#define A_END (GWEN_ARGS_FLAGS_HELP | GWEN_ARGS_FLAGS_LAST)
#define A_CHAR GWEN_ArgsType_Char
#define A_INT GWEN_ArgsType_Int
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static int _runCommand(AQH_DATACLIENT *dc);
static void _handleDevice(AQH_DATACLIENT *dc, const AQH_DEVICE *device);
static void _handleValue(AQH_DATACLIENT *dc, const AQH_VALUE *value);
static void _printDataPoints(const uint64_t *dataPoints, uint32_t numValues);
static void _printSingleDataPoint(uint64_t timestamp, double data);
/* ------------------------------------------------------------------------------------------------
* code
* ------------------------------------------------------------------------------------------------
*/
int AQH_Tool_DeviceState(GWEN_DB_NODE *dbGlobalArgs, int argc, char **argv)
{
AQH_EVENT_LOOP *eventLoop;
AQH_DATACLIENT *dc;
int rv;
const GWEN_ARGS args[]= {
/* flags type name min max s long short_descr, long_descr */
{ A_ARG, A_CHAR, "brokerAddress", 0, 1, "t", "tcpaddress", I18S("TCP address to connect to [127.0.0.1]"), NULL},
{ A_ARG, A_INT, "brokerPort", 0, 1, "P", "tcpport", I18S("Specify the TCP port to listen on"), NULL},
{ A_ARG, A_INT, "timeout", 0, 1, "T", NULL, I18S("Specify timeout in seconds for response"), NULL},
{ A_ARG, A_CHAR, "brokerClientId", 0, 1, "c", "clientid", I18S("Specify CLIENTID"), NULL},
{ A_ARG, A_CHAR, "userId", 0, 1, "u", "userid", I18S("Specify user id"), NULL},
{ A_ARG, A_CHAR, "password", 0, 1, "p", "password", I18S("Specify service password"), NULL},
{ A_ARG, A_CHAR, "device", 1, 1, "d", "device", I18S("device name"), NULL},
{ A_ARG, A_CHAR, "valueName", 0, 1, "N", "valuename", I18S("Value name for device(e.g. LIGHT)"), NULL},
{ A_ARG, A_INT, "numOfDatapoints", 0, 1, "n", NULL, I18S("Get up to n datapoints"), NULL},
{ A_END, A_INT, "help", 0, 0, "h", "help", I18S("Show this help screen"), NULL}
};
eventLoop=AQH_EventLoop_new();
dc=AQH_DataClient_new(eventLoop, AQH_IPC_PROTOCOL_DATA_ID, AQH_IPC_PROTOCOL_DATA_VERSION);
rv=AQH_DataClient_ReadLocalArgs(dc, dbGlobalArgs, args, argc, argv);
if (rv<0) {
DBG_ERROR(NULL, "here (%d)", rv);
AQH_DataClient_free(dc);
AQH_EventLoop_free(eventLoop);
return 2;
}
rv=AQH_DataClient_ConnectWithArgs(dc, 0);
if (rv<0) {
DBG_ERROR(NULL, "Error connecting (%d)", rv);
AQH_DataClient_free(dc);
AQH_EventLoop_free(eventLoop);
return 2;
}
rv=_runCommand(dc);
if (rv<0) {
DBG_ERROR(NULL, "Error running (%d)", rv);
AQH_DataClient_free(dc);
AQH_EventLoop_free(eventLoop);
return 2;
}
AQH_DataClient_free(dc);
AQH_EventLoop_free(eventLoop);
return 0;
}
int _runCommand(AQH_DATACLIENT *dc)
{
GWEN_DB_NODE *dbLocalArgs;
const char *deviceName;
AQH_DEVICE_LIST *deviceList;
AQH_DEVICE *device;
dbLocalArgs=AQH_DataClient_GetDbLocalArgs(dc);
deviceName=GWEN_DB_GetCharValue(dbLocalArgs, "device", 0, "*");
deviceList=AQH_DataClient_GetDevices(dc);
if (deviceList==NULL) {
DBG_ERROR(NULL, "Error getting devices");
return GWEN_ERROR_GENERIC;
}
device=AQH_Device_List_First(deviceList);
while(device) {
const char *s;
s=AQH_Device_GetNameForSystem(device);
if (s && *s && -1!=GWEN_Text_ComparePattern(s, deviceName, 0)) {
_handleDevice(dc, device);
}
device=AQH_Device_List_Next(device);
}
AQH_Device_List_free(deviceList);
return 0;
}
void _handleDevice(AQH_DATACLIENT *dc, const AQH_DEVICE *device)
{
AQH_VALUE_LIST *valueList;
const char *devName;
const char *roomName;
const char *location;
const char *descr;
devName=AQH_Device_GetNameForSystem(device);
roomName=AQH_Device_GetRoomName(device);
location=AQH_Device_GetLocation(device);
descr=AQH_Device_GetDescription(device);
fprintf(stdout, "%s (room: %s, loc: %s, descr: %s)\n",
devName,
roomName?roomName:"--",
location?location:"--",
descr?descr:"--");
valueList=AQH_DataClient_GetValues(dc, devName, 0);
if (valueList) {
const AQH_VALUE *value;
value=AQH_Value_List_First(valueList);
while(value) {
if (AQH_Value_GetValueType(value)==AQH_ValueType_Sensor)
_handleValue(dc, value);
value=AQH_Value_List_Next(value);
}
}
AQH_Value_List_free(valueList);
}
void _handleValue(AQH_DATACLIENT *dc, const AQH_VALUE *value)
{
GWEN_DB_NODE *dbLocalArgs;
const char *wantedValueName;
const char *valueName;
int numDataPoints;
dbLocalArgs=AQH_DataClient_GetDbLocalArgs(dc);
numDataPoints=GWEN_DB_GetIntValue(dbLocalArgs, "numOfDatapoints", 0, 5);
wantedValueName=GWEN_DB_GetCharValue(dbLocalArgs, "valueName", 0, "*");
valueName=AQH_Value_GetName(value);
if (valueName &&
-1==GWEN_Text_ComparePattern(valueName, "stats_*", 0) &&
-1!=GWEN_Text_ComparePattern(valueName, wantedValueName, 0)) {
const char *valueNameForSystem;
uint64_t *dataPoints;
uint64_t recvdNum;
valueNameForSystem=AQH_Value_GetNameForSystem(value);
fprintf(stdout, " %s: ", valueName?valueName:"<empty>");
dataPoints=malloc(numDataPoints*sizeof(uint64_t)*2);
recvdNum=AQH_DataClient_GetLastData(dc, valueNameForSystem, dataPoints, numDataPoints);
if (recvdNum>0)
_printDataPoints(dataPoints, recvdNum);
free(dataPoints);
fprintf(stdout, "\n");
}
}
void _printDataPoints(const uint64_t *dataPoints, uint32_t numValues)
{
uint32_t i;
for(i=0; i<numValues; i++) {
uint64_t timestamp;
union {double f; uint64_t i;} u;
if (i)
fprintf(stdout, " | ");
timestamp=*(dataPoints++);
u.i=*(dataPoints++);
_printSingleDataPoint(timestamp, u.f);
}
}
void _printSingleDataPoint(uint64_t timestamp, double data)
{
GWEN_TIMESTAMP *ts;
ts=GWEN_Timestamp_fromLocalTime((time_t) timestamp);
if (ts)
fprintf(stdout, "%lf (%04d/%02d/%02d-%02d:%02d:%02d)",
data,
GWEN_Timestamp_GetYear(ts),
GWEN_Timestamp_GetMonth(ts),
GWEN_Timestamp_GetDay(ts),
GWEN_Timestamp_GetHour(ts),
GWEN_Timestamp_GetMinute(ts),
GWEN_Timestamp_GetSecond(ts));
else
fprintf(stdout, "%lf", data);
}

View File

@@ -0,0 +1,21 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
****************************************************************************/
#ifndef AQHOME_TOOL_DEVICESTATE_H
#define AQHOME_TOOL_DEVICESTATE_H
#include <gwenhywfar/db.h>
int AQH_Tool_DeviceState(GWEN_DB_NODE *dbGlobalArgs, int argc, char **argv);
#endif

View File

@@ -10,7 +10,7 @@
# include <config.h>
#endif
#include "./getvalues.h"
#include "./getdatapoints.h"
#include "../client.h"
#include "../utils.h"
@@ -52,7 +52,6 @@
static AQH_MESSAGE *_createRequestMessage(AQH_OBJECT *o, uint32_t msgId);
static int _handleResponseMessage(AQH_OBJECT *o, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList, int first);
static void _handleDataResponse(const GWEN_TAG16_LIST *tagList, int printMean, int printDiff);
static uint64_t _getTimeStampFromString(const char *s);
@@ -110,18 +109,23 @@ AQH_MESSAGE *_createRequestMessage(GWEN_UNUSED AQH_OBJECT *o, uint32_t msgId)
dbArgs=AQH_ToolClient_GetDbLocalArgs(o);
valueName=GWEN_DB_GetCharValue(dbArgs, "valueName", 0, NULL);
num=GWEN_DB_GetIntValue(dbArgs, "numOfLastDatapoints", 0, 0);
tsBegin=_getTimeStampFromString(GWEN_DB_GetCharValue(dbArgs, "tsBegin", 0, NULL));
tsBegin=Utils_GetTimeStampFromString(GWEN_DB_GetCharValue(dbArgs, "tsBegin", 0, NULL));
if (tsBegin==(uint64_t) (-1)) {
DBG_ERROR(NULL, "Bad begin timestamp");
return NULL;
}
tsEnd=_getTimeStampFromString(GWEN_DB_GetCharValue(dbArgs, "tsEnd", 0, NULL));
tsEnd=Utils_GetTimeStampFromString(GWEN_DB_GetCharValue(dbArgs, "tsEnd", 0, NULL));
if (tsEnd==(uint64_t) (-1)) {
DBG_ERROR(NULL, "Bad end timestamp");
return NULL;
}
return AQH_IpcdMessageGetData_new(AQH_MSGTYPE_IPC_DATA_GETDATA_REQ, msgId, 0, valueName, tsBegin, tsEnd, num);
// TODO: use "mode" correctly
return AQH_IpcdMessageGetData_new(AQH_MSGTYPE_IPC_DATA_GETDATA_REQ,
msgId, 0,
AQH_MSGDATA_GETDATA_MODE_LAST,
valueName,
tsBegin, tsEnd, num);
}
@@ -178,45 +182,3 @@ void _handleDataResponse(const GWEN_TAG16_LIST *tagList, int printMean, int prin
uint64_t _getTimeStampFromString(const char *s)
{
if (s && *s) {
if (*s=='-') {
uint64_t x=0;
uint64_t now=time(NULL);
s++;
while(*s && isdigit(*s)) {
unsigned int i;
i=*(s++)-'0';
x*=10;
x+=i;
}
if (*s) {
switch(*s) {
case 0:
case 'm': x*=60; break;
case 'h': x*=(60*60); break;
case 'd': x*=(60*60*24); break;
default: break;
}
}
return (now-x);
}
else {
unsigned long int x;
if (1!=sscanf(s, "%lu", &x)) {
DBG_ERROR(NULL, "ERROR: Invalid timestamp");
return (uint64_t) (-1);
}
return (uint64_t) x;
}
}
return 0;
}

View File

@@ -0,0 +1,153 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
****************************************************************************/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "./getfirstdata.h"
#include "../utils.h"
#include "aqhome/dataclient/client.h"
#include "aqhome/msg/ipc/m_ipc.h"
#include "aqhome/msg/ipc/m_ipc_result.h"
#include "aqhome/msg/ipc/data/m_ipcd.h"
#include "aqhome/msg/ipc/data/m_ipcd_getdata.h"
#include "aqhome/msg/ipc/data/m_ipcd_multidata.h"
#include "aqhome/dataclient/client.h"
#include <gwenhywfar/args.h>
#include <gwenhywfar/i18n.h>
#include <gwenhywfar/debug.h>
#include <gwenhywfar/text.h>
#include <ctype.h>
/* ------------------------------------------------------------------------------------------------
* defs
* ------------------------------------------------------------------------------------------------
*/
#define I18S(msg) msg
#define I18N(msg) GWEN_I18N_Translate(PACKAGE, msg)
#define A_ARG GWEN_ARGS_FLAGS_HAS_ARGUMENT
#define A_END (GWEN_ARGS_FLAGS_HELP | GWEN_ARGS_FLAGS_LAST)
#define A_CHAR GWEN_ArgsType_Char
#define A_INT GWEN_ArgsType_Int
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static int _runCommand(AQH_DATACLIENT *dc);
/* ------------------------------------------------------------------------------------------------
* code
* ------------------------------------------------------------------------------------------------
*/
int AQH_Tool_GetFirstData(GWEN_DB_NODE *dbGlobalArgs, int argc, char **argv)
{
AQH_EVENT_LOOP *eventLoop;
AQH_DATACLIENT *dc;
int rv;
const GWEN_ARGS args[]= {
/* flags type name min max s long short_descr, long_descr */
{ A_ARG, A_CHAR, "brokerAddress", 0, 1, "t", "tcpaddress", I18S("TCP address to connect to [127.0.0.1]"), NULL},
{ A_ARG, A_INT, "brokerPort", 0, 1, "P", "tcpport", I18S("Specify the TCP port to listen on"), NULL},
{ A_ARG, A_INT, "timeout", 0, 1, "T", NULL, I18S("Specify timeout in seconds for response"), NULL},
{ A_ARG, A_CHAR, "brokerClientId", 0, 1, "c", "clientid", I18S("Specify CLIENTID"), NULL},
{ A_ARG, A_CHAR, "userId", 0, 1, "u", "userid", I18S("Specify user id"), NULL},
{ A_ARG, A_CHAR, "password", 0, 1, "p", "password", I18S("Specify service password"), NULL},
{ A_ARG, A_CHAR, "valueName", 1, 1, "N", "valuename", I18S("Value name (e.g. server/temp/system)"), NULL},
{ A_ARG, A_INT, "numOfDatapoints", 0, 1, "n", NULL, I18S("Get up to n datapoints"), NULL},
{ 0, A_INT, "printMean", 0, 1, "M", "mean", I18S("Print mean value of data received"), NULL},
{ 0, A_INT, "printDiff", 0, 1, "D", "diff", I18S("Print diff last-first value"), NULL},
{ A_END, A_INT, "help", 0, 0, "h", "help", I18S("Show this help screen"), NULL}
};
eventLoop=AQH_EventLoop_new();
dc=AQH_DataClient_new(eventLoop, AQH_IPC_PROTOCOL_DATA_ID, AQH_IPC_PROTOCOL_DATA_VERSION);
rv=AQH_DataClient_ReadLocalArgs(dc, dbGlobalArgs, args, argc, argv);
if (rv<0) {
DBG_ERROR(NULL, "here (%d)", rv);
AQH_DataClient_free(dc);
AQH_EventLoop_free(eventLoop);
return 2;
}
rv=AQH_DataClient_ConnectWithArgs(dc, 0);
if (rv<0) {
DBG_ERROR(NULL, "Error connecting (%d)", rv);
AQH_DataClient_free(dc);
AQH_EventLoop_free(eventLoop);
return 2;
}
rv=_runCommand(dc);
if (rv<0) {
DBG_ERROR(NULL, "Error running (%d)", rv);
AQH_DataClient_free(dc);
AQH_EventLoop_free(eventLoop);
return 2;
}
AQH_DataClient_free(dc);
AQH_EventLoop_free(eventLoop);
return 0;
}
int _runCommand(AQH_DATACLIENT *dc)
{
GWEN_DB_NODE *dbLocalArgs;
const char *valueName;
uint64_t num;
int printMean;
int printDiff;
dbLocalArgs=AQH_DataClient_GetDbLocalArgs(dc);
valueName=GWEN_DB_GetCharValue(dbLocalArgs, "valueName", 0, NULL);
num=GWEN_DB_GetIntValue(dbLocalArgs, "numOfDatapoints", 0, 1);
printMean=GWEN_DB_GetIntValue(dbLocalArgs, "printMean", 0, 0);
printDiff=GWEN_DB_GetIntValue(dbLocalArgs, "printDiff", 0, 0);
if (num>0) {
uint64_t *dataPoints;
uint64_t recvdNum;
dataPoints=malloc(num*sizeof(uint64_t)*2);
recvdNum=AQH_DataClient_GetFirstData(dc, valueName, dataPoints, num);
if (recvdNum>0) {
if (printMean)
Utils_PrintMeanData(dataPoints, recvdNum, NULL);
else if (printDiff)
Utils_PrintDiffData(dataPoints, recvdNum, NULL);
else
Utils_PrintDataPoints(dataPoints, recvdNum, NULL);
}
free(dataPoints);
}
return 0;
}

View File

@@ -0,0 +1,21 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
****************************************************************************/
#ifndef AQHOME_TOOL_GETFIRSTDATA_H
#define AQHOME_TOOL_GETFIRSTDATA_H
#include <gwenhywfar/db.h>
int AQH_Tool_GetFirstData(GWEN_DB_NODE *dbGlobalArgs, int argc, char **argv);
#endif

View File

@@ -0,0 +1,153 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
****************************************************************************/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "./getlastdata.h"
#include "../utils.h"
#include "aqhome/dataclient/client.h"
#include "aqhome/msg/ipc/m_ipc.h"
#include "aqhome/msg/ipc/m_ipc_result.h"
#include "aqhome/msg/ipc/data/m_ipcd.h"
#include "aqhome/msg/ipc/data/m_ipcd_getdata.h"
#include "aqhome/msg/ipc/data/m_ipcd_multidata.h"
#include "aqhome/dataclient/client.h"
#include <gwenhywfar/args.h>
#include <gwenhywfar/i18n.h>
#include <gwenhywfar/debug.h>
#include <gwenhywfar/text.h>
#include <ctype.h>
/* ------------------------------------------------------------------------------------------------
* defs
* ------------------------------------------------------------------------------------------------
*/
#define I18S(msg) msg
#define I18N(msg) GWEN_I18N_Translate(PACKAGE, msg)
#define A_ARG GWEN_ARGS_FLAGS_HAS_ARGUMENT
#define A_END (GWEN_ARGS_FLAGS_HELP | GWEN_ARGS_FLAGS_LAST)
#define A_CHAR GWEN_ArgsType_Char
#define A_INT GWEN_ArgsType_Int
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static int _runCommand(AQH_DATACLIENT *dc);
/* ------------------------------------------------------------------------------------------------
* code
* ------------------------------------------------------------------------------------------------
*/
int AQH_Tool_GetLastData(GWEN_DB_NODE *dbGlobalArgs, int argc, char **argv)
{
AQH_EVENT_LOOP *eventLoop;
AQH_DATACLIENT *dc;
int rv;
const GWEN_ARGS args[]= {
/* flags type name min max s long short_descr, long_descr */
{ A_ARG, A_CHAR, "brokerAddress", 0, 1, "t", "tcpaddress", I18S("TCP address to connect to [127.0.0.1]"), NULL},
{ A_ARG, A_INT, "brokerPort", 0, 1, "P", "tcpport", I18S("Specify the TCP port to listen on"), NULL},
{ A_ARG, A_INT, "timeout", 0, 1, "T", NULL, I18S("Specify timeout in seconds for response"), NULL},
{ A_ARG, A_CHAR, "brokerClientId", 0, 1, "c", "clientid", I18S("Specify CLIENTID"), NULL},
{ A_ARG, A_CHAR, "userId", 0, 1, "u", "userid", I18S("Specify user id"), NULL},
{ A_ARG, A_CHAR, "password", 0, 1, "p", "password", I18S("Specify service password"), NULL},
{ A_ARG, A_CHAR, "valueName", 1, 1, "N", "valuename", I18S("Value name (e.g. server/temp/system)"), NULL},
{ A_ARG, A_INT, "numOfDatapoints", 0, 1, "n", NULL, I18S("Get up to n datapoints"), NULL},
{ 0, A_INT, "printMean", 0, 1, "M", "mean", I18S("Print mean value of data received"), NULL},
{ 0, A_INT, "printDiff", 0, 1, "D", "diff", I18S("Print diff last-first value"), NULL},
{ A_END, A_INT, "help", 0, 0, "h", "help", I18S("Show this help screen"), NULL}
};
eventLoop=AQH_EventLoop_new();
dc=AQH_DataClient_new(eventLoop, AQH_IPC_PROTOCOL_DATA_ID, AQH_IPC_PROTOCOL_DATA_VERSION);
rv=AQH_DataClient_ReadLocalArgs(dc, dbGlobalArgs, args, argc, argv);
if (rv<0) {
DBG_ERROR(NULL, "here (%d)", rv);
AQH_DataClient_free(dc);
AQH_EventLoop_free(eventLoop);
return 2;
}
rv=AQH_DataClient_ConnectWithArgs(dc, 0);
if (rv<0) {
DBG_ERROR(NULL, "Error connecting (%d)", rv);
AQH_DataClient_free(dc);
AQH_EventLoop_free(eventLoop);
return 2;
}
rv=_runCommand(dc);
if (rv<0) {
DBG_ERROR(NULL, "Error running (%d)", rv);
AQH_DataClient_free(dc);
AQH_EventLoop_free(eventLoop);
return 2;
}
AQH_DataClient_free(dc);
AQH_EventLoop_free(eventLoop);
return 0;
}
int _runCommand(AQH_DATACLIENT *dc)
{
GWEN_DB_NODE *dbLocalArgs;
const char *valueName;
uint64_t num;
int printMean;
int printDiff;
dbLocalArgs=AQH_DataClient_GetDbLocalArgs(dc);
valueName=GWEN_DB_GetCharValue(dbLocalArgs, "valueName", 0, NULL);
num=GWEN_DB_GetIntValue(dbLocalArgs, "numOfDatapoints", 0, 1);
printMean=GWEN_DB_GetIntValue(dbLocalArgs, "printMean", 0, 0);
printDiff=GWEN_DB_GetIntValue(dbLocalArgs, "printDiff", 0, 0);
if (num>0) {
uint64_t *dataPoints;
uint64_t recvdNum;
dataPoints=malloc(num*sizeof(uint64_t)*2);
recvdNum=AQH_DataClient_GetLastData(dc, valueName, dataPoints, num);
if (recvdNum>0) {
if (printMean)
Utils_PrintMeanData(dataPoints, recvdNum, NULL);
else if (printDiff)
Utils_PrintDiffData(dataPoints, recvdNum, NULL);
else
Utils_PrintDataPoints(dataPoints, recvdNum, NULL);
}
free(dataPoints);
}
return 0;
}

View File

@@ -0,0 +1,21 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
****************************************************************************/
#ifndef AQHOME_TOOL_GETLASTDATA_H
#define AQHOME_TOOL_GETLASTDATA_H
#include <gwenhywfar/db.h>
int AQH_Tool_GetLastData(GWEN_DB_NODE *dbGlobalArgs, int argc, char **argv);
#endif

View File

@@ -0,0 +1,167 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
****************************************************************************/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "./getperioddata.h"
#include "../utils.h"
#include "aqhome/dataclient/client.h"
#include "aqhome/msg/ipc/m_ipc.h"
#include "aqhome/msg/ipc/m_ipc_result.h"
#include "aqhome/msg/ipc/data/m_ipcd.h"
#include "aqhome/msg/ipc/data/m_ipcd_getdata.h"
#include "aqhome/msg/ipc/data/m_ipcd_multidata.h"
#include "aqhome/dataclient/client.h"
#include <gwenhywfar/args.h>
#include <gwenhywfar/i18n.h>
#include <gwenhywfar/debug.h>
#include <gwenhywfar/text.h>
#include <ctype.h>
/* ------------------------------------------------------------------------------------------------
* defs
* ------------------------------------------------------------------------------------------------
*/
#define I18S(msg) msg
#define I18N(msg) GWEN_I18N_Translate(PACKAGE, msg)
#define A_ARG GWEN_ARGS_FLAGS_HAS_ARGUMENT
#define A_END (GWEN_ARGS_FLAGS_HELP | GWEN_ARGS_FLAGS_LAST)
#define A_CHAR GWEN_ArgsType_Char
#define A_INT GWEN_ArgsType_Int
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static int _runCommand(AQH_DATACLIENT *dc);
/* ------------------------------------------------------------------------------------------------
* code
* ------------------------------------------------------------------------------------------------
*/
int AQH_Tool_GetPeriodData(GWEN_DB_NODE *dbGlobalArgs, int argc, char **argv)
{
AQH_EVENT_LOOP *eventLoop;
AQH_DATACLIENT *dc;
int rv;
const GWEN_ARGS args[]= {
/* flags type name min max s long short_descr, long_descr */
{ A_ARG, A_CHAR, "brokerAddress", 0, 1, "t", "tcpaddress", I18S("TCP address to connect to [127.0.0.1]"), NULL},
{ A_ARG, A_INT, "brokerPort", 0, 1, "P", "tcpport", I18S("Specify the TCP port to listen on"), NULL},
{ A_ARG, A_INT, "timeout", 0, 1, "T", NULL, I18S("Specify timeout in seconds for response"), NULL},
{ A_ARG, A_CHAR, "brokerClientId", 0, 1, "c", "clientid", I18S("Specify CLIENTID"), NULL},
{ A_ARG, A_CHAR, "userId", 0, 1, "u", "userid", I18S("Specify user id"), NULL},
{ A_ARG, A_CHAR, "password", 0, 1, "p", "password", I18S("Specify service password"), NULL},
{ A_ARG, A_CHAR, "valueName", 1, 1, "N", "valuename", I18S("Value name (e.g. server/temp/system)"), NULL},
{ A_ARG, A_INT, "numOfDatapoints", 0, 1, "n", NULL, I18S("Get up to n datapoints"), NULL},
{ A_ARG, A_CHAR, "tsBegin", 0, 1, "tb", "tsbegin", I18S("Timestamp range begin"), NULL},
{ A_ARG, A_CHAR, "tsEnd", 0, 1, "te", "tsend", I18S("Timestamp range end"), NULL},
{ 0, A_INT, "printMean", 0, 1, "M", "mean", I18S("Print mean value of data received"), NULL},
{ 0, A_INT, "printDiff", 0, 1, "D", "diff", I18S("Print diff last-first value"), NULL},
{ A_END, A_INT, "help", 0, 0, "h", "help", I18S("Show this help screen"), NULL}
};
eventLoop=AQH_EventLoop_new();
dc=AQH_DataClient_new(eventLoop, AQH_IPC_PROTOCOL_DATA_ID, AQH_IPC_PROTOCOL_DATA_VERSION);
rv=AQH_DataClient_ReadLocalArgs(dc, dbGlobalArgs, args, argc, argv);
if (rv<0) {
DBG_ERROR(NULL, "here (%d)", rv);
AQH_DataClient_free(dc);
AQH_EventLoop_free(eventLoop);
return 2;
}
rv=AQH_DataClient_ConnectWithArgs(dc, 0);
if (rv<0) {
DBG_ERROR(NULL, "Error connecting (%d)", rv);
AQH_DataClient_free(dc);
AQH_EventLoop_free(eventLoop);
return 2;
}
rv=_runCommand(dc);
if (rv<0) {
DBG_ERROR(NULL, "Error running (%d)", rv);
AQH_DataClient_free(dc);
AQH_EventLoop_free(eventLoop);
return 2;
}
AQH_DataClient_free(dc);
AQH_EventLoop_free(eventLoop);
return 0;
}
int _runCommand(AQH_DATACLIENT *dc)
{
GWEN_DB_NODE *dbLocalArgs;
const char *valueName;
uint64_t num;
uint64_t tsBegin;
uint64_t tsEnd;
int printMean;
int printDiff;
dbLocalArgs=AQH_DataClient_GetDbLocalArgs(dc);
valueName=GWEN_DB_GetCharValue(dbLocalArgs, "valueName", 0, NULL);
num=GWEN_DB_GetIntValue(dbLocalArgs, "numOfDatapoints", 0, 1);
tsBegin=Utils_GetTimeStampFromString(GWEN_DB_GetCharValue(dbLocalArgs, "tsBegin", 0, NULL));
if (tsBegin==(uint64_t) (-1)) {
DBG_ERROR(NULL, "Bad begin timestamp");
return 1;
}
tsEnd=Utils_GetTimeStampFromString(GWEN_DB_GetCharValue(dbLocalArgs, "tsEnd", 0, NULL));
if (tsEnd==(uint64_t) (-1)) {
DBG_ERROR(NULL, "Bad end timestamp");
return 1;
}
printMean=GWEN_DB_GetIntValue(dbLocalArgs, "printMean", 0, 0);
printDiff=GWEN_DB_GetIntValue(dbLocalArgs, "printDiff", 0, 0);
if (num>0) {
uint64_t *dataPoints;
uint64_t recvdNum;
dataPoints=malloc(num*sizeof(uint64_t)*2);
recvdNum=AQH_DataClient_GetPeriodData(dc, valueName, dataPoints, num, tsBegin, tsEnd);
if (recvdNum>0) {
if (printMean)
Utils_PrintMeanData(dataPoints, recvdNum, NULL);
else if (printDiff)
Utils_PrintDiffData(dataPoints, recvdNum, NULL);
else
Utils_PrintDataPoints(dataPoints, recvdNum, NULL);
}
free(dataPoints);
}
return 0;
}

View File

@@ -0,0 +1,21 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
****************************************************************************/
#ifndef AQHOME_TOOL_GETPERIODDATA_H
#define AQHOME_TOOL_GETPERIODDATA_H
#include <gwenhywfar/db.h>
int AQH_Tool_GetPeriodData(GWEN_DB_NODE *dbGlobalArgs, int argc, char **argv);
#endif

View File

@@ -14,10 +14,12 @@
#include "../client.h"
#include "../utils.h"
#include "aqhome/aqhome.h"
#include "aqhome/msg/ipc/m_ipc.h"
#include "aqhome/msg/ipc/m_ipc_result.h"
#include "aqhome/msg/ipc/data/m_ipcd.h"
#include "aqhome/msg/ipc/data/m_ipcd_values.h"
#include "aqhome/msg/ipc/data/m_ipcd_getvalues.h"
#include <gwenhywfar/args.h>
#include <gwenhywfar/i18n.h>
@@ -67,6 +69,8 @@ int AQH_Tool_GetValues(GWEN_DB_NODE *dbGlobalArgs, int argc, char **argv)
{ A_ARG, A_INT, "timeout", 0, 1, "T", NULL, I18S("Specify timeout in seconds for response"), NULL},
{ A_ARG, A_CHAR, "brokerClientId", 0, 1, "c", "clientid", I18S("Specify CLIENTID"), NULL},
{ A_ARG, A_CHAR, "userId", 0, 1, "u", "userid", I18S("Specify user id"), NULL},
{ A_ARG, A_CHAR, "device", 0, 1, "d", "device", I18S("device name to match"), NULL},
{ A_ARG, A_CHAR, "modality", 0, 1, "m", NULL, I18S("Modality to match"), NULL},
{ A_ARG, A_CHAR, "password", 0, 1, "p", "password", I18S("Specify service password"), NULL},
{ 0, A_INT, "printHeader", 0, 1, "H", "printheader", I18S("Print header if given"), NULL},
{ A_END, A_INT, "help", 0, 0, "h", "help", I18S("Show this help screen"), NULL}
@@ -90,11 +94,23 @@ int AQH_Tool_GetValues(GWEN_DB_NODE *dbGlobalArgs, int argc, char **argv)
AQH_MESSAGE *_createRequestMessage(GWEN_UNUSED AQH_OBJECT *o, uint32_t msgId)
{
return AQH_IpcMessage_new(AQH_IPC_PROTOCOL_DATA_ID,
AQH_IPC_PROTOCOL_DATA_VERSION,
AQH_MSGTYPE_IPC_DATA_GETVALUES_REQ,
msgId, 0,
0, NULL);
GWEN_DB_NODE *dbArgs;
const char *deviceName;
const char *s;
int modality;
dbArgs=AQH_ToolClient_GetDbLocalArgs(o);
deviceName=GWEN_DB_GetCharValue(dbArgs, "device", 0, NULL);
s=GWEN_DB_GetCharValue(dbArgs, "modality", 0, NULL);
if (s && *s) {
modality=AQH_ValueModality_fromString(s);
if (modality==AQH_ValueModality_Unknown) {
}
}
else
modality=AQH_ValueModality_Unknown;
return AQH_IpcdMessageGetValues_new(AQH_MSGTYPE_IPC_DATA_GETVALUES_REQ, msgId, 0, deviceName, modality);
}

View File

@@ -1,6 +1,6 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
@@ -17,9 +17,13 @@
#include "./data/getdevices.h"
#include "./data/adddata.h"
#include "./data/getdatapoints.h"
#include "./data/getfirstdata.h"
#include "./data/getlastdata.h"
#include "./data/getperioddata.h"
#include "./data/setdata.h"
#include "./data/moddevice.h"
#include "./data/watch.h"
#include "./data/devicestate.h"
#include <aqhome/api.h>
#include <aqhome/aqhome.h>
@@ -92,9 +96,13 @@ int main(int argc, char **argv)
GWEN_FE_DAH("adddata", AQH_Tool_AddDataPoint, I18N("Send a datapoint to the data server")),
GWEN_FE_DAH("addjsondata", AQH_Tool_AddDataPoint, I18N("(same as adddata)")),
GWEN_FE_DAH("getdata", AQH_Tool_GetDataPoints, I18N("Request list of datapoints for a value on the data server")),
GWEN_FE_DAH("getfirstdata", AQH_Tool_GetFirstData, I18N("Request first datapoints for a value on the data server")),
GWEN_FE_DAH("getlastdata", AQH_Tool_GetLastData, I18N("Request last datapoints for a value on the data server")),
GWEN_FE_DAH("getperioddata", AQH_Tool_GetPeriodData, I18N("Request datapoints from a date range for a value on the data server")),
GWEN_FE_DAH("setdata", AQH_Tool_SetData, I18N("Set data for a value on the data server (e.g. a switch or thermostat)")),
GWEN_FE_DAH("moddevice", AQH_Tool_ModDevice, I18N("Modify a device on the data server")),
GWEN_FE_DAH("watch", AQH_Tool_Watch, I18N("Watch and print changes of values on the data server")),
GWEN_FE_DAH("devicestate", AQH_Tool_DeviceState, I18N("Show state of devices")),
GWEN_FE_END(),
};
const GWEN_FUNCS *func;

View File

@@ -58,6 +58,7 @@ static AQH_MESSAGE *_createRequestMessage(AQH_OBJECT *o, uint32_t msgId);
static int _handleResponseMessage(AQH_OBJECT *o, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList, int first);
static void _printNode(const AQH_NODE_INFO *ni, int printAll);
static void _printUintAsTextOrHex(uint32_t u, int bits);
static void _printDeviceIdAsTextOrHex(uint32_t u, int version);
@@ -163,9 +164,7 @@ void _printNode(const AQH_NODE_INFO *ni, int printAll)
_printUintAsTextOrHex(u, 32);
fprintf(stdout, ":");
u=AQH_NodeInfo_GetDeviceType(ni);
_printUintAsTextOrHex(u, 16);
u=AQH_NodeInfo_GetDeviceVersion(ni);
fprintf(stdout, " v%d.%d", (u>>8) & 0xff, u & 0xff);
_printDeviceIdAsTextOrHex(u, AQH_NodeInfo_GetDeviceVersion(ni));
u=AQH_NodeInfo_GetFirmwareVersion(ni);
fprintf(stdout, ", firmware=%d.%d.%d (%d), ",
(u>>16) & 0xff, (u>>8) & 0xff, u & 0xff, (u>>24) & 0xff);
@@ -189,6 +188,36 @@ void _printNode(const AQH_NODE_INFO *ni, int printAll)
void _printDeviceIdAsTextOrHex(uint32_t u, int version)
{
int i;
uint8_t d;
int hasNonPrintable=0;
int hasPrintable=0;
for (i=0; i<16; i+=8) {
d=((u>>i) & 0xff);
if (d==0) { /* undecided */
}
else if (isalnum(d))
hasPrintable=1;
else
hasNonPrintable=1;
}
if (hasNonPrintable || !hasPrintable)
fprintf(stdout, "%02x v%d.%d", u, (version>>8) & 0xff, version & 0xff);
else {
for (i=0; i<16; i+=8) {
d=((u>>i) & 0xff);
fprintf(stdout, "%c", d?d:' ');
}
fprintf(stdout, "%2d (%d)", (version>>8) & 0xff, version & 0xff);
}
}
void _printUintAsTextOrHex(uint32_t u, int bits)
{
int i;

View File

@@ -19,6 +19,7 @@
#include "aqhome/msg/ipc/nodes/m_ipcn_setaccmsggrps.h"
#include "aqhome/ipc2/tcp_object.h"
#include "aqhome/ipc2/ipc_client.h"
#include "aqhome/dataclient/client.h"
#include <gwenhywfar/debug.h>
@@ -27,6 +28,7 @@
#include <time.h>
#include <unistd.h>
#include <ctype.h>
#define UTILS_IPC_ENDPOINT_DEFAULT_MSGSIZE 4096
@@ -361,4 +363,47 @@ void Utils_PrintValue(const AQH_VALUE *value, int printHeader)
uint64_t Utils_GetTimeStampFromString(const char *s)
{
if (s && *s) {
if (*s=='-') {
uint64_t x=0;
uint64_t now=time(NULL);
s++;
while(*s && isdigit(*s)) {
unsigned int i;
i=*(s++)-'0';
x*=10;
x+=i;
}
if (*s) {
switch(*s) {
case 0:
case 'm': x*=60; break;
case 'h': x*=(60*60); break;
case 'd': x*=(60*60*24); break;
default: break;
}
}
return (now-x);
}
else {
unsigned long int x;
if (1!=sscanf(s, "%lu", &x)) {
DBG_ERROR(NULL, "ERROR: Invalid timestamp");
return (uint64_t) (-1);
}
return (uint64_t) x;
}
}
return 0;
}

View File

@@ -41,6 +41,8 @@ void Utils_PrintValue(const AQH_VALUE *value, int printHeader);
AQH_DEVICE *Utils_DeviceFromArgs(GWEN_DB_NODE *dbArgs);
uint64_t Utils_GetTimeStampFromString(const char *s);
#endif

14
aqhome-cgi.sh Normal file
View File

@@ -0,0 +1,14 @@
#!/bin/bash
# AQHOME_LOGLEVEL=info LD_LIBRARY_PATH="../../aqhome/:$LD_LIBRARY_PATH" ./aqhomed -l aqhome.log -db aqhome.db -ma 127.0.0.1
# export AQHOME_LOGLEVEL=info
export LD_LIBRARY_PATH="0-build/aqhome/:$LD_LIBRARY_PATH"
# aqhomed -l /var/log/aqhome.log -db /var/cache/aqhome/nodes.db -ma 192.168.117.192 -mp 1883 -t 127.0.0.1 -W /var/cache/aqhome
# 0-build/apps/aqhomed/aqhomed -l aqhome.log -db aqhome.db -ma 192.168.117.192 -mp 1883 -t 127.0.0.1 -p aqhomed.pid
# 0-build/apps/aqhomed/aqhomed -l aqhome.log -db aqhome.db -p aqhomed.pid -W /tmp/aqhome/aqhomed -ma 192.168.117.192 -mp 1883 -t 127.0.0.1 --mqttclientid=AQHOMEMQTTLOGTEST1
0-build/apps/aqhome-cgi/aqhome-cgi
#0-build/apps/aqhome-nodes/aqhome-nodes -l aqhome-nodes.log -db aqhome-nodes.db -p aqhome-nodes.pid -t 127.0.0.1 -ba 127.0.0.1 "$@"

View File

@@ -69,6 +69,7 @@
hexfile
data
events2
dataclient
</subdirs>
@@ -79,6 +80,7 @@
aqhhexfile
aqhdata
aqhevents2
aqhdataclient
</useTargets>
<libraries>

View File

@@ -267,6 +267,8 @@ int AQH_ValueModality_fromString(const char *s)
return AQH_ValueModality_TVOC;
else if (strcasecmp(s, "stats")==0)
return AQH_ValueModality_Stats;
else if (strcasecmp(s, "light")==0)
return AQH_ValueModality_Light;
}
return AQH_ValueModality_Unknown;
}
@@ -285,6 +287,7 @@ const char *AQH_ValueModality_toString(int i)
case AQH_ValueModality_Co2: return "co2";
case AQH_ValueModality_TVOC: return "tvoc";
case AQH_ValueModality_Stats: return "stats";
case AQH_ValueModality_Light: return "light";
case AQH_ValueModality_Unknown:
default: return "unknown";
}

View File

@@ -45,7 +45,8 @@ enum {
AQH_ValueModality_Motion,
AQH_ValueModality_Co2,
AQH_ValueModality_TVOC,
AQH_ValueModality_Stats
AQH_ValueModality_Stats,
AQH_ValueModality_Light
};

View File

@@ -23,7 +23,7 @@
#define AQH_STORAGE_DATAPOINTS_STEPS 128
#define AQH_STORAGE_MAXOPENFILES 128
/* ------------------------------------------------------------------------------------------------
@@ -305,6 +305,17 @@ AQH_STORAGE_GETLASTDATAPOINT_FN AQH_Storage_SetGetLastDatapointFn(AQH_STORAGE *s
AQH_STORAGE_GETFIRSTNDATAPOINTS_FN AQH_Storage_SetGetFirstNDatapointsFn(AQH_STORAGE *sto, AQH_STORAGE_GETFIRSTNDATAPOINTS_FN fn)
{
AQH_STORAGE_GETFIRSTNDATAPOINTS_FN oldFn;
oldFn=sto->getFirstNDatapointsFn;
sto->getFirstNDatapointsFn=fn;
return oldFn;
}
AQH_STORAGE_GETLASTNDATAPOINTS_FN AQH_Storage_SetGetLastNDatapointsFn(AQH_STORAGE *sto, AQH_STORAGE_GETLASTNDATAPOINTS_FN fn)
{
AQH_STORAGE_GETLASTNDATAPOINTS_FN oldFn;
@@ -383,8 +394,8 @@ int AQH_Storage_AddDatapoint(AQH_STORAGE *sto, uint64_t valueId, uint64_t timest
}
uint64_t *AQH_Storage_GetDataPoints(AQH_STORAGE *sto, uint64_t valueId, uint64_t fromTime, uint64_t toTime, uint64_t maxArrayLen)
uint64_t *AQH_Storage_GetDataPoints(AQH_STORAGE *sto, uint64_t valueId, uint64_t fromTime, uint64_t toTime,
uint64_t maxDataPointsRequested)
{
AQH_DATAFILE *df;
uint64_t numEntries;
@@ -399,13 +410,9 @@ uint64_t *AQH_Storage_GetDataPoints(AQH_STORAGE *sto, uint64_t valueId, uint64_t
return NULL;
}
numEntries=AQH_DataFile_GetNumberOfEntries(df);
if (fromTime==0 && toTime==0)
arrayLen=(numEntries*2)+1;
else
arrayLen=(AQH_STORAGE_DATAPOINTS_STEPS*2)+1;
if (arrayLen>maxArrayLen+1)
arrayLen=maxArrayLen+1;
if (maxDataPointsRequested>numEntries)
maxDataPointsRequested=numEntries;
arrayLen=(maxDataPointsRequested*2)+1;
arrayPtr=(uint64_t*) malloc(arrayLen*sizeof(uint64_t));
if (arrayPtr==NULL) {
DBG_ERROR(AQH_LOGDOMAIN, "Not enough memory for %lu entries", (unsigned long int) arrayLen);
@@ -427,30 +434,11 @@ uint64_t *AQH_Storage_GetDataPoints(AQH_STORAGE *sto, uint64_t valueId, uint64_t
}
if ((fromTime==0 || ts>=fromTime) && (toTime==0 || ts<=toTime)) {
if ((arrayPos+1)>maxArrayLen) {
if ((arrayPos+1)>arrayLen) {
DBG_INFO(AQH_LOGDOMAIN, "Limit for number of returned entries reached");
break;
}
if (arrayPos+1>=arrayLen) {
uint64_t newArrayLen;
void *p;
newArrayLen=arrayLen+(AQH_STORAGE_DATAPOINTS_STEPS*2);
if (newArrayLen>maxArrayLen+1)
newArrayLen=maxArrayLen+1;
if (newArrayLen==arrayLen) {
DBG_INFO(AQH_LOGDOMAIN, "Limit for number of returned entries reached");
break;
}
p=realloc((void*) arrayPtr, newArrayLen*sizeof(uint64_t));
if (p==NULL) {
DBG_ERROR(AQH_LOGDOMAIN, "Not enough memory for %lu entries", (unsigned long int) arrayLen+AQH_STORAGE_DATAPOINTS_STEPS);
free(arrayPtr);
return NULL;
}
arrayPtr=(uint64_t*) p;
arrayLen=newArrayLen;
}
arrayPtr[arrayPos++]=ts;
arrayPtr[arrayPos++]=u.i;
}
@@ -468,6 +456,67 @@ uint64_t *AQH_Storage_GetDataPoints(AQH_STORAGE *sto, uint64_t valueId, uint64_t
uint64_t *AQH_Storage_GetFirstNDataPoints(AQH_STORAGE *sto, uint64_t valueId, uint64_t maxDataPointsRequested)
{
AQH_DATAFILE *df;
uint64_t numEntries;
uint64_t numOfDataEntries;
uint64_t arrayLen;
uint64_t arrayPos;
uint64_t *arrayPtr;
uint64_t firstRecord;
uint64_t i;
df=_getDataFileByValueId(sto, valueId);
if (df==NULL) {
DBG_ERROR(AQH_LOGDOMAIN, "No file for value id %lu", (unsigned long int) valueId);
return NULL;
}
numEntries=AQH_DataFile_GetNumberOfEntries(df);
numOfDataEntries=numEntries-1; /* first entry is reserved, don't count it here */
if (numOfDataEntries<1) {
DBG_INFO(AQH_LOGDOMAIN, "No data records for value id %lu", (unsigned long int) valueId);
return NULL;
}
firstRecord=1;
if (numOfDataEntries>maxDataPointsRequested) /* more entries in file than requested */
arrayLen=(maxDataPointsRequested*2)+1; /* +1 because the first array entry contains the number of entries */
else
arrayLen=(numOfDataEntries*2)+1;
arrayPtr=(uint64_t*) malloc(arrayLen*sizeof(uint64_t));
if (arrayPtr==NULL) {
DBG_ERROR(AQH_LOGDOMAIN, "Not enough memory for %lu entries", (unsigned long int) arrayLen);
return NULL;
}
arrayPos=1;
for (i=firstRecord; i<numEntries; i++) {
union {double f; uint64_t i;} u;
uint64_t ts;
int rv;
rv=AQH_DataFile_ReadRecord(df, i, &ts, &(u.f));
if (rv<0) {
DBG_ERROR(AQH_LOGDOMAIN, "here (%d)", rv);
free(arrayPtr);
return NULL;
}
if ((arrayPos+1)>=arrayLen) {
DBG_INFO(AQH_LOGDOMAIN, "Requested number of entries reached");
break;
}
arrayPtr[arrayPos++]=ts;
arrayPtr[arrayPos++]=u.i;
} /* for */
arrayPtr[0]=arrayPos-1;
return arrayPtr;
}
uint64_t *AQH_Storage_GetLastNDataPoints(AQH_STORAGE *sto, uint64_t valueId, uint64_t maxDataPointsRequested)
{
AQH_DATAFILE *df;
@@ -601,6 +650,16 @@ AQH_DATAFILE *_getDataFileByValueId(AQH_STORAGE *sto, uint64_t valueId)
DBG_ERROR(AQH_LOGDOMAIN, "Error opening/creating datafile for valueId \"%lu\"", (unsigned long int) valueId);
return NULL;
}
if (AQH_DataFile_List_GetCount(sto->dataFileList)>=AQH_STORAGE_MAXOPENFILES) {
AQH_DATAFILE *dfLast;
dfLast=AQH_DataFile_List_Last(sto->dataFileList);
if (dfLast) {
AQH_DataFile_Close(dfLast);
AQH_DataFile_List_Del(dfLast);
AQH_DataFile_free(dfLast);
}
}
DBG_DEBUG(AQH_LOGDOMAIN, "Adding datafile for valueId \"%lu\" to list", (unsigned long int) valueId);
AQH_DataFile_List_Add(df, sto->dataFileList);
}
@@ -612,8 +671,17 @@ AQH_DATAFILE *_getDataFileByValueId(AQH_STORAGE *sto, uint64_t valueId)
AQH_DATAFILE *_findDataFileByValueId(const AQH_STORAGE *sto, uint64_t valueId)
{
if (sto && sto->dataFileList)
return AQH_DataFile_List_GetByValueId(sto->dataFileList, valueId);
if (sto && sto->dataFileList) {
AQH_DATAFILE *df;
df=AQH_DataFile_List_GetByValueId(sto->dataFileList, valueId);
if (df) {
/* move to front of list */
AQH_DataFile_List_Del(df);
AQH_DataFile_List_Insert(df, sto->dataFileList);
return df;
}
}
return NULL;
}

View File

@@ -53,6 +53,8 @@ typedef uint64_t *(*AQH_STORAGE_GETDATAPOINTS_FN)(AQH_STORAGE *sto, uint64_t val
uint64_t maxArrayLen);
typedef int (*AQH_STORAGE_GETFIRSTDATAPOINT_FN)(AQH_STORAGE *sto, uint64_t valueId, uint64_t *pTimestamp, double *pValue);
typedef int (*AQH_STORAGE_GETLASTDATAPOINT_FN)(AQH_STORAGE *sto, uint64_t valueId, uint64_t *pTimestamp, double *pValue);
typedef uint64_t *(*AQH_STORAGE_GETFIRSTNDATAPOINTS_FN)(AQH_STORAGE *sto, uint64_t valueId, uint64_t maxDataPointsRequested);
typedef uint64_t *(*AQH_STORAGE_GETLASTNDATAPOINTS_FN)(AQH_STORAGE *sto, uint64_t valueId, uint64_t maxDataPointsRequested);
@@ -96,6 +98,8 @@ AQHOME_API uint64_t *AQH_Storage_GetDataPoints(AQH_STORAGE *sto, uint64_t valueI
uint64_t maxArrayLen);
AQHOME_API int AQH_Storage_GetFirstDataPoint(AQH_STORAGE *sto, uint64_t valueId, uint64_t *pTimestamp, double *pValue);
AQHOME_API int AQH_Storage_GetLastDataPoint(AQH_STORAGE *sto, uint64_t valueId, uint64_t *pTimestamp, double *pValue);
AQHOME_API uint64_t *AQH_Storage_GetFirstNDataPoints(AQH_STORAGE *sto, uint64_t valueId, uint64_t maxDataPointsRequested);
AQHOME_API uint64_t *AQH_Storage_GetLastNDataPoints(AQH_STORAGE *sto, uint64_t valueId, uint64_t maxDataPointsRequested);
@@ -105,6 +109,8 @@ AQHOME_API AQH_STORAGE_ADDDATAPOINT_FN AQH_Storage_SetAddDatapointFn(AQH_STORAGE
AQHOME_API AQH_STORAGE_GETDATAPOINTS_FN AQH_Storage_SetGetDatapointsFn(AQH_STORAGE *sto, AQH_STORAGE_GETDATAPOINTS_FN fn);
AQHOME_API AQH_STORAGE_GETFIRSTDATAPOINT_FN AQH_Storage_SetGetFirstDatapointFn(AQH_STORAGE *sto, AQH_STORAGE_GETFIRSTDATAPOINT_FN fn);
AQHOME_API AQH_STORAGE_GETLASTDATAPOINT_FN AQH_Storage_SetGetLastDatapointFn(AQH_STORAGE *sto, AQH_STORAGE_GETLASTDATAPOINT_FN fn);
AQHOME_API AQH_STORAGE_GETFIRSTNDATAPOINTS_FN AQH_Storage_SetGetFirstNDatapointsFn(AQH_STORAGE *sto, AQH_STORAGE_GETFIRSTNDATAPOINTS_FN fn);
AQHOME_API AQH_STORAGE_GETLASTNDATAPOINTS_FN AQH_Storage_SetGetLastNDatapointsFn(AQH_STORAGE *sto, AQH_STORAGE_GETLASTNDATAPOINTS_FN fn);

View File

@@ -47,6 +47,7 @@ struct AQH_STORAGE {
AQH_STORAGE_GETFIRSTDATAPOINT_FN getFirstDatapointFn;
AQH_STORAGE_GETLASTDATAPOINT_FN getLastDatapointFn;
AQH_STORAGE_GETLASTNDATAPOINTS_FN getLastNDatapointsFn;
AQH_STORAGE_GETFIRSTNDATAPOINTS_FN getFirstNDatapointsFn;
};

81
aqhome/dataclient/0BUILD Normal file
View File

@@ -0,0 +1,81 @@
<?xml?>
<gwbuild>
<target type="ConvenienceLibrary" name="aqhdataclient" >
<includes type="c" >
$(gwenhywfar_cflags)
$(aqdatabase_cflags)
-I$(topsrcdir)
-I$(topbuilddir)
</includes>
<includes type="tm2" >
--include=$(builddir)
--include=$(srcdir)
--include=$(aqdatabase_AQDATABASE_TYPEMAKERDIR)/c
</includes>
<define name="BUILDING_AQHOME" />
<setVar name="local/cflags">$(visibility_cflags)</setVar>
<setVar name="tm2flags" >
--api=AQHOME_API
</setVar>
<setVar name="local/typefiles" >
</setVar>
<setVar name="local/built_sources" >
</setVar>
<setVar name="local/built_headers_pub">
</setVar>
<setVar name="local/built_headers_priv" >
</setVar>
<headers dist="false" install="$(pkgincludedir)/dataclient" >
$(local/built_headers_pub)
</headers>
<headers dist="true" install="$(pkgincludedir)/dataclient" >
client.h
</headers>
<headers dist="true" >
client_p.h
</headers>
<sources>
$(local/typefiles)
client.c
</sources>
<extradist>
</extradist>
<useTargets>
</useTargets>
<subdirs>
</subdirs>
</target>
</gwbuild>

555
aqhome/dataclient/client.c Normal file
View File

@@ -0,0 +1,555 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
****************************************************************************/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "./client_p.h"
#include "aqhome/aqhome.h"
#include "aqhome/msg/ipc/m_ipc.h"
#include "aqhome/msg/ipc/m_ipc_tag16.h"
#include "aqhome/msg/ipc/m_ipc_result.h"
#include "aqhome/msg/ipc/data/m_ipcd.h"
#include "aqhome/msg/ipc/nodes/m_ipcn.h"
#include "aqhome/msg/ipc/m_ipc_connect.h"
#include "aqhome/msg/ipc/data/m_ipcd_devices.h"
#include "aqhome/msg/ipc/data/m_ipcd_values.h"
#include "aqhome/msg/ipc/data/m_ipcd_getvalues.h"
#include "aqhome/msg/ipc/data/m_ipcd_getdata.h"
#include "aqhome/msg/ipc/data/m_ipcd_multidata.h"
#include "aqhome/msg/ipc/data/m_ipcd_setdata.h"
#include "aqhome/ipc2/tcp_object.h"
#include "aqhome/ipc2/ipc_client.h"
#include <aqhome/ipc2/ipc_endpoint.h>
#include <gwenhywfar/debug.h>
#include <gwenhywfar/timestamp.h>
#include <gwenhywfar/db.h>
#include <gwenhywfar/i18n.h>
#include <gwenhywfar/text.h>
#define AQH_DATA_CLIENT_DEFAULT_CMD_TIMEOUT 5
static int _connectEndpoint(AQH_DATACLIENT *dc, const char *addr, int port, uint32_t flags);
static int _exchangeConnectMsgs(AQH_DATACLIENT *dc, const char *userId, const char *passwd, const char *clientId, uint32_t flags);
static uint64_t _getFirstOrLastData(AQH_DATACLIENT *dc, const char *valueName, uint64_t *dataPtr, uint64_t maxNum, int mode);
static uint64_t _handleDataResponses(AQH_DATACLIENT *dc, uint64_t *dataPtr, uint64_t maxNum, uint32_t msgId);
static int _handleResult(AQH_DATACLIENT *dc, uint32_t msgId);
AQH_DATACLIENT *AQH_DataClient_new(AQH_EVENT_LOOP *eventLoop, uint8_t protoId, uint8_t protoVer)
{
AQH_DATACLIENT *dc;
GWEN_NEW_OBJECT(AQH_DATACLIENT, dc);
dc->eventLoop=eventLoop;
dc->protoId=protoId;
dc->protoVer=protoVer;
dc->timeoutInSeconds=AQH_DATA_CLIENT_DEFAULT_CMD_TIMEOUT;
return dc;
}
void AQH_DataClient_free(AQH_DATACLIENT *dc)
{
if (dc) {
AQH_Object_free(dc->ipcEndpoint);
GWEN_FREE_OBJECT(dc);
}
}
int AQH_DataClient_ReadLocalArgs(AQH_DATACLIENT *dc,
GWEN_DB_NODE *dbGlobalArgs, const GWEN_ARGS *args,
int argc, char **argv)
{
if (dc) {
int rv;
GWEN_DB_Group_free(dc->dbLocalArgs);
dc->dbLocalArgs=GWEN_DB_GetGroup(dbGlobalArgs, GWEN_DB_FLAGS_DEFAULT, "local");
rv=GWEN_Args_Check(argc, argv, 1, GWEN_ARGS_MODE_ALLOW_FREEPARAM, args, dc->dbLocalArgs);
if (rv==GWEN_ARGS_RESULT_ERROR) {
fprintf(stderr, "ERROR: Could not parse arguments\n");
return 1;
}
else if (rv==GWEN_ARGS_RESULT_HELP) {
GWEN_BUFFER *ubuf;
ubuf=GWEN_Buffer_new(0, 1024, 0, 1);
if (GWEN_Args_Usage(args, ubuf, GWEN_ArgsOutType_Txt)) {
fprintf(stderr, "ERROR: Could not create help string\n");
return 1;
}
fprintf(stderr, "%s\n", GWEN_Buffer_GetStart(ubuf));
GWEN_Buffer_free(ubuf);
return 1;
}
dc->timeoutInSeconds=GWEN_DB_GetIntValue(dc->dbLocalArgs, "timeout", 0, 5);
AQH_MergeConfigFileIntoConfig(dc->dbLocalArgs, "ConfigFile");
return 0;
}
return GWEN_ERROR_INVALID;
}
GWEN_DB_NODE *AQH_DataClient_GetDbLocalArgs(const AQH_DATACLIENT *dc)
{
return dc?dc->dbLocalArgs:NULL;
}
int AQH_DataClient_Connect(AQH_DATACLIENT *dc,
const char *addr, int port,
const char *userId, const char *passwd,
const char *clientId,
uint32_t flags)
{
if (dc) {
int rv;
AQH_Object_free(dc->ipcEndpoint);
dc->ipcEndpoint=NULL;
rv=_connectEndpoint(dc, addr, port, 0 /* connection flags */);
if (rv<0) {
DBG_INFO(NULL, "here (%d)", rv);
return rv;
}
rv=_exchangeConnectMsgs(dc, userId, passwd, clientId, flags);
if (rv<0) {
AQH_Object_free(dc->ipcEndpoint);
dc->ipcEndpoint=NULL;
DBG_INFO(NULL, "here (%d)", rv);
return rv;
}
return 0;
}
return GWEN_ERROR_INVALID;
}
int AQH_DataClient_Disconnect(AQH_DATACLIENT *dc)
{
if (dc) {
AQH_Object_free(dc->ipcEndpoint);
dc->ipcEndpoint=NULL;
return 0;
}
return GWEN_ERROR_INVALID;
}
void AQH_DataClient_SetTimeout(AQH_DATACLIENT *dc, int i)
{
if (dc) {
dc->timeoutInSeconds=i;
}
}
AQH_DEVICE_LIST *AQH_DataClient_GetDevices(AQH_DATACLIENT *dc)
{
if (dc) {
AQH_MESSAGE *msgOut;
AQH_MESSAGE *msgIn;
uint32_t msgId;
AQH_DEVICE_LIST *fullDeviceList;
fullDeviceList=AQH_Device_List_new();
msgId=++(dc->lastMsgId);
msgOut=AQH_IpcMessage_new(dc->protoId, dc->protoVer, AQH_MSGTYPE_IPC_DATA_GETDEVICES_REQ, msgId, 0, 0, NULL);
AQH_Endpoint_AddMsgOut(dc->ipcEndpoint, msgOut);
while( (msgIn=AQH_IpcEndpoint_WaitForResponseMsg(dc->ipcEndpoint, msgId, dc->timeoutInSeconds)) ) {
GWEN_TAG16_LIST *tagList;
tagList=AQH_IpcMessageTag16_ParsePayload(msgIn, 0);
if (tagList) {
uint16_t code;
code=AQH_IpcMessage_GetCode(msgIn);
if (code==AQH_MSGTYPE_IPC_DATA_GETDEVICES_RSP) {
AQH_DEVICE_LIST *deviceList;
deviceList=AQH_IpcdMessageDevices_ReadDeviceList(tagList);
if (deviceList) {
AQH_Device_List_AddList(fullDeviceList, deviceList);
AQH_Device_List_free(deviceList);
}
if (AQH_IpcdMessageDevices_GetFlags(tagList) & AQH_MSGDATA_DEVICES_FLAGS_LASTMSG) {
GWEN_Tag16_List_free(tagList);
AQH_Message_free(msgIn);
break;
}
}
else if (code==AQH_MSGTYPE_IPC_DATA_RESULT) {
DBG_ERROR(NULL, "Server Error: %d", AQH_IpcMessageResult_GetResult(tagList));
GWEN_Tag16_List_free(tagList);
AQH_Message_free(msgIn);
AQH_Device_List_free(fullDeviceList);
return NULL;
}
else {
DBG_INFO(NULL, "Ignoring message \"%d\"", code);
}
GWEN_Tag16_List_free(tagList);
}
AQH_Message_free(msgIn);
} /* while */
if (AQH_Device_List_GetCount(fullDeviceList)>0)
return fullDeviceList;
AQH_Device_List_free(fullDeviceList);
}
return NULL;
}
AQH_VALUE_LIST *AQH_DataClient_GetValues(AQH_DATACLIENT *dc, const char *deviceName, int modality)
{
if (dc) {
AQH_MESSAGE *msgOut;
AQH_MESSAGE *msgIn;
uint32_t msgId;
AQH_VALUE_LIST *fullValueList;
fullValueList=AQH_Value_List_new();
msgId=++(dc->lastMsgId);
msgOut=AQH_IpcdMessageGetValues_new(AQH_MSGTYPE_IPC_DATA_GETVALUES_REQ, msgId, 0, deviceName, modality);
AQH_Endpoint_AddMsgOut(dc->ipcEndpoint, msgOut);
while( (msgIn=AQH_IpcEndpoint_WaitForResponseMsg(dc->ipcEndpoint, msgId, dc->timeoutInSeconds)) ) {
GWEN_TAG16_LIST *tagList;
tagList=AQH_IpcMessageTag16_ParsePayload(msgIn, 0);
if (tagList) {
uint16_t code;
code=AQH_IpcMessage_GetCode(msgIn);
if (code==AQH_MSGTYPE_IPC_DATA_GETVALUES_RSP) {
AQH_VALUE_LIST *valueList;
valueList=AQH_IpcdMessageValues_ReadValueList(tagList);
if (valueList) {
AQH_Value_List_AddList(fullValueList, valueList);
AQH_Value_List_free(valueList);
}
if (AQH_IpcdMessageValues_GetFlags(tagList) & AQH_MSGDATA_VALUES_FLAGS_LASTMSG) {
GWEN_Tag16_List_free(tagList);
AQH_Message_free(msgIn);
break;
}
}
else if (code==AQH_MSGTYPE_IPC_DATA_RESULT) {
DBG_ERROR(NULL, "Server Error: %d", AQH_IpcMessageResult_GetResult(tagList));
GWEN_Tag16_List_free(tagList);
AQH_Message_free(msgIn);
AQH_Value_List_free(fullValueList);
return NULL;
}
else {
DBG_INFO(NULL, "Ignoring message \"%d\"", code);
}
GWEN_Tag16_List_free(tagList);
}
AQH_Message_free(msgIn);
} /* while */
if (AQH_Value_List_GetCount(fullValueList)>0)
return fullValueList;
AQH_Value_List_free(fullValueList);
}
return NULL;
}
uint64_t AQH_DataClient_GetFirstData(AQH_DATACLIENT *dc, const char *valueName, uint64_t *dataPtr, uint64_t maxNum)
{
return _getFirstOrLastData(dc, valueName, dataPtr, maxNum, AQH_MSGDATA_GETDATA_MODE_FIRST);
}
uint64_t AQH_DataClient_GetLastData(AQH_DATACLIENT *dc, const char *valueName, uint64_t *dataPtr, uint64_t maxNum)
{
return _getFirstOrLastData(dc, valueName, dataPtr, maxNum, AQH_MSGDATA_GETDATA_MODE_LAST);
}
uint64_t AQH_DataClient_GetPeriodData(AQH_DATACLIENT *dc, const char *valueName,
uint64_t *dataPtr, uint64_t maxNum,
uint64_t tsBegin, uint64_t tsEnd)
{
if (dc) {
AQH_MESSAGE *msgOut;
uint32_t msgId;
msgId=++(dc->lastMsgId);
msgOut=AQH_IpcdMessageGetData_new(AQH_MSGTYPE_IPC_DATA_GETDATA_REQ,
msgId, 0,
AQH_MSGDATA_GETDATA_MODE_PERIOD,
valueName, tsBegin, tsEnd, maxNum);
AQH_Endpoint_AddMsgOut(dc->ipcEndpoint, msgOut);
return _handleDataResponses(dc, dataPtr, maxNum, msgId);
}
return 0;
}
int AQH_DataClient_SetData(AQH_DATACLIENT *dc, const AQH_VALUE *v, const char *data)
{
if (dc) {
AQH_MESSAGE *msgOut;
uint32_t msgId;
msgId=++(dc->lastMsgId);
msgOut=AQH_IpcdMessageSetData_new(AQH_MSGTYPE_IPC_DATA_SETDATA, msgId, 0, v, data);
AQH_Endpoint_AddMsgOut(dc->ipcEndpoint, msgOut);
return _handleResult(dc, msgId);
}
return GWEN_ERROR_INVALID;
}
int AQH_DataClient_UpdateData(AQH_DATACLIENT *dc, const AQH_VALUE *v, uint64_t timeStamp, double dataPoint)
{
if (dc) {
AQH_MESSAGE *msgOut;
uint32_t msgId;
msgId=++(dc->lastMsgId);
msgOut=AQH_IpcdMessageMultiData_newForOne(AQH_MSGTYPE_IPC_DATA_UPDATEDATA, msgId, 0, v, timeStamp, dataPoint);
AQH_Endpoint_AddMsgOut(dc->ipcEndpoint, msgOut);
return _handleResult(dc, msgId);
}
return GWEN_ERROR_INVALID;
}
int _connectEndpoint(AQH_DATACLIENT *dc, const char *addr, int port, uint32_t flags)
{
if (dc) {
AQH_OBJECT *ep;
int fd;
fd=AQH_TcpObject_CreateConnectedSocket(addr, port);
if (fd<0) {
DBG_ERROR(NULL, "Error connecting to broker server %s:%d", addr, port);
return GWEN_ERROR_IO;
}
ep=AQH_IpcClientObject_new(dc->eventLoop, fd);
assert(ep);
AQH_Endpoint_AddFlags(ep, flags);
dc->ipcEndpoint=ep;
return 0;
}
return GWEN_ERROR_INVALID;
}
int _exchangeConnectMsgs(AQH_DATACLIENT *dc, const char *userId, const char *passwd, const char *clientId, uint32_t flags)
{
AQH_MESSAGE *msgOut;
uint32_t msgId;
DBG_INFO(NULL, "Sending connect message for proto=%d.%d", dc->protoId, dc->protoVer);
msgId=AQH_Endpoint_GetNextMessageId(dc->ipcEndpoint);
msgOut=AQH_IpcMessageConnect_new(dc->protoId, dc->protoVer,
AQH_MSGTYPE_IPC_CONNECT_REQ,
msgId, 0,
clientId, userId, passwd, flags);
AQH_Endpoint_AddMsgOut(dc->ipcEndpoint, msgOut);
return AQH_IpcEndpoint_WaitForResultMsg(dc->ipcEndpoint,
dc->protoId, dc->protoVer, AQH_MSGTYPE_IPC_RESULT,
msgId, dc->timeoutInSeconds);
}
uint64_t _getFirstOrLastData(AQH_DATACLIENT *dc, const char *valueName, uint64_t *dataPtr, uint64_t maxNum, int mode)
{
if (dc) {
AQH_MESSAGE *msgOut;
uint32_t msgId;
msgId=++(dc->lastMsgId);
msgOut=AQH_IpcdMessageGetData_new(AQH_MSGTYPE_IPC_DATA_GETDATA_REQ,
msgId, 0,
mode,
valueName, 0, 0, maxNum);
AQH_Endpoint_AddMsgOut(dc->ipcEndpoint, msgOut);
return _handleDataResponses(dc, dataPtr, maxNum, msgId);
}
return 0;
}
uint64_t _handleDataResponses(AQH_DATACLIENT *dc, uint64_t *dataPtr, uint64_t maxNum, uint32_t msgId)
{
AQH_MESSAGE *msgIn;
uint64_t fullNumberOfPoints=0;
while( (msgIn=AQH_IpcEndpoint_WaitForResponseMsg(dc->ipcEndpoint, msgId, dc->timeoutInSeconds)) ) {
GWEN_TAG16_LIST *tagList;
tagList=AQH_IpcMessageTag16_ParsePayload(msgIn, 0);
if (tagList) {
uint16_t code;
code=AQH_IpcMessage_GetCode(msgIn);
if (code==AQH_MSGTYPE_IPC_DATA_GETDATA_RSP) {
const uint64_t *recvDataPtr;
uint64_t recvNumberOfPoints;
AQH_IpcdMessageMultiData_ReadDatapoints(tagList, &recvDataPtr, &recvNumberOfPoints);
if (recvNumberOfPoints) {
uint64_t i;
for (i=0; i<recvNumberOfPoints; i++) {
if (fullNumberOfPoints<maxNum) {
dataPtr[fullNumberOfPoints*2]=recvDataPtr[i*2];
dataPtr[(fullNumberOfPoints*2)+1]=recvDataPtr[(i*2)+1];
fullNumberOfPoints++;
}
else {
DBG_ERROR(NULL, "Too many bytes received");
break;
}
}
}
GWEN_Tag16_List_free(tagList);
AQH_Message_free(msgIn);
break;
}
else if (code==AQH_MSGTYPE_IPC_DATA_RESULT) {
DBG_INFO(NULL, "Server Error: %d", AQH_IpcMessageResult_GetResult(tagList));
GWEN_Tag16_List_free(tagList);
AQH_Message_free(msgIn);
return 0;
}
else {
DBG_INFO(NULL, "Ignoring message \"%d\"", code);
}
GWEN_Tag16_List_free(tagList);
}
AQH_Message_free(msgIn);
} /* while */
return fullNumberOfPoints;
}
int _handleResult(AQH_DATACLIENT *dc, uint32_t msgId)
{
AQH_MESSAGE *msgIn;
while( (msgIn=AQH_IpcEndpoint_WaitForResponseMsg(dc->ipcEndpoint, msgId, dc->timeoutInSeconds)) ) {
GWEN_TAG16_LIST *tagList;
tagList=AQH_IpcMessageTag16_ParsePayload(msgIn, 0);
if (tagList) {
uint16_t code;
code=AQH_IpcMessage_GetCode(msgIn);
if (code==AQH_MSGTYPE_IPC_DATA_RESULT) {
int result;
result=AQH_IpcMessageResult_GetResult(tagList);
DBG_INFO(NULL, "Server result: %d", result);
GWEN_Tag16_List_free(tagList);
AQH_Message_free(msgIn);
if (result!=AQH_MSGDATA_RESULT_SUCCESS) {
DBG_INFO(NULL, "here (%d)", result);
return GWEN_ERROR_GENERIC;
}
return 0;
}
else {
DBG_INFO(NULL, "Ignoring message \"%d\"", code);
}
GWEN_Tag16_List_free(tagList);
}
AQH_Message_free(msgIn);
} /* while */
return GWEN_ERROR_TIMEOUT;
}
int AQH_DataClient_ConnectWithArgs(AQH_DATACLIENT *dc, uint32_t flags)
{
const char *brokerAddress;
int brokerPort;
const char *userId;
const char *passwd;
const char *clientId;
int rv;
brokerAddress=GWEN_DB_GetCharValue(dc->dbLocalArgs, "brokerAddress", 0, NULL);
if (!(brokerAddress && *brokerAddress))
brokerAddress=GWEN_DB_GetCharValue(dc->dbLocalArgs, "ConfigFile/brokerAddress", 0, "127.0.0.1");
brokerPort=GWEN_DB_GetIntValue(dc->dbLocalArgs, "brokerPort", 0, -1);
if (brokerPort<0)
brokerPort=GWEN_DB_GetIntValue(dc->dbLocalArgs, "ConfigFile/brokerPort", 0, 1899);
userId=GWEN_DB_GetCharValue(dc->dbLocalArgs, "userId", 0, NULL);
passwd=GWEN_DB_GetCharValue(dc->dbLocalArgs, "password", 0, NULL);
clientId=GWEN_DB_GetCharValue(dc->dbLocalArgs, "brokerClientId", 0, NULL);
rv=AQH_DataClient_Connect(dc, brokerAddress, brokerPort, userId, passwd, clientId, flags);
if (rv<0) {
DBG_INFO(NULL, "here (%d)", rv);
return rv;
}
return 0;
}

View File

@@ -0,0 +1,57 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
****************************************************************************/
#ifndef AQHOME_DATA_CLIENT_H
#define AQHOME_DATA_CLIENT_H
#include <aqhome/api.h>
#include <aqhome/events2/object.h>
#include <aqhome/data/value.h>
#include <aqhome/data/device.h>
#include <gwenhywfar/args.h>
typedef struct AQH_DATACLIENT AQH_DATACLIENT;
AQHOME_API AQH_DATACLIENT *AQH_DataClient_new(AQH_EVENT_LOOP *eventLoop, uint8_t protoId, uint8_t protoVer);
AQHOME_API void AQH_DataClient_free(AQH_DATACLIENT *dc);
AQHOME_API void AQH_DataClient_SetTimeout(AQH_DATACLIENT *dc, int i);
AQHOME_API int AQH_DataClient_Connect(AQH_DATACLIENT *dc,
const char *addr, int port,
const char *userId, const char *passwd,
const char *clientId,
uint32_t flags);
AQHOME_API int AQH_DataClient_Disconnect(AQH_DATACLIENT *dc);
AQHOME_API AQH_DEVICE_LIST *AQH_DataClient_GetDevices(AQH_DATACLIENT *dc);
AQHOME_API AQH_VALUE_LIST *AQH_DataClient_GetValues(AQH_DATACLIENT *dc, const char *deviceName, int modality);
AQHOME_API uint64_t AQH_DataClient_GetFirstData(AQH_DATACLIENT *dc, const char *valueName, uint64_t *dataPtr, uint64_t maxNum);
AQHOME_API uint64_t AQH_DataClient_GetLastData(AQH_DATACLIENT *dc, const char *valueName, uint64_t *dataPtr, uint64_t maxNum);
AQHOME_API uint64_t AQH_DataClient_GetPeriodData(AQH_DATACLIENT *dc, const char *valueName,
uint64_t *dataPtr, uint64_t maxNum,
uint64_t tsBegin, uint64_t tsEnd);
AQHOME_API int AQH_DataClient_SetData(AQH_DATACLIENT *dc, const AQH_VALUE *v, const char *data);
AQHOME_API int AQH_DataClient_UpdateData(AQH_DATACLIENT *dc, const AQH_VALUE *v, uint64_t timeStamp, double dataPoint);
AQHOME_API int AQH_DataClient_ReadLocalArgs(AQH_DATACLIENT *dc,
GWEN_DB_NODE *dbGlobalArgs, const GWEN_ARGS *argDescrs,
int argc, char **argv);
AQHOME_API int AQH_DataClient_ConnectWithArgs(AQH_DATACLIENT *dc, uint32_t flags);
AQHOME_API GWEN_DB_NODE *AQH_DataClient_GetDbLocalArgs(const AQH_DATACLIENT *dc);
#endif

View File

@@ -0,0 +1,32 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
****************************************************************************/
#ifndef AQHOME_DATA_CLIENT_P_H
#define AQHOME_DATA_CLIENT_P_H
#include "aqhome/dataclient/client.h"
struct AQH_DATACLIENT {
AQH_EVENT_LOOP *eventLoop;
AQH_OBJECT *ipcEndpoint;
int timeoutInSeconds;
uint8_t protoId;
uint8_t protoVer;
uint32_t lastMsgId;
GWEN_DB_NODE *dbLocalArgs;
};
#endif

View File

@@ -14,6 +14,7 @@
#include <gwenhywfar/inherit.h>
#include <gwenhywfar/debug.h>
#include <gwenhywfar/text.h>
//#include <sys/socket.h>
#include <errno.h>
@@ -170,6 +171,7 @@ int AQH_FdObject_Read(AQH_OBJECT *o, uint8_t *ptrBuffer, uint32_t lenBuffer)
else if (rv>0) {
/* data received */
DBG_DEBUG(AQH_LOGDOMAIN, "Received %d bytes", (int) rv);
// GWEN_Text_LogString((const char*) ptrBuffer, rv, NULL, GWEN_LoggerLevel_Error);
return (int) rv;
}
else {

View File

@@ -79,7 +79,7 @@ AQH_MESSAGE *AQH_IpcEndpoint_WaitForResponseMsg(AQH_OBJECT *ipcEndpoint, uint32_
uint16_t code;
code=AQH_IpcMessage_GetCode(msg);
DBG_ERROR(NULL, "Received unexpected message %d (%x), ignoring", code, code);
DBG_DEBUG(NULL, "Received unexpected message %d (%x), ignoring", code, code);
AQH_Message_free(msg);
}
}

View File

@@ -24,7 +24,7 @@
#define AQH_MSG_READER_HEADER_SIZE 4
#define AQH_MSG_READER_MINMSGSIZE 12
#define AQH_MSG_READER_MAXMSGSIZE 10240
#define AQH_MSG_READER_MAXMSGSIZE 20480

View File

@@ -176,17 +176,17 @@ int _handleSocketReady(AQH_OBJECT *o)
{
AQH_TCPD_OBJECT *xo;
DBG_INFO(NULL, "Socket ready");
DBG_DEBUG(NULL, "Socket ready");
xo=GWEN_INHERIT_GETDATA(AQH_OBJECT, AQH_TCPD_OBJECT, o);
if (xo) {
int clientSk;
clientSk=_acceptConnection(xo->fdSocket);
if (clientSk<0) {
DBG_ERROR(AQH_LOGDOMAIN, "here (%d)", clientSk);
DBG_INFO(AQH_LOGDOMAIN, "here (%d)", clientSk);
}
else {
DBG_INFO(AQH_LOGDOMAIN, "New connection");
DBG_NOTICE(AQH_LOGDOMAIN, "New connection");
if (0==AQH_Object_EmitSignal(o, AQH_TCPD_OBJECT_SIGNAL_NEWCONN, clientSk, NULL)) {
DBG_ERROR(AQH_LOGDOMAIN, "New connection not handled");
close(clientSk);

View File

@@ -51,6 +51,7 @@
m_ipcd_values.h
m_ipcd_getdata.h
m_ipcd_setdata.h
m_ipcd_getvalues.h
</headers>
@@ -67,6 +68,7 @@
m_ipcd_values.c
m_ipcd_getdata.c
m_ipcd_setdata.c
m_ipcd_getvalues.c
</sources>

View File

@@ -28,6 +28,8 @@
* ------------------------------------------------------------------------------------------------
*/
static const char *_modeToChar(int mode);
/* ------------------------------------------------------------------------------------------------
@@ -37,6 +39,7 @@
AQH_MESSAGE *AQH_IpcdMessageGetData_new(uint16_t code,
uint32_t msgId, uint32_t refMsgId,
int mode,
const char *valueName, uint64_t tsBegin, uint64_t tsEnd, uint64_t num)
{
AQH_MESSAGE *msg;
@@ -49,6 +52,7 @@ AQH_MESSAGE *AQH_IpcdMessageGetData_new(uint16_t code,
GWEN_Tag16_WriteUint64TagToBuffer(AQH_MSGDATA_GETDATA_TAGS_BEGIN, tsBegin, buf);
GWEN_Tag16_WriteUint64TagToBuffer(AQH_MSGDATA_GETDATA_TAGS_END, tsEnd, buf);
GWEN_Tag16_WriteUint64TagToBuffer(AQH_MSGDATA_GETDATA_TAGS_NUM, num, buf);
GWEN_Tag16_WriteUint64TagToBuffer(AQH_MSGDATA_GETDATA_TAGS_MODE, mode, buf);
msg=AQH_IpcMessage_new(AQH_IPC_PROTOCOL_DATA_ID, AQH_IPC_PROTOCOL_DATA_VERSION, code, msgId, refMsgId,
GWEN_Buffer_GetUsedBytes(buf), (const uint8_t*) GWEN_Buffer_GetStart(buf));
@@ -64,19 +68,22 @@ void AQH_IpcdMessageGetData_DumpToBuffer(const AQH_MESSAGE *msg, const GWEN_TAG1
char *valueName;
uint64_t tsBegin;
uint64_t tsEnd;
uint64_t mode;
valueName=tagList?AQH_Tag16_GetTagDataAsNewString(tagList, AQH_MSGDATA_GETDATA_TAGS_NAME, NULL):NULL;
tsBegin=tagList?AQH_Tag16_GetTagDataAsUint64(tagList, AQH_MSGDATA_GETDATA_TAGS_BEGIN, 0):0;
tsEnd=tagList?AQH_Tag16_GetTagDataAsUint64(tagList, AQH_MSGDATA_GETDATA_TAGS_END, 0):0;
mode=tagList?AQH_Tag16_GetTagDataAsUint64(tagList, AQH_MSGDATA_GETDATA_TAGS_MODE, 0):0;
GWEN_Buffer_AppendArgs(dbuf,
"GETDATA(%s) %s (code=%d, proto=%d, proto version=%d, name=%s, tsBegin=%lu, tsEnd=%lu)\n",
"GETDATA(%s) %s (code=%d, proto=%d, proto version=%d, name=%s, mode=%s, tsBegin=%lu, tsEnd=%lu)\n",
AQH_IpcdMessage_MsgTypeToChar(AQH_IpcMessage_GetCode(msg)),
sText?sText:"",
AQH_IpcMessage_GetCode(msg),
AQH_IpcMessage_GetProtoId(msg),
AQH_IpcMessage_GetProtoVersion(msg),
valueName?valueName:"<empty>",
valueName?valueName:"<empty>",
_modeToChar(mode),
(unsigned long int) tsBegin,
(unsigned long int) tsEnd);
free(valueName);
@@ -85,3 +92,15 @@ void AQH_IpcdMessageGetData_DumpToBuffer(const AQH_MESSAGE *msg, const GWEN_TAG1
const char *_modeToChar(int mode)
{
switch(mode) {
case AQH_MSGDATA_GETDATA_MODE_FIRST: return "first";
case AQH_MSGDATA_GETDATA_MODE_LAST: return "last";
case AQH_MSGDATA_GETDATA_MODE_PERIOD: return "period";
default: return "unknown";
}
}

View File

@@ -24,10 +24,20 @@
#define AQH_MSGDATA_GETDATA_TAGS_BEGIN 0x0020
#define AQH_MSGDATA_GETDATA_TAGS_END 0x0021
#define AQH_MSGDATA_GETDATA_TAGS_NUM 0x0022
#define AQH_MSGDATA_GETDATA_TAGS_MODE 0x0023
enum {
AQH_MSGDATA_GETDATA_MODE_FIRST=0,
AQH_MSGDATA_GETDATA_MODE_LAST,
AQH_MSGDATA_GETDATA_MODE_PERIOD
};
AQHOME_API AQH_MESSAGE *AQH_IpcdMessageGetData_new(uint16_t code,
uint32_t msgId, uint32_t refMsgId,
int mode,
const char *valueName, uint64_t tsBegin, uint64_t tsEnd, uint64_t num);
AQHOME_API void AQH_IpcdMessageGetData_DumpToBuffer(const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList,

View File

@@ -0,0 +1,81 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
****************************************************************************/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "aqhome/msg/ipc/data/m_ipcd_getvalues.h"
#include "aqhome/msg/ipc/m_ipc_tag16.h"
#include "aqhome/msg/ipc/data/m_ipcd.h"
#include "aqhome/msg/ipc/m_ipc.h"
#include <gwenhywfar/text.h>
#include <gwenhywfar/tag16.h>
#include <gwenhywfar/buffer.h>
#include <gwenhywfar/debug.h>
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
/* ------------------------------------------------------------------------------------------------
* implementation
* ------------------------------------------------------------------------------------------------
*/
AQH_MESSAGE *AQH_IpcdMessageGetValues_new(uint16_t code, uint32_t msgId, uint32_t refMsgId,
const char *deviceName, int modality)
{
AQH_MESSAGE *msg;
GWEN_BUFFER *buf;
buf=GWEN_Buffer_new(0, 256, 0, 1);
if (deviceName && *deviceName)
GWEN_Tag16_WriteStringTagToBuffer(AQH_MSGDATA_GETVALUES_TAGS_DEVICENAME, deviceName, buf);
GWEN_Tag16_WriteUint64TagToBuffer(AQH_MSGDATA_GETVALUES_TAGS_MODALITY, modality, buf);
msg=AQH_IpcMessage_new(AQH_IPC_PROTOCOL_DATA_ID, AQH_IPC_PROTOCOL_DATA_VERSION, code, msgId, refMsgId,
GWEN_Buffer_GetUsedBytes(buf), (const uint8_t*) GWEN_Buffer_GetStart(buf));
GWEN_Buffer_free(buf);
return msg;
}
void AQH_IpcdMessageGetValues_DumpToBuffer(const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList,
GWEN_BUFFER *dbuf, const char *sText)
{
char *deviceName;
uint64_t modality;
deviceName=tagList?AQH_Tag16_GetTagDataAsNewString(tagList, AQH_MSGDATA_GETVALUES_TAGS_DEVICENAME, NULL):NULL;
modality=tagList?AQH_Tag16_GetTagDataAsUint64(tagList, AQH_MSGDATA_GETVALUES_TAGS_MODALITY, 0):0;
GWEN_Buffer_AppendArgs(dbuf,
"GETVALUES(%s) %s (code=%d, proto=%d, proto version=%d, device=%s, modality=%s)\n",
AQH_IpcdMessage_MsgTypeToChar(AQH_IpcMessage_GetCode(msg)),
sText?sText:"",
AQH_IpcMessage_GetCode(msg),
AQH_IpcMessage_GetProtoId(msg),
AQH_IpcMessage_GetProtoVersion(msg),
deviceName?deviceName:"<empty>",
AQH_ValueModality_toString(modality));
free(deviceName);
}

View File

@@ -0,0 +1,36 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
****************************************************************************/
#ifndef AQH_M_IPCD_GETVALUES_H
#define AQH_M_IPCD_GETVALUES_H
#include <aqhome/api.h>
#include <aqhome/ipc2/message.h>
#include <aqhome/data/value.h>
#include <gwenhywfar/tag16.h>
#include <gwenhywfar/buffer.h>
#define AQH_MSGDATA_GETVALUES_TAGS_DEVICENAME 0x0001
#define AQH_MSGDATA_GETVALUES_TAGS_MODALITY 0x0002
AQHOME_API AQH_MESSAGE *AQH_IpcdMessageGetValues_new(uint16_t code, uint32_t msgId, uint32_t refMsgId,
const char *deviceName, int modality);
AQHOME_API void AQH_IpcdMessageGetValues_DumpToBuffer(const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList,
GWEN_BUFFER *dbuf, const char *sText);
#endif

View File

@@ -62,6 +62,7 @@
m_flashend.h
m_flashready.h
m_flashresponse.h
m_range.h
</headers>
@@ -89,6 +90,7 @@
m_flashend.c
m_flashready.c
m_flashresponse.c
m_range.c
</sources>

View File

@@ -19,8 +19,8 @@
#define AQH_MSG_OFFS_MEMSTATS_SECONDS 0 /* 4 bytes */
#define AQH_MSG_OFFS_MEMSTATS_UID 4 /* 4 bytes */
#define AQH_MSG_OFFS_MEMSTATS_UID 0 /* 4 bytes */
#define AQH_MSG_OFFS_MEMSTATS_SECONDS 4 /* 4 bytes */
#define AQH_MSG_OFFS_MEMSTATS_STACKUSAGE 8 /* 2 bytes */
#define AQH_MSG_OFFS_MEMSTATS_BUFFERSUSED 10 /* 1 byte */
#define AQH_MSG_OFFS_MEMSTATS_MAXBUFFERSUSED 11 /* 1 byte */

View File

@@ -28,6 +28,7 @@
#include "aqhome/msg/node/m_flashend.h"
#include "aqhome/msg/node/m_flashready.h"
#include "aqhome/msg/node/m_flashresponse.h"
#include "aqhome/msg/node/m_range.h"
#include <gwenhywfar/text.h>
@@ -212,6 +213,7 @@ const char *AQH_NodeMessage_MsgTypeToChar(uint8_t i)
case AQH_MSG_TYPE_CLAIM_ADDRESS: return "ClaimAddress";
case AQH_MSG_TYPE_DENY_ADDRESS: return "DenyAddress";
case AQH_MSG_TYPE_ADDRESS_RANGE: return "Range";
case AQH_MSG_TYPE_REENUM: return "Reenum";
case AQH_MSG_TYPE_FLASH_START: return "FlashStart";
case AQH_MSG_TYPE_FLASH_END: return "FlashEnd";
@@ -246,6 +248,8 @@ void AQH_NodeMessage_DumpSpecificToBuffer(const AQH_MESSAGE *msg, GWEN_BUFFER *d
case AQH_MSG_TYPE_HAVE_ADDRESS: AQH_AddrMessage_DumpToBuffer(msg, dbuf, sText); break;
case AQH_MSG_TYPE_CLAIM_ADDRESS: AQH_AddrMessage_DumpToBuffer(msg, dbuf, sText); break;
case AQH_MSG_TYPE_DENY_ADDRESS: AQH_AddrMessage_DumpToBuffer(msg, dbuf, sText); break;
case AQH_MSG_TYPE_ADDRESS_RANGE: AQH_RangeMessage_DumpToBuffer(msg, dbuf, sText); break;
case AQH_MSG_TYPE_REENUM: AQH_RangeMessage_DumpToBuffer(msg, dbuf, sText); break;
case AQH_MSG_TYPE_FLASH_START: AQH_FlashStartMessage_DumpToBuffer(msg, dbuf, sText); break;
case AQH_MSG_TYPE_FLASH_END: AQH_FlashEndMessage_DumpToBuffer(msg, dbuf, sText); break;
@@ -266,7 +270,6 @@ void AQH_NodeMessage_DumpSpecificToBuffer(const AQH_MESSAGE *msg, GWEN_BUFFER *d
case AQH_MSG_TYPE_DEBUG:
case AQH_MSG_TYPE_TWIBUSMEMBER:
case AQH_MSG_TYPE_ADDRESS_RANGE:
default: AQH_NodeMessage_DumpToBuffer(msg, dbuf, sText); break;
}
}

View File

@@ -29,8 +29,8 @@
#define AQH_MSG_TYPE_PING 10
#define AQH_MSG_TYPE_PONG 11
#define AQH_MSG_TYPE_COMSENDSTATS 20
#define AQH_MSG_TYPE_COMRECVSTATS 21
#define AQH_MSG_TYPE_COMSENDSTATS 22
#define AQH_MSG_TYPE_COMRECVSTATS 23
#define AQH_MSG_TYPE_TWIBUSMEMBER 30
#define AQH_MSG_TYPE_DEBUG 40
#define AQH_MSG_TYPE_VALUE 50 /* deprecated */
@@ -40,6 +40,7 @@
#define AQH_MSG_TYPE_CLAIM_ADDRESS 62
#define AQH_MSG_TYPE_DENY_ADDRESS 63
#define AQH_MSG_TYPE_ADDRESS_RANGE 64
#define AQH_MSG_TYPE_REENUM 65
#define AQH_MSG_TYPE_FLASH_START 70
#define AQH_MSG_TYPE_FLASH_END 71

61
aqhome/msg/node/m_range.c Normal file
View File

@@ -0,0 +1,61 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
****************************************************************************/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "aqhome/msg/node/m_range.h"
#include "aqhome/msg/node/m_node.h"
#include <gwenhywfar/debug.h>
#define AQH_MSG_OFFS_RANGE_UID 0 /* 4 bytes */
#define AQH_MSG_OFFS_RANGE_BEGIN 4 /* 1 bytes */
#define AQH_MSG_OFFS_RANGE_END 5 /* 1 bytes */
uint32_t AQH_RangeMessage_GetUid(const AQH_MESSAGE *msg)
{
return AQH_Message_ReadUint32At(msg, AQH_MSG_OFFS_ALL_DATA_BEGIN+AQH_MSG_OFFS_RANGE_UID, 0);
}
uint8_t AQH_RangeMessage_GetRangeBegin(const AQH_MESSAGE *msg)
{
return AQH_Message_ReadUint8At(msg, AQH_MSG_OFFS_ALL_DATA_BEGIN+AQH_MSG_OFFS_RANGE_BEGIN, 0);
}
uint8_t AQH_RangeMessage_GetRangeEnd(const AQH_MESSAGE *msg)
{
return AQH_Message_ReadUint8At(msg, AQH_MSG_OFFS_ALL_DATA_BEGIN+AQH_MSG_OFFS_RANGE_END, 0);
}
void AQH_RangeMessage_DumpToBuffer(const AQH_MESSAGE *msg, GWEN_BUFFER *dbuf, const char *sText)
{
GWEN_Buffer_AppendArgs(dbuf,
"0x%02x->0x%02x: RANGE(%s) %s (uid=0x%08x, begin=0x%x, end=0x%x)\n",
AQH_NodeMessage_GetSourceAddress(msg),
AQH_NodeMessage_GetDestAddress(msg),
AQH_NodeMessage_MsgTypeToChar(AQH_NodeMessage_GetMsgType(msg)),
sText,
(unsigned int) AQH_RangeMessage_GetUid(msg),
AQH_RangeMessage_GetRangeBegin(msg),
AQH_RangeMessage_GetRangeEnd(msg));
}

30
aqhome/msg/node/m_range.h Normal file
View File

@@ -0,0 +1,30 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
****************************************************************************/
#ifndef AQH_M_RANGE_H
#define AQH_M_RANGE_H
#include <aqhome/api.h>
#include <aqhome/ipc2/message.h>
#include <gwenhywfar/debug.h>
/* This message is used for message types ClaimAddr, DenyAddr, HaveAddr */
AQHOME_API uint32_t AQH_RangeMessage_GetUid(const AQH_MESSAGE *msg);
AQHOME_API uint8_t AQH_RangeMessage_GetAddr(const AQH_MESSAGE *msg);
AQHOME_API uint8_t AQH_RangeMessage_GetRangeBegin(const AQH_MESSAGE *msg);
AQHOME_API uint8_t AQH_RangeMessage_GetRangeEnd(const AQH_MESSAGE *msg);
AQHOME_API void AQH_RangeMessage_DumpToBuffer(const AQH_MESSAGE *msg, GWEN_BUFFER *dbuf, const char *sText);
#endif

View File

@@ -18,13 +18,21 @@
#include <gwenhywfar/debug.h>
#define AQH_MSG_OFFS_RECVSTATS_UID 0 /* 4 bytes */
#define AQH_MSG_OFFS_RECVSTATS_PACKETSIN 4 /* 2 bytes */
#define AQH_MSG_OFFS_RECVSTATS_CRCERRORS 6 /* 2 bytes */
#define AQH_MSG_OFFS_RECVSTATS_IOERRORS 8 /* 2 bytes */
#define AQH_MSG_OFFS_RECVSTATS_NOBUFFER 10 /* 2 bytes */
#define AQH_MSG_OFFS_RECVSTATS_HANDLED 12 /* 2 bytes */
#define AQH_MSG_OFFS_RECVSTATS_MISSED 14 /* 2 bytes */
#define AQH_MSG_OFFS_RECVSTATS_UID 0 /* 4 bytes */
#define AQH_MSG_OFFS_RECVSTATS_IFACE 4 /* 1 byte */
#define AQH_MSG_OFFS_RECVSTATS_PACKETSIN 5 /* 2 bytes */
#define AQH_MSG_OFFS_RECVSTATS_CRCERRORS 7 /* 2 bytes */
#define AQH_MSG_OFFS_RECVSTATS_IOERRORS 9 /* 2 bytes */
#define AQH_MSG_OFFS_RECVSTATS_NOBUFFER 11 /* 2 bytes */
#define AQH_MSG_OFFS_RECVSTATS_MSGSIZEERRORS 13 /* 2 bytes */
#define AQH_MSG_OFFS_RECVSTATS_MISSED 15 /* 2 bytes */
uint8_t AQH_RecvStatsMessage_GetInterface(const AQH_MESSAGE *msg)
{
return AQH_Message_ReadUint8At(msg, AQH_MSG_OFFS_ALL_DATA_BEGIN+AQH_MSG_OFFS_RECVSTATS_IFACE, 0);
}
@@ -63,9 +71,9 @@ uint16_t AQH_RecvStatsMessage_GetNoBufferErrors(const AQH_MESSAGE *msg)
uint16_t AQH_RecvStatsMessage_GetHandled(const AQH_MESSAGE *msg)
uint16_t AQH_RecvStatsMessage_GetMsgSizeErrors(const AQH_MESSAGE *msg)
{
return AQH_Message_ReadUint16At(msg, AQH_MSG_OFFS_ALL_DATA_BEGIN+AQH_MSG_OFFS_RECVSTATS_HANDLED, 0);
return AQH_Message_ReadUint16At(msg, AQH_MSG_OFFS_ALL_DATA_BEGIN+AQH_MSG_OFFS_RECVSTATS_MSGSIZEERRORS, 0);
}
@@ -80,17 +88,18 @@ uint16_t AQH_RecvStatsMessage_GetMissed(const AQH_MESSAGE *msg)
void AQH_RecvStatsMessage_DumpToBuffer(const AQH_MESSAGE *msg, GWEN_BUFFER *dbuf, const char *sText)
{
GWEN_Buffer_AppendArgs(dbuf,
"0x%02x->0x%02x: RECVSTATS %s "
"(uid=0x%08x, in=%d, crc errs=%d, io errs=%d, nobuf errs=%d, handled=%d, missed=%d)\n",
"0x%02x->0x%02x: RECVSTATS %s"
"(uid=0x%08x, dev=%d, in=%d, eCrc=%d, eIo=%d, eNobuf=%d, eMsgSize=%d, eMissed=%d)\n",
AQH_NodeMessage_GetSourceAddress(msg),
AQH_NodeMessage_GetDestAddress(msg),
sText,
(unsigned int) AQH_RecvStatsMessage_GetUid(msg),
AQH_RecvStatsMessage_GetInterface(msg),
AQH_RecvStatsMessage_GetPacketsIn(msg),
AQH_RecvStatsMessage_GetCrcErrors(msg),
AQH_RecvStatsMessage_GetIoErrors(msg),
AQH_RecvStatsMessage_GetNoBufferErrors(msg),
AQH_RecvStatsMessage_GetHandled(msg),
AQH_RecvStatsMessage_GetMsgSizeErrors(msg),
AQH_RecvStatsMessage_GetMissed(msg));
}

View File

@@ -17,12 +17,13 @@
AQHOME_API uint8_t AQH_RecvStatsMessage_GetInterface(const AQH_MESSAGE *msg);
AQHOME_API uint32_t AQH_RecvStatsMessage_GetUid(const AQH_MESSAGE *msg);
AQHOME_API uint16_t AQH_RecvStatsMessage_GetPacketsIn(const AQH_MESSAGE *msg);
AQHOME_API uint16_t AQH_RecvStatsMessage_GetCrcErrors(const AQH_MESSAGE *msg);
AQHOME_API uint16_t AQH_RecvStatsMessage_GetIoErrors(const AQH_MESSAGE *msg);
AQHOME_API uint16_t AQH_RecvStatsMessage_GetNoBufferErrors(const AQH_MESSAGE *msg);
AQHOME_API uint16_t AQH_RecvStatsMessage_GetHandled(const AQH_MESSAGE *msg);
AQHOME_API uint16_t AQH_RecvStatsMessage_GetMsgSizeErrors(const AQH_MESSAGE *msg);
AQHOME_API uint16_t AQH_RecvStatsMessage_GetMissed(const AQH_MESSAGE *msg);
AQHOME_API void AQH_RecvStatsMessage_DumpToBuffer(const AQH_MESSAGE *msg, GWEN_BUFFER *dbuf, const char *sText);

View File

@@ -1,6 +1,6 @@
/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
* AqHome (c) by 2025 Martin Preuss, all rights reserved.
*
* The license for this file can be found in the file COPYING which you
* should have received along with this file.
@@ -19,9 +19,17 @@
#define AQH_MSG_OFFS_SENDSTATS_UID 0 /* 4 bytes */
#define AQH_MSG_OFFS_SENDSTATS_PACKETSOUT 4 /* 2 bytes */
#define AQH_MSG_OFFS_SENDSTATS_COLLISIONS 6 /* 2 bytes */
#define AQH_MSG_OFFS_SENDSTATS_BUSY 8 /* 2 bytes */
#define AQH_MSG_OFFS_SENDSTATS_IFACE 4 /* 1 byte */
#define AQH_MSG_OFFS_SENDSTATS_PACKETSOUT 5 /* 2 bytes */
#define AQH_MSG_OFFS_SENDSTATS_COLLISIONS 7 /* 2 bytes */
#define AQH_MSG_OFFS_SENDSTATS_BUSY 9 /* 2 bytes */
uint8_t AQH_SendStatsMessage_GetInterface(const AQH_MESSAGE *msg)
{
return AQH_Message_ReadUint8At(msg, AQH_MSG_OFFS_ALL_DATA_BEGIN+AQH_MSG_OFFS_SENDSTATS_IFACE, 0);
}
@@ -57,11 +65,12 @@ void AQH_SendStatsMessage_DumpToBuffer(const AQH_MESSAGE *msg, GWEN_BUFFER *dbuf
{
if (msg)
GWEN_Buffer_AppendArgs(dbuf,
"0x%02x->0x%02x: SENDSTATS %s (uid=0x%08x, out=%d, collisions=%d, busy line=%d)\n",
"0x%02x->0x%02x: SENDSTATS %s (uid=0x%08x, dev=%d, out=%d, collisions=%d, busy line=%d)\n",
AQH_NodeMessage_GetSourceAddress(msg),
AQH_NodeMessage_GetDestAddress(msg),
sText,
(unsigned int) AQH_SendStatsMessage_GetUid(msg),
AQH_SendStatsMessage_GetInterface(msg),
AQH_SendStatsMessage_GetPacketsOut(msg),
AQH_SendStatsMessage_GetCollisions(msg),
AQH_SendStatsMessage_GetBusyErrors(msg));

View File

@@ -17,6 +17,7 @@
AQHOME_API uint8_t AQH_SendStatsMessage_GetInterface(const AQH_MESSAGE *msg);
AQHOME_API uint32_t AQH_SendStatsMessage_GetUid(const AQH_MESSAGE *msg);
AQHOME_API uint16_t AQH_SendStatsMessage_GetPacketsOut(const AQH_MESSAGE *msg);
AQHOME_API uint16_t AQH_SendStatsMessage_GetCollisions(const AQH_MESSAGE *msg);

View File

@@ -12,6 +12,7 @@
#include "aqhome/aqhome.h"
#include "aqhome/msg/node/m_value.h"
#include "aqhome/msg/node/m_node.h"
@@ -102,19 +103,7 @@ uint16_t AQH_ValueMessage_GetValueDenom(const AQH_MESSAGE *msg)
const char *AQH_ValueMessage_GetValueTypeName(const AQH_MESSAGE *msg)
{
uint8_t t;
t=AQH_ValueMessage_GetValueType(msg);
switch(t) {
case AQH_MSG_VALUE_TYPE_TEMP: return "temperature";
case AQH_MSG_VALUE_TYPE_HUMIDITY: return "humidity";
case AQH_MSG_VALUE_TYPE_DOOR: return "door_window";
case AQH_MSG_VALUE_TYPE_MOTION: return "motion";
case AQH_MSG_VALUE_TYPE_CO2: return "CO2";
case AQH_MSG_VALUE_TYPE_TVOC: return "TVOC";
default: break;
}
return "unknown";
return AQH_ValueModality_toString(AQH_ValueMessage_GetValueType(msg));
}

View File

@@ -9,6 +9,9 @@
network
reportsensors
stats
router
hub
forwarder
</subdirs>
<extradist>

View File

@@ -3,15 +3,13 @@
<gwbuild>
<subdirs>
boot
main
</subdirs>
<extradist>
defs.asm
README
main.asm
</extradist>
</gwbuild>

359
avr/apps/forwarder/main.asm Normal file
View File

@@ -0,0 +1,359 @@
; ***************************************************************************
; copyright : (C) 2025 by Martin Preuss
; email : martin@libchipcard.de
;
; ***************************************************************************
; * This file is part of the project "AqHome". *
; * Please see toplevel file COPYING of that project for license details. *
; ***************************************************************************
; ***************************************************************************
; defines
; ---------------------------------------------------------------------------
; network interfaces
.equ NETDEV0_IFACENUM = 1
.equ NETDEV1_IFACENUM = 2
; ***************************************************************************
; data
.dseg
; nothing so far
; ***************************************************************************
; code
.cseg
; ---------------------------------------------------------------------------
; @routine AppForwarder_Init @global
AppForwarder_Init:
; set interface number for NETDEV0
ldi r16, NETDEV0_IFACENUM
sts netInterfaceData+NET_IFACE_OFFS_IFACENUM, r16
; set interface number for NETDEV1
ldi r16, NETDEV1_IFACENUM
sts netInterfaceData2+NET_IFACE_OFFS_IFACENUM, r16
sec
ret
; @end
; ---------------------------------------------------------------------------
; @routine AppForwarder_EveryDay @global
;
; @clobbers R16, R17, X
AppForwarder_EveryDay:
ldi yl, LOW(netInterfaceData)
ldi yh, HIGH(netInterfaceData)
bigcall NET_Interface_ResetStats ; (R16, R17, X)
ldi yl, LOW(netInterfaceData2)
ldi yh, HIGH(netInterfaceData2)
bigcall NET_Interface_ResetStats ; (R16, R17, X)
ret
; @end
; ---------------------------------------------------------------------------
; @routine AppForwarder_Run @global
;
; Read messages from either interface and forward to the other one.
AppForwarder_Run:
rjmp appForwarderCheckRecvdMsg
; @end
; ---------------------------------------------------------------------------
; @routine appForwarderCheckRecvdMsg
;
; Read messages from either interface and forward to the other one.
; @return CFLAG set if something done, cleared otherwise
; @clobbers any
appForwarderCheckRecvdMsg:
rcall NET_PeekNextIncomingMsgNum ; check read queue (bufNum->r16)
brcc appForwarderCheckRecvdMsg_ret ; no msg, jmp
rcall NET_Buffer_Locate ; (R17)
push r16
ld r16, X ; read buffer header
andi r16, 0x0f ; keep interface number (in low nibble)
rcall appForwarderGetDeviceByIfaceNum ; Y=src interface (R17)
pop r16
brcc appForwarderCheckRecvdMsg_ret ; interface not found
adiw xh:xl, 1 ; point to message begin
push r16
rcall appForwarderHandleMsgAnyDev ; check for message we should handle (ping etc)
pop r16
; let system handle incoming messages
push r16
rcall appForwarderLetSysHandleMsg
pop r16
; forward to other interface
ldd r17, Y+NET_IFACE_OFFS_IFACENUM
rcall appForwarderSendToOtherDev
brcc appForwarderCheckRecvdMsg_ret ; could not add, jmp
rcall NET_GetNextIncomingMsgNum ; take off the queue
sec
appForwarderCheckRecvdMsg_ret:
ret
; @end
; ---------------------------------------------------------------------------
; @routine appForwarderHandleMsgAnyDev @global
;
; @param Y pointer to source interface for the message
; @param X pointer to received message
; @return CFLAG set if msg handled, cleared otherwise
; @clobbers any, !X
appForwarderHandleMsgAnyDev:
push xl
push xh
rcall appForwarderHandleMsgAnyDev_savedX
pop xh
pop xl
rjmp appForwarderHandleMsgAnyDev_end
appForwarderHandleMsgAnyDev_savedX:
adiw xh:xl, NETMSG_OFFS_CMD ; maybe move ping/reboot handling to all/main.asm?
ld r16, X
sbiw xh:xl, NETMSG_OFFS_CMD
cpi r16, NETMSG_CMD_REBOOT_REQUEST
breq appForwarderHandleMsgAnyDev_handleRebootMsg
cpi r16, NETMSG_CMD_PING
breq appForwarderHandleMsgAnyDev_handlePingMsg
cpi r16, NETMSG_CMD_CLAIM_ADDRESS
breq appForwarderHandleMsgAnyDev_handleClaimAddr
rjmp appForwarderHandleMsgAnyDev_clcRet
appForwarderHandleMsgAnyDev_handleRebootMsg:
rcall appForwarderHandleRebootRequest
ret
appForwarderHandleMsgAnyDev_handlePingMsg:
rcall appForwarderHandlePingRequest
clc
ret
appForwarderHandleMsgAnyDev_handleClaimAddr:
rcall appForwarderHandleClaimAddrRequest
clc
ret
appForwarderHandleMsgAnyDev_clcRet:
clc
appForwarderHandleMsgAnyDev_end:
ret
; @end
; ---------------------------------------------------------------------------
; @routine appForwarderHandleClaimAddrRequest
;
; @param X pointer to received message
; @param Y pointer to source interface for the message
; @clobbers
appForwarderHandleClaimAddrRequest:
rcall NETMSG_Address_Read ; R18=cmd, R19=addr(R18, R19)
lds r16, netInterfaceData+NET_IFACE_OFFS_ADDRESS
cp r19, r16
brne appForwarderHandleClaimAddrRequest_ret
ldi r18, NETMSG_CMD_DENY_ADDRESS ; deny addr
rcall appForwarderSendAddrMsg ; (R16, R17, R18, R19, R20, R21, X, Y)
appForwarderHandleClaimAddrRequest_ret:
ret
; @end
; ---------------------------------------------------------------------------
; @routine appForwarderSendAddrMsg
;
; @param R18 command
; @param R19 address to send
; @param Y pointer to interface to send to
; @clobbers R16 (R17, R18, R19, R20, R21, X, Y)
appForwarderSendAddrMsg:
bigcall NET_Buffer_Alloc ; (R16, R17, X)
brcc appForwarderSendAddrMsg_end
push r16
adiw xh:xl, 1
bigcall NETMSG_Address_Write ; (R16, R17, R18, R19, R20, R21)
sbiw xh:xl, 1
pop r16
bigcall NET_Interface_AddOrReleaseOutMsg ; (R16, R17, R18, X)
appForwarderSendAddrMsg_end:
ret
; @end
; ---------------------------------------------------------------------------
; @routine appForwarderHandleRebootRequest
;
; Doesn't return if reboot msg is valid.
;
; @param X pointer to received message
appForwarderHandleRebootRequest:
rcall NETMSG_RebootRequestRead
brcc appForwarderHandleRebootRequest_end
; reboot
cli
bigjmp BOOTLOADER_ADDR
appForwarderHandleRebootRequest_end:
ret
; @end
; ---------------------------------------------------------------------------
; @routine appForwarderHandlePingRequest
;
; @param X pointer to received message
; @param Y pointer to source interface for the message
appForwarderHandlePingRequest:
ld r17, X
lds r16, (netInterfaceData+NET_IFACE_OFFS_ADDRESS)
cp r16, r17
breq appForwarderHandlePingRequest_forMe
cpi r17, 0xff
breq appForwarderHandlePingRequest_forMe
clc
rjmp appForwarderHandlePingRequest_end
appForwarderHandlePingRequest_forMe:
adiw xh:xl, NETMSG_OFFS_SRCADDR
ld r17, X
sbiw xh:xl, NETMSG_OFFS_SRCADDR
push r17
bigcall NET_Buffer_Alloc ; (R16, R17, X)
pop r17
brcc appForwarderHandlePingRequest_end ; jmp on error
push r16 ; buffer num
mov r16, r17 ; DEST addr
adiw xh:xl, 1
bigcall NETMSG_Pong_Write ; (R16, R17, R18, R19, R20, X)
sbiw xh:xl, 1
pop r16 ; buffer num
bigcall NET_Interface_AddOrReleaseOutMsg ; (R16, R17, R18, X)
appForwarderHandlePingRequest_end:
ret
; ---------------------------------------------------------------------------
; @routine appForwarderLetSysHandleMsg
;
; @param X pointer to msg to handle (point behind the buffer header!)
; @param Y pointer to source interface for the message
; @clobbers any, !X
appForwarderLetSysHandleMsg:
ld r16, X
cpi r16, 0xff
breq appForwarderLetSysHandleMsg_forMe
lds r17, netInterfaceData+NET_IFACE_OFFS_ADDRESS
cp r16, r17
brne appForwarderLetSysHandleMsg_end
appForwarderLetSysHandleMsg_forMe:
push xl
push xh
rcall onMessageReceived
pop xh
pop xl
push xl
push xh
rcall mainModulesOnPacketReceived
pop xh
pop xl
push xl
push xh
rcall mainAppsOnPacketReceived
pop xh
pop xl
appForwarderLetSysHandleMsg_end:
ret
; @end
; ---------------------------------------------------------------------------
; @routine appForwarderSendToOtherDev
; @param r16 buffer num
; @param r17 src interface num
appForwarderSendToOtherDev:
ldi yl, LOW(netInterfaceData)
ldi yh, HIGH(netInterfaceData)
ldd r18, Y+NET_IFACE_OFFS_IFACENUM
andi r18, 0x0f
cp r18, r17
breq appForwarderSendToAllDevsBut_check2
bigcall NET_Interface_AddOutgoingMsgNum ; (R17, R18, X)
rjmp appForwarderSendToOtherDev_ret
appForwarderSendToAllDevsBut_check2:
ldi yl, LOW(netInterfaceData2)
ldi yh, HIGH(netInterfaceData2)
ldd r18, Y+NET_IFACE_OFFS_IFACENUM
andi r18, 0x0f
cp r18, r17
breq appForwarderSendToOtherDev_ret
bigcall NET_Interface_AddOutgoingMsgNum ; (R17, R18, X)
appForwarderSendToOtherDev_ret:
ret
; @end
; ---------------------------------------------------------------------------
; @routine appForwarderGetDeviceByIfaceNum
;
; @param r16 interface number
; @return CFLAG set if interface found (cleared otherwise)
; @return Y pointer to interface with given number
; @clobbers r17
appForwarderGetDeviceByIfaceNum:
ldi yl, LOW(netInterfaceData)
ldi yh, HIGH(netInterfaceData)
ldd r17, Y+NET_IFACE_OFFS_IFACENUM
cp r16, r17
breq appForwarderGetDeviceByIfaceNum_secRet
ldi yl, LOW(netInterfaceData2)
ldi yh, HIGH(netInterfaceData2)
ldd r17, Y+NET_IFACE_OFFS_IFACENUM
cp r16, r17
breq appForwarderGetDeviceByIfaceNum_secRet
clc
rjmp appForwarderGetDeviceByIfaceNum_ret
appForwarderGetDeviceByIfaceNum_secRet:
sec
appForwarderGetDeviceByIfaceNum_ret:
ret
; @end

15
avr/apps/hub/0BUILD Normal file
View File

@@ -0,0 +1,15 @@
<?xml?>
<gwbuild>
<subdirs>
</subdirs>
<extradist>
main.asm
</extradist>
</gwbuild>

715
avr/apps/hub/main.asm Normal file
View File

@@ -0,0 +1,715 @@
; ***************************************************************************
; copyright : (C) 2025 by Martin Preuss
; email : martin@libchipcard.de
;
; ***************************************************************************
; * This file is part of the project "AqHome". *
; * Please see toplevel file COPYING of that project for license details. *
; ***************************************************************************
; ***************************************************************************
; defines
; ---------------------------------------------------------------------------
; network interfaces
; ***************************************************************************
; data
.dseg
appHubDataBegin:
appHubRangeBegin: .byte 1
appHubRangeEnd: .byte 1
appHubDataEnd:
; ***************************************************************************
; code
.cseg
; ---------------------------------------------------------------------------
; @routine AppHub_Init @global
AppHub_Init:
ldi xh, HIGH(appHubDataBegin)
ldi xl, LOW(appHubDataBegin)
clr r16
ldi r17, (appHubDataEnd-appHubDataBegin)
rcall Utils_FillSram
; set device address and interface number in all interfaces
ldi r16, 0xf0 ; hub address
ldi r17, 1 ; first interface number
rcall appHubAllSetAddrIfaceNumAndRange ; (R17, R19, R20, Y)
; TODO: read ranges from EEPROM
; TODO: send range msg to all interfaces
ret
; @end
; ---------------------------------------------------------------------------
; @routine AppHub_Run @global
;
; Read messages from any interface, handle them and probably forward to the other
; interfaces.
; @return CFLAG set if something done, cleared otherwise
; @clobbers all
AppHub_Run:
rjmp appHubCheckRecvdMsg
; @end
; ---------------------------------------------------------------------------
; @routine appHubGetDeviceByIfaceNum
;
; @param r16 interface number
; @clobbers r17, r19, r20, Y
appHubGetDeviceByIfaceNum:
ldi r19, COM_PORTS
ldi yl, LOW(com2w0_iface) ; first interface
ldi yh, HIGH(com2w0_iface)
appHubGetDeviceByIfaceNum_loop:
ldd r17, Y+NET_IFACE_OFFS_IFACENUM
cp r16, r17
sec ; mark "found"
breq appHubGetDeviceByIfaceNum_ret
ldi r20, COM2W_IFACE_SIZE
add yl, r20
adc yh, r20
sub yh, r20
dec r19
brne appHubGetDeviceByIfaceNum_loop
clc ; mark "not found"
appHubGetDeviceByIfaceNum_ret:
ret
; @end
; ---------------------------------------------------------------------------
; @routine appHubAllSetAddrIfaceNumAndRange
;
; @clobbers R17, R18, R19, R20, Y
appHubAllSetAddrIfaceNumAndRange:
ldi r19, COM_PORTS
ldi yl, LOW(com2w0_iface) ; first interface
ldi yh, HIGH(com2w0_iface)
ldi r18, 0x10
appHubAllSetAddrIfaceNumAndRange_loop:
; set address
std Y+NET_IFACE_OFFS_ADDRESS, r16
; set interface number
std Y+NET_IFACE_OFFS_IFACENUM, r17
inc r17
; set default range (step of 16)
std Y+NET_IFACE_OFFS_RANGE_BEGIN, r18
subi r18, -15
std Y+NET_IFACE_OFFS_RANGE_END, r18
inc r18
; next interface
ldi r20, COM2W_IFACE_SIZE
add yl, r20
adc yh, r20
sub yh, r20
dec r19
brne appHubAllSetAddrIfaceNumAndRange_loop
ret
; @end
; ---------------------------------------------------------------------------
; @routine appHubSendRangeMsg
;
; @param R18 msg code
; @param Y pointer to interface data
; @return CFLAG set if message enqueued, cleared on error
; @clobbers (R16, R17, R18, R19, R20, R21, R24, R25, X)
appHubSendRangeMsg:
bigcall NET_Buffer_Alloc ; (R16, R17, X)
brcc appHubSendRangeMsg_end
push r16
ldd r20, Y+NET_IFACE_OFFS_RANGE_BEGIN
ldd r21, Y+NET_IFACE_OFFS_RANGE_END
adiw xh:xl, 1
bigcall NETMSG_Range_Write ; (R16, R17, R18, R19, R20, R21)
sbiw xh:xl, 1
pop r16
rcall appHubSendMsg ; (R16, R17, R18, R24, R25, X)
appHubSendRangeMsg_end:
ret
; @end
; ---------------------------------------------------------------------------
; @routine appHubSendDenyAddrR19
;
; @param R19 address to send
; @clobbers R16, R17, R18, R19, R20, R21, X, Y
appHubSendDenyAddrR19:
ldi r18, NETMSG_CMD_DENY_ADDRESS ; deny addr
rjmp appHubSendAddrMsg ; (R16, R17, R18, R19, R20, R21, X, Y)
; @end
; ---------------------------------------------------------------------------
; @routine appHubSendAddrMsg
;
; @param R18 command
; @param R19 address to send
; @clobbers R16 (R17, R18, R19, R20, R21, X, Y)
appHubSendAddrMsg:
bigcall NET_Buffer_Alloc ; (R16, R17, X)
brcc appHubSendAddrMsg_end
push r16
adiw xh:xl, 1
bigcall NETMSG_Address_Write ; (R16, R17, R18, R19, R20, R21)
sbiw xh:xl, 1
pop r16
rcall appHubSendMsg ; (R16, R17, R18, R24, R25, X)
appHubSendAddrMsg_end:
ret
; @end
; ---------------------------------------------------------------------------
; @routine appHubSendValueResponse
;
; @param R17 value id
; @param R19:R18 value
; @param R21:R20 denom (e.g. 100, meaning value must be divided by 100)
; @param R23 command
; @param R25:R24 ref msg id
; @return CFLAG on success, cleared on error
; @clobbers r22 (r16, r17, r18, r19, r20, r21, r23, r24, r25, X)
appHubSendValueResponse:
push r17
rcall NET_Buffer_Alloc ; (R16, R17, X)
pop r17
brcc appHubSendValueResponse_end ; jmp on error
push r16 ; buffer num
ldi r16, 0xff ; DEST addr
clr r22 ; value type
adiw xh:xl, 1
rcall NETMSG_ValueWriteResponse ; (R16, R17, R18, R19, R20, R21, R23, R24, R25)
sbiw xh:xl, 1
pop r16 ; buffer num
rcall NET_Interface_AddOrReleaseOutMsg ; (R16, R17, R18, X)
appHubSendValueResponse_end:
ret
; @end
; ---------------------------------------------------------------------------
; @routine appHubSendMsg
;
; @param R16 num of allocated buffer
; @param Y pointer to interface data
; @param X msg to send (points to start of allocated buffer, e.g. buffer header)
; @return CFLAG set if message enqueued, cleared on error
; @clobbers R16 (R17, R18, R24, R25, X)
appHubSendMsg:
bigcall NET_Interface_AddOutgoingMsgNum ; (R17, R18, X)
brcs appHubSendMsg_end
bigcall NET_Buffer_ReleaseByNum ; (R16, X)
ldi r16, NET_IFACE_OFFS_ERR_NOBUF_LOW
rcall NET_Interface_IncCounter16 ; (R24, R25)
clc
appHubSendMsg_end:
ret
; @end
; ---------------------------------------------------------------------------
; @routine appHubAllResetStats
;
; @clobbers r19, r20, Y (r16, r17, X)
appHubAllResetStats:
ldi r19, COM_PORTS
ldi yl, LOW(com2w0_iface) ; first interface
ldi yh, HIGH(com2w0_iface)
appHubAllResetStats_loop:
bigcall NET_Interface_ResetStats ; (R16, R17, X)
ldi r20, COM2W_IFACE_SIZE
add yl, r20
adc yh, r20
sub yh, r20
dec r19
brne appHubAllResetStats_loop
ret
; @end
; ---------------------------------------------------------------------------
; @routine AppHub_EveryDay @global
;
; @clobbers R16, R17, X
AppHub_EveryDay:
rcall appHubAllResetStats
ret
; @end
; ---------------------------------------------------------------------------
; @routine appHubSendMsgToAllOthers
;
; @param R16 buffer number
; @param X buffer pointer
; @clobbers r17, r19, r20, r24, r25 (r16, r18, X)
appHubSendMsgToAllOthers:
ld r24, X
andi r24, 0x0f ; get sender interface num
ldi r19, COM_PORTS ; number of ports
ldi yl, LOW(com2w0_iface) ; first interface
ldi yh, HIGH(com2w0_iface)
appHubSendMsgToAllOthers_loop:
ldd r25, Y+NET_IFACE_OFFS_IFACENUM
cp r24, r25 ; same interface?
breq appHubSendMsgToAllOthers_next
; current iface is not source, send and inc ref counter
bigcall NET_Interface_AddOutgoingMsgNum ; (R17, R18, X)
brcs appHubSendMsgToAllOthers_added
; inc error counter
push r24
mov r17, r16 ; save r16
ldi r16, NET_IFACE_OFFS_ERR_NOBUF_LOW
bigcall NET_Interface_IncCounter16 ; (R24, R25)
mov r16, r17 ; restore r16
pop r24
rjmp appHubSendMsgToAllOthers_next
appHubSendMsgToAllOthers_added:
mov r17, r16 ; save r16
bigcall NET_Buffer_IncRef ; (r16)
mov r16, r17 ; restore r16
appHubSendMsgToAllOthers_next:
ldi r20, COM2W_IFACE_SIZE
add yl, r20
adc yh, r20
sub yh, r20
dec r19
brne appHubSendMsgToAllOthers_loop
ret
; @end
; ---------------------------------------------------------------------------
; @routine appHubHandleMsgLocally
;
; @param X pointer to received buffer (points to header byte)
; @param Y pointer to IFACE data from which msg was received
; @clobbers all, !X
appHubHandleMsgLocally:
push xl
push xh
rcall appHubHandleMsgLocally_savedX
pop xh
pop xl
rjmp appHubHandleMsgLocally_ret
appHubHandleMsgLocally_savedX:
; get message type
adiw xh:xl, NETMSG_OFFS_CMD+1 ; account for header byte
ld r16, X
sbiw xh:xl, NETMSG_OFFS_CMD+1
cpi r16, NETMSG_CMD_PING
breq appHubHandleMsgLocally_handlePingMsg
cpi r16, NETMSG_CMD_REBOOT_REQUEST
breq appHubHandleMsgLocally_handleRebootMsg
cpi r16, NETMSG_CMD_VALUE_SET
breq appHubHandleMsgLocally_handleSetValue
rjmp appHubHandleMsgLocally_ret
appHubHandleMsgLocally_handlePingMsg:
rjmp appHubHandlePingMsg
appHubHandleMsgLocally_handleRebootMsg:
rjmp appHubHandleRebootMsg
appHubHandleMsgLocally_handleSetValue:
rjmp appHubHandleSetValueMsg
appHubHandleMsgLocally_ret:
ret
; @end
; ---------------------------------------------------------------------------
; @routine appHubHandlePingMsg
;
; @param X pointer to received message
; @clobbers any, !X, !Y
appHubHandlePingMsg:
adiw xh:xl, NETMSG_OFFS_DESTADDR+1
ld r17, X
sbiw xh:xl, NETMSG_OFFS_DESTADDR+1
ldd r16, Y+NET_IFACE_OFFS_ADDRESS
cp r16, r17
breq appHubHandlePingMsg_forMe
cpi r17, 0xff
breq appHubHandlePingMsg_forMe
clc
rjmp appHubHandlePingMsg_end
appHubHandlePingMsg_forMe:
adiw xh:xl, NETMSG_OFFS_SRCADDR+1
ld r17, X
sbiw xh:xl, NETMSG_OFFS_SRCADDR+1
push r17
bigcall NET_Buffer_Alloc ; (R16, R17, X)
pop r17
brcc appHubHandlePingMsg_end ; jmp on error
push r16 ; buffer num
mov r16, r17 ; DEST addr
adiw xh:xl, 1
bigcall NETMSG_Pong_Write ; (R16, R17, R18, R19, R20, X)
sbiw xh:xl, 1
pop r16 ; buffer num
bigcall NET_Interface_AddOrReleaseOutMsg ; (R16, R17, R18, X)
appHubHandlePingMsg_end:
ret
; @end
; ---------------------------------------------------------------------------
; @routine appHubHandleRebootMsg
;
; Doesn't return if reboot msg is valid.
;
; @param X pointer to received message
appHubHandleRebootMsg:
rcall NETMSG_RebootRequestRead
brcc appHubHandleRebootMsg_end
; reboot
cli
bigjmp BOOTLOADER_ADDR
appHubHandleRebootMsg_end:
ret
; @end
; ---------------------------------------------------------------------------
; @routine appHubHandleSetValueMsg
;
; @param X pointer to received message
; @param Y pointer to IFACE data from which msg was received
; @clobbers all, !Y
appHubHandleSetValueMsg:
adiw xh:xl, NETMSG_OFFS_DESTADDR+1
ld r17, X
sbiw xh:xl, NETMSG_OFFS_DESTADDR+1
ldd r16, Y+NET_IFACE_OFFS_ADDRESS
cp r16, r17
breq appHubHandleSetValueMsg_forMe
cpi r17, 0xff
breq appHubHandleSetValueMsg_forMe
rjmp appHubHandleSetValueMsg_ret
appHubHandleSetValueMsg_forMe:
rcall NETMSG_ValueRead ; (none)
cpi r17, VALUE_ID_HUB_SETRANGE1
brcs appHubHandleSetValueMsg_ret
cpi r17, VALUE_ID_HUB_SETRANGE8+1
brcc appHubHandleSetValueMsg_ret
appHubHandleSetValueMsg_setRange:
push yl
push yh
subi r17, (VALUE_ID_HUB_SETRANGE1-1)
push r18
push r19
; send ACK back the same interface the request came from
push r17 ; interface number
ldi r23, NETMSG_CMD_VALUE_SET_ACK
rcall appHubSendValueResponse ; r22 (r16, r17, r18, r19, r20, r21, r23, r24, r25, X)
pop r16 ; pop interface number to r16 (from r17)
rcall appHubGetDeviceByIfaceNum ; Y=interface to modify (r17, r19, r20)
pop r19
pop r18
; modify interface
std Y+NET_IFACE_OFFS_RANGE_BEGIN, r18
std Y+NET_IFACE_OFFS_RANGE_END, r19
; TODO: store new config
; let subnodes of modified interface re-eunumerate
ldi r18, NETMSG_CMD_REENUM
rcall appHubSendRangeMsg ; (R16, R17, R18, R19, R20, R21, X)
pop yh
pop yl
appHubHandleSetValueMsg_ret:
ret
; @end
; ---------------------------------------------------------------------------
; @routine appHubHandleRoutingMsg
;
; @param X pointer to received buffer (points to header byte)
; @param Y pointer to IFACE data from which msg was received
; @clobbers all, !X
appHubHandleRoutingMsg:
push xl
push xh
rcall appHubHandleRoutingMsg_savedX
pop xh
pop xl
rjmp appHubHandleRoutingMsg_ret
appHubHandleRoutingMsg_savedX:
; get message type
adiw xh:xl, NETMSG_OFFS_CMD+1 ; account for header byte
ld r16, X
sbiw xh:xl, NETMSG_OFFS_CMD+1
cpi r16, NETMSG_CMD_NEED_ADDRESS
breq appHubHandleRoutingMsg_handleNeedAddress
cpi r16, NETMSG_CMD_CLAIM_ADDRESS
breq appHubHandleRoutingMsg_handleClaimAddress
clc
rjmp appHubHandleRoutingMsg_ret
appHubHandleRoutingMsg_handleNeedAddress:
rcall appHubHandleNeedAddressMsg
rjmp appHubHandleRoutingMsg_secRet
appHubHandleRoutingMsg_handleClaimAddress:
rcall appHubHandleClaimAddressMsg
appHubHandleRoutingMsg_secRet:
sec
appHubHandleRoutingMsg_ret:
ret
; @end
; ---------------------------------------------------------------------------
; @routine appHubHandleNeedAddressMsg
;
; @param X pointer to received message
; @param Y pointer to IFACE data from which msg was received
; @clobbers all, !Y
appHubHandleNeedAddressMsg:
ldi r18, NETMSG_CMD_ADDRESS_RANGE
rcall appHubSendRangeMsg ; (R16, R17, R18, R19, R20, R21, X)
ret
; @end
; ---------------------------------------------------------------------------
; @routine appHubHandleClaimAddressMsg
;
; @param X pointer to received message
; @param Y pointer to IFACE data from which msg was received
; @clobbers all, !Y
appHubHandleClaimAddressMsg:
rcall NETMSG_Address_Read ; R18=cmd, R19=addr(R18, R19)
rcall appHubIsR19InRange
brcs appHubHandleClaimAddressMsg_end
; is not in subnet range, deny
rcall appHubSendDenyAddrR19 ; (R16, R17, R18, R19, R20, R21, X)
appHubHandleClaimAddressMsg_end:
ret
; @end
; ---------------------------------------------------------------------------
; @routine appHubIsR19InRange
;
; @param R19 address to check against range
; @param Y pointer to IFACE data
; @clobbers R16
appHubIsR19InRange:
ldd r16, Y+NET_IFACE_OFFS_RANGE_BEGIN
cp r19, r16
brcs appHubIsR19InRangeClcRet
ldd r16, Y+NET_IFACE_OFFS_RANGE_END
cp r16, r19
brcs appHubIsR19InRangeClcRet
sec
rjmp appHubIsR19InRange_end
appHubIsR19InRangeClcRet:
clc
appHubIsR19InRange_end:
ret
; @end
; ---------------------------------------------------------------------------
; @routine appHubCheckRecvdMsg
;
; Read messages from any interface and forward to the other ones.
;
; @return CFLAG set if something done, cleared otherwise
appHubCheckRecvdMsg:
rcall NET_PeekNextIncomingMsgNum ; check read queue (R16=bufNum)
brcc appHubCheckRecvdMsg_end ; no msg, jmp
rcall NET_Buffer_Locate ; (R17)
rcall appHubHandleRecvdMsg ; (all, !X)
rcall NET_GetNextIncomingMsgNum ; take off the queue
rcall NET_Buffer_ReleaseByNum ; (R16, X)
sec ; we had a message, so something was done
appHubCheckRecvdMsg_end:
ret
; @end
; ---------------------------------------------------------------------------
; @routine appHubHandleRecvdMsg
;
; @param R16 buffer number
; @param X pointer to received message
; @clobbers all, !X
appHubHandleRecvdMsg:
mov r18, r16
ld r16, X
andi r16, 0x0f
rcall appHubGetDeviceByIfaceNum ; Y=source iface (r17, r19, r20)
brcc appHubHandleRecvdMsg_ret
; filter out routing msgs
push yl
push yh
push r18
rcall appHubHandleRoutingMsg ; (all, !X)
pop r18
pop yh
pop yl
brcs appHubHandleRecvdMsg_ret
; check for PING, SETVALUE etc
push r18
push yl
push yh
rcall appHubHandleMsgLocally ; (all, !X)
pop yh
pop yl
; let other apps and modules handle message
push xl
push xh
bigcall mainHandleMessages
pop xh
pop xl
pop r18
; forward message to all other interfaces
mov r16, r18 ; buffer number
push xl
push xh
rcall appHubSendMsgToAllOthers ; (r17, r19, r20, r24, r25 (r16, r18, X)
pop xh
pop xl
appHubHandleRecvdMsg_ret:
ret
; @end
; ---------------------------------------------------------------------------
; @routine appHubWriteConfToEeprom
;
; @clobbers R16, X (R17)
appHubWriteConfToEeprom:
; write range begin
ldi xl, LOW(EEPROM_OFFS_ROUTER_RANGE_BEGIN)
ldi xh, HIGH(EEPROM_OFFS_ROUTER_RANGE_BEGIN)
lds r16, appHubRangeBegin
rcall Eeprom_WriteByteIfChanged ; (R17)
brcc appHubWriteConfToEeprom_end
; write range end
ldi xl, LOW(EEPROM_OFFS_ROUTER_RANGE_END)
ldi xh, HIGH(EEPROM_OFFS_ROUTER_RANGE_END)
lds r16, appHubRangeEnd
rcall Eeprom_WriteByteIfChanged ; (R17)
appHubWriteConfToEeprom_end:
ret
; @end
#if 0
; ---------------------------------------------------------------------------
; @routine appHubReadConfFromEeprom
;
; @clobbers R16, X (R17)
appHubReadConfFromEeprom:
; read range begin
ldi xl, LOW(EEPROM_OFFS_ROUTER_RANGE_BEGIN)
ldi xh, HIGH(EEPROM_OFFS_ROUTER_RANGE_BEGIN)
rcall Eeprom_ReadByte
brcc appHubReadConfFromEeprom_end
cpi r16, 0xff
breq appHubReadConfFromEeprom_okay ; not set, jmp
cpi r16, 2 ; range should at least start at 2 to assign 1 to router
brcs appHubReadConfFromEeprom_okay
sts appHubRangeBegin, r16
dec r16
sts netInterfaceData+NET_IFACE_OFFS_ADDRESS, r16 ; use addr rangeBegin-1 for router itself
sts netInterfaceData2+NET_IFACE_OFFS_ADDRESS, r16 ; use same address for both interfaces to save on addresses
; read range end
ldi xl, LOW(EEPROM_OFFS_ROUTER_RANGE_END)
ldi xh, HIGH(EEPROM_OFFS_ROUTER_RANGE_END)
rcall Eeprom_ReadByte
brcc appHubReadConfFromEeprom_end
cpi r16, 0xff
breq appHubReadConfFromEeprom_okay
sts appHubRangeEnd, r16
appHubReadConfFromEeprom_okay:
sec
appHubReadConfFromEeprom_end:
ret
; @end
#endif

View File

@@ -100,29 +100,56 @@ AppNetwork_Every100ms_jump:
; ---------------------------------------------------------------------------
; @routine AppNetwork_EveryDay @global
;
; @clobbers R16, R17, X
AppNetwork_EveryDay:
bigjmp NET_Interface_ResetStats ; (R16, R17, X)
; @end
; ---------------------------------------------------------------------------
; @routine AppNetwork_HandleMsg @global
;
; @param X pointer to received message
AppNetwork_HandleMsg:
push xl
push xh
rcall AppNetwork_HandleMsg_savedX
pop xh
pop xl
rjmp AppNetwork_HandleMsg_end
AppNetwork_HandleMsg_savedX:
adiw xh:xl, NETMSG_OFFS_CMD
ld r16, X
sbiw xh:xl, NETMSG_OFFS_CMD
cpi r16, NETMSG_CMD_REBOOT_REQUEST
breq AppNetwork_HandleMsg_handleRebootMsg
cpi r16, NETMSG_CMD_PING
breq AppNetwork_HandleMsg_handlePingMsg
cpi r16, NETMSG_CMD_REENUM
breq AppNetwork_HandleMsg_handleReenumMsg
cpi r16, NETMSG_CMD_NEED_ADDRESS
brcs AppNetwork_HandleMsg_clcRet ; lower than "HAVE_NEED"
cpi r16, NETMSG_CMD_ADDRESS_RANGE
breq AppNetwork_HandleMsg_handleRangeMsg
brcc AppNetwork_HandleMsg_clcRet ; higher or equal to "ADDR_RANGE"
rjmp AppNetwork_HandleMsg_handleAddrMsg
AppNetwork_HandleMsg_handleReenumMsg:
rjmp appNetworkHandleReeunumRequest
AppNetwork_HandleMsg_handleRangeMsg:
; TODO
bigcall NETMSG_Range_Read
std Y+NET_IFACE_OFFS_RANGE_BEGIN, r20
std Y+NET_IFACE_OFFS_RANGE_END, r21
std Y+NET_IFACE_OFFS_ADDRESS, r20
rjmp AppNetwork_HandleMsg_clcRet
AppNetwork_HandleMsg_handleAddrMsg:
rcall NETMSG_Address_Read ; R18=cmd, R19=addr(R18, R19)
bigcall NETMSG_Address_Read ; R18=cmd, R19=addr(R18, R19)
mov r16, r18
subi r16, NETMSG_CMD_NEED_ADDRESS
ldi zl, LOW(appNetworkMsgTable)
@@ -132,14 +159,13 @@ AppNetwork_HandleMsg_handleAddrMsg:
sub zh, r16
ijmp
AppNetwork_HandleMsg_handleRebootMsg:
push xl
push xh
rcall appNetworkHandleRebootRequest
pop xh
pop xl
rcall appNetworkHandleRebootRequest
ret
AppNetwork_HandleMsg_handlePingMsg:
rjmp appNetworkHandlePingRequest
AppNetwork_HandleMsg_clcRet:
clc
AppNetwork_HandleMsg_end:
ret
; @end
@@ -152,16 +178,66 @@ AppNetwork_HandleMsg_clcRet:
appNetworkHandleRebootRequest:
rcall NETMSG_RebootRequestRead
brcc appNetworkHandleRebootRequest_end
brcc appNetworkHandleRebootRequest_end ; uid doesn't match
; reboot
cli
rjmp BOOTLOADER_ADDR
bigjmp BOOTLOADER_ADDR
appNetworkHandleRebootRequest_end:
ret
; @end
appNetworkHandlePingRequest:
ld r17, X
lds r16, (netInterfaceData+NET_IFACE_OFFS_ADDRESS)
cp r16, r17
breq appNetworkHandlePingRequest_forMe
cpi r17, 0xff
breq appNetworkHandlePingRequest_forMe
clc
rjmp appNetworkHandlePingRequest_end
appNetworkHandlePingRequest_forMe:
adiw xh:xl, NETMSG_OFFS_SRCADDR
ld r17, X
push r17
bigcall NET_Buffer_Alloc ; (R16, R17, X)
pop r17
brcc appNetworkHandlePingRequest_end ; jmp on error
push r16 ; buffer num
mov r16, r17 ; DEST addr
adiw xh:xl, 1
ldi yl, LOW(netInterfaceData)
ldi yh, HIGH(netInterfaceData)
bigcall NETMSG_Pong_Write ; (R16, R17, R18, R19, R20, X)
sbiw xh:xl, 1
pop r16 ; buffer num
bigcall NET_Interface_AddOrReleaseOutMsg ; (R16, R17, R18, X)
appNetworkHandlePingRequest_end:
ret
appNetworkHandleReeunumRequest:
push xl
push xh
bigcall Utils_ReadUid ; r21:r20:r19:r18=uid (r16, X)
pop xh
pop xl
rcall NETMSG_Range_Read ; r20=range begin, r21=range end (none)
ldi r16, APP_NETWORK_STATE_INITIALWAIT
std Y+NET_IFACE_OFFS_STATUS, r16
cpi r18, 20
brcc appNetworkHandleReeunumRequest_setWait
subi r18, -20 ; minimum 2s
appNetworkHandleReeunumRequest_setWait:
std Y+NET_IFACE_OFFS_STATETIMER, r18 ; use lowest byte of uid as wat time
std Y+NET_IFACE_OFFS_RANGE_BEGIN, r20
std Y+NET_IFACE_OFFS_RANGE_END, r21
ret
; @end
appNetworkTimerTable:
rjmp appNetworkHandleStateInitialWait
@@ -209,20 +285,11 @@ appNetworkHandleStateHaveAddress2:
std Y+NET_IFACE_OFFS_STATUS, r16
ldi r16, APP_NETWORK_TIMER_100MS
std Y+NET_IFACE_OFFS_STATETIMER, r16
ldd r16, Y+NET_IFACE_OFFS_RANGE_BEGIN ; set interface address
ldd r17, Y+NET_IFACE_OFFS_ADDRESS
cp r16, r17
breq appNetworkHandleStateHaveAddress2_end
; store new address in IFACE and in EEPROM
std Y+NET_IFACE_OFFS_ADDRESS, r16
push r15
in r15, SREG
cli
ldi xl, LOW(EEPROM_OFFS_COMADDR)
ldi xh, HIGH(EEPROM_OFFS_COMADDR)
rcall Utils_WriteEepromIncr ; write address to EEPROM
out SREG, r15
pop r15
ldd r16, Y+NET_IFACE_OFFS_ADDRESS
ldi xl, LOW(EEPROM_OFFS_COMADDR)
ldi xh, HIGH(EEPROM_OFFS_COMADDR)
bigcall Eeprom_WriteByteIfChanged ; write address to EEPROM (R17)
appNetworkHandleStateHaveAddress2_end:
ret
@@ -233,7 +300,6 @@ appNetworkHandleStateUp:
ret
; ---------------------------------------------------------------------------
; @routine appNetworkSendMsgNextState
;
@@ -241,7 +307,7 @@ appNetworkHandleStateUp:
; @clobbers R16, R19 (R17, R18, R20, R21, X)
appNetworkSendMsgNextState:
ldd r19, Y+NET_IFACE_OFFS_RANGE_BEGIN
ldd r19, Y+NET_IFACE_OFFS_ADDRESS
rcall appNetworkSendAddrMsg ; (R16, R17, R18, R19, R20, R21, X)
brcc appNetworkSendMsgNextState_retry
ldd r16, Y+NET_IFACE_OFFS_STATUS
@@ -306,9 +372,9 @@ appNetworkHandleMsgClaimAddr:
cp r19, r16
brne appNetworkHandleMsgClaimAddr_end ; not our address
ldd r16, Y+NET_IFACE_OFFS_STATUS
cpi r16, APP_NETWORK_STATE_UP ; up?
brne appNetworkHandleMsgClaimAddr_end ; nope, ignore
; network is up, someone claimed our address, deny it
cpi r16, APP_NETWORK_STATE_CLAIMADDRESS1
brcs appNetworkHandleMsgClaimAddr_end ; nope, ignore
; network is somewhat up, someone claimed our address, deny it
ldi r18, NETMSG_CMD_DENY_ADDRESS ; deny our addr
ldd r19, Y+NET_IFACE_OFFS_ADDRESS
rjmp appNetworkSendAddrMsg
@@ -330,12 +396,12 @@ appNetworkHandleMsgDenyAddr:
cpi r16, APP_NETWORK_STATE_UP
breq appNetworkHandleMsgDenyAddr_end ; ignore (our network stack is up)
; still setting up address, check whether the last one is denied now
ldd r16, Y+NET_IFACE_OFFS_RANGE_BEGIN
ldd r16, Y+NET_IFACE_OFFS_ADDRESS
cp r19, r16 ; our claimed address?
brne appNetworkHandleMsgDenyAddr_end ; nope, jump
; try next address (if any left)
ldd r17, Y+NET_IFACE_OFFS_RANGE_END
inc r16 ; RANGE_BEGIN+1
inc r16 ; next address
cp r17, r16 ; smaller than or equal to RANGE_END?
brcc appNetworkHandleMsgDenyAddr_claimNext
; out of addresses, start completely new after some waiting time
@@ -345,7 +411,7 @@ appNetworkHandleMsgDenyAddr:
rjmp appNetworkHandleMsgDenyAddr_end
appNetworkHandleMsgDenyAddr_claimNext:
; send CLAIM_ADDR for next address (new state: APP_NETWORK_STATE_NEEDADDRESS+1)
std Y+NET_IFACE_OFFS_RANGE_BEGIN, r16
std Y+NET_IFACE_OFFS_ADDRESS, r16
ldi r16, APP_NETWORK_STATE_NEEDADDRESS
std Y+NET_IFACE_OFFS_STATUS, r16
ldi r18, NETMSG_CMD_CLAIM_ADDRESS
@@ -365,16 +431,16 @@ appNetworkHandleMsgDenyAddr_end:
; @clobbers R16 (R17, R18, R19, R20, R21, X)
appNetworkSendAddrMsg:
rcall NET_Buffer_Alloc ; (R16, R17, X)
bigcall NET_Buffer_Alloc ; (R16, R17, X)
brcc appNetworkSendAddrMsg_end
adiw xh:xl, 1
push r16
rcall NETMSG_Address_Write ; (R16, R17, R18, R19, R20, R21)
bigcall NETMSG_Address_Write ; (R16, R17, R18, R19, R20, R21)
pop r16
sbiw xh:xl, 1
rcall NET_Interface_AddOutgoingMsgNum ; (R17, R18, X)
bigcall NET_Interface_AddOutgoingMsgNum ; (R17, R18, X)
brcs appNetworkSendAddrMsg_end
rcall NET_Buffer_ReleaseByNum ; (R16, X)
bigcall NET_Buffer_ReleaseByNum ; (R16, X)
clc
appNetworkSendAddrMsg_end:
ret
@@ -396,6 +462,7 @@ appNetworkResetState:
std Y+NET_IFACE_OFFS_STATETIMER, r16
ldi r16, APP_NETWORK_ADDRESS_RANGE_BEGIN
std Y+NET_IFACE_OFFS_RANGE_BEGIN, r16
std Y+NET_IFACE_OFFS_ADDRESS, r16
rcall appNetworkGetAddressFromEeprom ; R16=addr (R15, X)
tst r16
breq appNetworkResetState_setRangeEnd
@@ -423,7 +490,7 @@ appNetworkGetAddressFromEeprom:
cli
ldi xl, LOW(EEPROM_OFFS_COMADDR)
ldi xh, HIGH(EEPROM_OFFS_COMADDR)
rcall Utils_ReadEepromIncr ; (R16)
bigcall Utils_ReadEepromIncr ; (R16)
out SREG, r15
pop r15
ret

View File

@@ -18,14 +18,20 @@
; @clobbers R16, X (R17, R18, R19, R20, R21, Z)
AppNetwork_SendTxdStats:
rcall NET_Buffer_Alloc ; (R16, R17, X)
bigcall NET_Buffer_Alloc ; (R16, R17, X)
brcc AppNetwork_SendTxdStats_end
push r16
adiw xh:xl, 1
rcall NETMSG_SendStats_Write ; (R16, R17, R18, R19, R20, R21, Z)
bigcall NETMSG_SendStats_Write ; (R16, R17, R18, R19, R20, R21, Z)
sbiw xh:xl, 1
pop r16
rcall NET_Interface_AddOrReleaseOutMsg ; (R16, R17, R18, X)
push yl
push yh
ldi yl, LOW(netInterfaceData)
ldi yh, HIGH(netInterfaceData)
bigcall NET_Interface_AddOrReleaseOutMsg ; (R16, R17, R18, X)
pop yh
pop yl
AppNetwork_SendTxdStats_end:
ret
; @end
@@ -35,18 +41,24 @@ AppNetwork_SendTxdStats_end:
; ---------------------------------------------------------------------------
; @routine AppNetwork_SendRxdStats
; @param Y network interface to work with
; @param Y network interface whose stats to send
; @clobbers R16, X (R17, R18, R19, R20, R21, Z)
AppNetwork_SendRxdStats:
rcall NET_Buffer_Alloc ; (R16, R17, X)
bigcall NET_Buffer_Alloc ; (R16, R17, X)
brcc AppNetwork_SendRxdStats_end
push r16
adiw xh:xl, 1
rcall NETMSG_RecvStats_Write ; (R16, R17, R18, R19, R20, R21, Z)
bigcall NETMSG_RecvStats_Write ; (R16, R17, R18, R19, R20, R21, Z)
sbiw xh:xl, 1
pop r16
rcall NET_Interface_AddOrReleaseOutMsg ; (R16, R17, R18, X)
push yl
push yh
ldi yl, LOW(netInterfaceData)
ldi yh, HIGH(netInterfaceData)
bigcall NET_Interface_AddOrReleaseOutMsg ; (R16, R17, R18, X)
pop yh
pop yl
AppNetwork_SendRxdStats_end:
ret
; @end
@@ -54,20 +66,25 @@ AppNetwork_SendRxdStats_end:
; ---------------------------------------------------------------------------
; @routine AppNetwork_SendRxdStats
; @routine AppNetwork_SendMemStats
; @param Y network interface to work with
; @clobbers R16, X (R17, R18, R19, R20, R21, Z)
AppNetwork_SendMemStats:
rcall NET_Buffer_Alloc ; (R16, R17, X)
bigcall NET_Buffer_Alloc ; (R16, R17, X)
brcc AppNetwork_SendMemStats_end
push r16
adiw xh:xl, 1
rcall NETMSG_MemStats_Write ; (R16, R17, R18, R19, R20, R21)
bigcall NETMSG_MemStats_Write ; (R16, R17, R18, R19, R20, R21)
sbiw xh:xl, 1
pop r16
rcall NET_Interface_AddOrReleaseOutMsg ; (R16, R17, R18, X)
push yl
push yh
ldi yl, LOW(netInterfaceData)
ldi yh, HIGH(netInterfaceData)
bigcall NET_Interface_AddOrReleaseOutMsg ; (R16, R17, R18, X)
pop yh
pop yl
AppNetwork_SendMemStats_end:
ret
; @end
@@ -81,14 +98,14 @@ AppNetwork_SendMemStats_end:
; @clobbers R16, X (R17, R18, R19, R20, R21, Z)
AppNetwork_SendDevice:
rcall NET_Buffer_Alloc ; (R16, R17, X)
bigcall NET_Buffer_Alloc ; (R16, R17, X)
brcc AppNetwork_SendDevice_end
push r16
adiw xh:xl, 1
rcall NETMSG_Device_Write ; (R16, R17, R18, R19, R20, R21, Z)
bigcall NETMSG_Device_Write ; (R16, R17, R18, R19, R20, R21, Z)
sbiw xh:xl, 1
pop r16
rcall NET_Interface_AddOrReleaseOutMsg ; (R16, R17, R18, X)
bigcall NET_Interface_AddOrReleaseOutMsg ; (R16, R17, R18, X)
AppNetwork_SendDevice_end:
ret
; @end

View File

@@ -11,7 +11,7 @@
; ***************************************************************************
; defines
.equ APP_REPORT_SENSORS_INTERVAL_SECS = 60
.equ APP_REPORT_SENSORS_INTERVAL_SECS = 120
@@ -78,35 +78,45 @@ AppReportSensors_OnEverySecond_store:
#ifdef MODULES_SI7021
cpi r16, 1
breq AppReportSensors_OnEverySecond_measureValue1
cpi r16, 19
cpi r16, 11
breq AppReportSensors_OnEverySecond_measureValue2
cpi r16, 39
cpi r16, 16
breq AppReportSensors_OnEverySecond_sendValue1
cpi r16, 49
cpi r16, 21
breq AppReportSensors_OnEverySecond_sendValue2
#endif
#ifdef MODULES_SGP40
cpi r16, 27
cpi r16, 32
breq AppReportSensors_OnEverySecond_measureValue4
cpi r16, 55
cpi r16, 42
breq AppReportSensors_OnEverySecond_sendValue4
#endif
#ifdef MODULES_SGP30
cpi r16, 29
breq AppReportSensors_OnEverySecond_measureValue5
cpi r16, 57
cpi r16, 53
breq AppReportSensors_OnEverySecond_sendValue5
cpi r16, 59
cpi r16, 63
breq AppReportSensors_OnEverySecond_sendValue6
#endif
#ifdef MODULES_DS18B20
cpi r16, 9
cpi r16, 84
breq AppReportSensors_OnEverySecond_sendValue3
#endif
#ifdef MODULES_CCS811
cpi r16, 94
breq AppReportSensors_OnEverySecond_sendCCS811_TVOC
cpi r16, 104
breq AppReportSensors_OnEverySecond_sendCCS811_CO2
#endif
#ifdef MODULES_BRIGHTNESS
cpi r16, 97
breq AppReportSensors_OnEverySecond_sendBrightness
#endif
ret
#ifdef MODULES_SI7021
@@ -133,13 +143,23 @@ AppReportSensors_OnEverySecond_sendValue3:
#endif
#ifdef MODULES_SGP30
AppReportSensors_OnEverySecond_measureValue5:
rjmp SGP30_Measure
AppReportSensors_OnEverySecond_sendValue5:
rjmp SGP30_SendTVOC
ret
AppReportSensors_OnEverySecond_sendValue6:
rjmp SGP30_SendCO2
AppReportSensors_OnEverySecond_sendValue5:
rjmp SGP30_SendTVOC
ret
AppReportSensors_OnEverySecond_sendValue6:
rjmp SGP30_SendCO2
#endif
#ifdef MODULES_CCS811
AppReportSensors_OnEverySecond_sendCCS811_TVOC:
rjmp CCS811_SendTVOC
AppReportSensors_OnEverySecond_sendCCS811_CO2:
rjmp CCS811_SendCO2
#endif
#ifdef MODULES_BRIGHTNESS
AppReportSensors_OnEverySecond_sendBrightness:
rjmp Brightness_Send
#endif
; @end

15
avr/apps/router/0BUILD Normal file
View File

@@ -0,0 +1,15 @@
<?xml?>
<gwbuild>
<subdirs>
</subdirs>
<extradist>
main.asm
</extradist>
</gwbuild>

579
avr/apps/router/main.asm Normal file
View File

@@ -0,0 +1,579 @@
; ***************************************************************************
; copyright : (C) 2025 by Martin Preuss
; email : martin@libchipcard.de
;
; ***************************************************************************
; * This file is part of the project "AqHome". *
; * Please see toplevel file COPYING of that project for license details. *
; ***************************************************************************
; ***************************************************************************
; defines
; ---------------------------------------------------------------------------
; network interfaces
.equ NETDEV0_IFACENUM = 1
.equ NETDEV1_IFACENUM = 2
; ***************************************************************************
; data
.dseg
appRouterDataBegin:
appRouterRangeBegin: .byte 1
appRouterRangeEnd: .byte 1
appRouterDataEnd:
; ***************************************************************************
; code
.cseg
; ---------------------------------------------------------------------------
; @routine AppRouter_Init @global
AppRouter_Init:
ldi xh, HIGH(appRouterDataBegin)
ldi xl, LOW(appRouterDataBegin)
clr r16
ldi r17, (appRouterDataEnd-appRouterDataBegin)
rcall Utils_FillSram
#ifndef APP_ROUTER_NO_ADDR_MGR
ldi r16, 0xe0 ; default range from 0xe0-0xef (will be changed later)
sts netInterfaceData+NET_IFACE_OFFS_ADDRESS, r16 ; use first address for router itself
sts netInterfaceData2+NET_IFACE_OFFS_ADDRESS, r16 ; use same address for both interfaces to save on addresses
inc r16
sts appRouterRangeBegin, r16 ; range from router+1
ldi r16, 0xef
sts appRouterRangeEnd, r16
rcall appRouterReadConfFromEeprom ; try to read config from EEPROM
#endif
; set interface number for NETDEV0
ldi r16, NETDEV0_IFACENUM
sts netInterfaceData+NET_IFACE_OFFS_IFACENUM, r16
; set interface number for NETDEV1
ldi r16, NETDEV1_IFACENUM
sts netInterfaceData2+NET_IFACE_OFFS_IFACENUM, r16
#ifndef APP_ROUTER_NO_ADDR_MGR
ldi r18, NETMSG_CMD_ADDRESS_RANGE
rcall appRouterSendRangeMsgToDev1
#endif
ret
; @end
; ---------------------------------------------------------------------------
; @routine AppRouter_EveryDay @global
;
; @clobbers R16, R17, X
AppRouter_EveryDay:
ldi yl, LOW(netInterfaceData)
ldi yh, HIGH(netInterfaceData)
bigcall NET_Interface_ResetStats ; (R16, R17, X)
ldi yl, LOW(netInterfaceData2)
ldi yh, HIGH(netInterfaceData2)
bigcall NET_Interface_ResetStats ; (R16, R17, X)
ret
; @end
; ---------------------------------------------------------------------------
; @routine AppRouter_Run @global
;
; Read messages from either interface and forward to the other one.
AppRouter_Run:
rjmp appRouterCheckRecvdMsg
; @end
; ---------------------------------------------------------------------------
; @routine appRouterHandleMsgAnyDev @global
;
; @param X pointer to received message
; @return CFLAG set if msg handled, cleared otherwise
; @clobbers any, !X
appRouterHandleMsgAnyDev:
push xl
push xh
rcall appRouterHandleMsgAnyDev_savedX
pop xh
pop xl
rjmp appRouterHandleMsgAnyDev_end
appRouterHandleMsgAnyDev_savedX:
adiw xh:xl, NETMSG_OFFS_CMD ; maybe move ping/reboot handling to all/main.asm?
ld r16, X
sbiw xh:xl, NETMSG_OFFS_CMD
cpi r16, NETMSG_CMD_REBOOT_REQUEST
breq appRouterHandleMsgAnyDev_handleRebootMsg
cpi r16, NETMSG_CMD_PING
breq appRouterHandleMsgAnyDev_handlePingMsg
cpi r16, NETMSG_CMD_VALUE_SET
breq appRouterHandleMsgAnyDev_handleSetValue
rjmp appRouterHandleMsgAnyDev_clcRet
appRouterHandleMsgAnyDev_handleRebootMsg:
rcall appRouterHandleRebootRequest
ret
appRouterHandleMsgAnyDev_handlePingMsg:
rcall appRouterHandlePingRequest
clc
ret
appRouterHandleMsgAnyDev_handleSetValue:
rcall NETMSG_ValueRead ; (none)
cpi r17, VALUE_ID_ROUTER_SETRANGE
breq appRouterHandleMsgAnyDev_handleSetRange
rjmp appRouterHandleMsgAnyDev_clcRet
appRouterHandleMsgAnyDev_handleSetRange:
#ifndef APP_ROUTER_NO_ADDR_MGR
sts netInterfaceData+NET_IFACE_OFFS_ADDRESS, r18 ; use first address for router itself
sts netInterfaceData2+NET_IFACE_OFFS_ADDRESS, r18 ; use same address for both interfaces to save on addresses
inc r18 ; start range after router
sts appRouterRangeBegin, r18
sts appRouterRangeEnd, r19
; send ACK
ldi r23, NETMSG_CMD_VALUE_SET_ACK
rcall Main_SendValueResponse ; (clobbers all, including Y)
; let subnodes re-eunumerate
ldi r18, NETMSG_CMD_REENUM
rcall appRouterSendRangeMsgToDev1 ; (R16, R17, R18, R19, R20, R21, X, Y)
rcall appRouterWriteConfToEeprom ; (r16, r17, X)
#endif
sec
ret
appRouterHandleMsgAnyDev_clcRet:
clc
appRouterHandleMsgAnyDev_end:
ret
; @end
; ---------------------------------------------------------------------------
; @routine appRouterHandleDev1Msg @global
;
; Handle messages from controlled subnet.
;
; @param X pointer to received message
; @return CFLAG set if msg handled, cleared otherwise
; @clobbers any, !X
appRouterHandleDev1Msg:
#ifndef APP_ROUTER_NO_ADDR_MGR
push xl
push xh
rcall appRouterHandleDev1Msg_savedX
pop xh
pop xl
rjmp appRouterHandleDev1Msg_end
appRouterHandleDev1Msg_savedX:
adiw xh:xl, NETMSG_OFFS_CMD
ld r16, X
sbiw xh:xl, NETMSG_OFFS_CMD
cpi r16, NETMSG_CMD_NEED_ADDRESS
breq appRouterHandleDev1Msg_handleNeedAddr
cpi r16, NETMSG_CMD_CLAIM_ADDRESS
breq appRouterHandleDev1Msg_handleClaimAddr
; add more here
ldi r16, 0
rjmp appRouterHandleDev1Msg_clcRet
appRouterHandleDev1Msg_handleNeedAddr:
ldi r18, NETMSG_CMD_ADDRESS_RANGE
rcall appRouterSendRangeMsgToDev1 ; (R16, R17, R18, R19, R20, R21, X, Y)
sec
rjmp appRouterHandleDev1Msg_end
appRouterHandleDev1Msg_handleClaimAddr:
rcall NETMSG_Address_Read ; R18=cmd, R19=addr(R18, R19)
rcall appRouterIsR19InRange
brcs appRouterHandleDev1Msg_end
; is not in subnet range, deny
rcall appRouterSendDenyAddrR19ToDev1 ; (R16, R17, R18, R19, R20, R21, X, Y)
sec
rjmp appRouterHandleDev1Msg_end
#endif
appRouterHandleDev1Msg_clcRet:
clc
appRouterHandleDev1Msg_end:
ret
; @end
; ---------------------------------------------------------------------------
; @routine appRouterIsR19InRange
;
; @param R19 address to check against range
; @clobbers R16
appRouterIsR19InRange:
lds r16, appRouterRangeBegin
cp r19, r16
brcs appRouterIsR19InRangeClcRet
lds r16, appRouterRangeEnd
cp r16, r19
brcs appRouterIsR19InRangeClcRet
sec
rjmp appRouterIsR19InRange_end
appRouterIsR19InRangeClcRet:
clc
appRouterIsR19InRange_end:
ret
; @end
; ---------------------------------------------------------------------------
; @routine appRouterSendDenyAddrR19ToDev1
;
; @param R19 address to send
; @clobbers R16, R17, R18, R19, R20, R21, X, Y
appRouterSendDenyAddrR19ToDev1:
ldi r18, NETMSG_CMD_DENY_ADDRESS ; deny addr
rjmp appRouterSendAddrMsgToDev1 ; (R16, R17, R18, R19, R20, R21, X, Y)
; @end
; ---------------------------------------------------------------------------
; @routine appRouterSendAddrMsgToDev1
;
; @param R18 command
; @param R19 address to send
; @clobbers R16 (R17, R18, R19, R20, R21, X, Y)
appRouterSendAddrMsgToDev1:
bigcall NET_Buffer_Alloc ; (R16, R17, X)
brcc appRouterSendAddrMsgToDev1_end
push r16
adiw xh:xl, 1
bigcall NETMSG_Address_Write ; (R16, R17, R18, R19, R20, R21)
sbiw xh:xl, 1
pop r16
rcall appRouterSendMsgToDev1 ; (R16, R17, R18, R24, R25, X, Y)
appRouterSendAddrMsgToDev1_end:
ret
; @end
; ---------------------------------------------------------------------------
; @routine appRouterSendRangeMsgToDev1
;
; @param R18 msg code
; @return CFLAG set if message enqueued, cleared on error
; @clobbers (R16, R17, R18, R19, R20, R21, R24, R25, X, Y)
appRouterSendRangeMsgToDev1:
bigcall NET_Buffer_Alloc ; (R16, R17, X)
brcc appRouterSendRangeMsgToDev1_end
push r16
lds r20, appRouterRangeBegin
lds r21, appRouterRangeEnd
adiw xh:xl, 1
bigcall NETMSG_Range_Write ; (R16, R17, R18, R19, R20, R21)
sbiw xh:xl, 1
pop r16
rcall appRouterSendMsgToDev1 ; (R16, R17, R18, R24, R25, X, Y)
appRouterSendRangeMsgToDev1_end:
ret
; @end
; ---------------------------------------------------------------------------
; @routine appRouterSendMsgToDev1
;
; @param R16 num of allocated buffer
; @param X msg to send (points to start of allocated buffer, e.g. buffer header)
; @return CFLAG set if message enqueued, cleared on error
; @clobbers Y (R16, R17, R18, R24, R25, X)
appRouterSendMsgToDev1:
ldi yl, LOW(netInterfaceData2)
ldi yh, HIGH(netInterfaceData2)
bigcall NET_Interface_AddOutgoingMsgNum ; (R17, R18, X)
brcs appRouterSendMsgToDev1_end
bigcall NET_Buffer_ReleaseByNum ; (R16, X)
ldi r16, NET_IFACE_OFFS_ERR_NOBUF_LOW
rcall NET_Interface_IncCounter16 ; (R24, R25)
clc
appRouterSendMsgToDev1_end:
ret
; @end
; ---------------------------------------------------------------------------
; @routine appRouterHandleRebootRequest
;
; Doesn't return if reboot msg is valid.
;
; @param X pointer to received message
appRouterHandleRebootRequest:
rcall NETMSG_RebootRequestRead
brcc appRouterHandleRebootRequest_end
; reboot
cli
bigjmp BOOTLOADER_ADDR
appRouterHandleRebootRequest_end:
ret
; @end
; ---------------------------------------------------------------------------
; @routine appRouterHandlePingRequest
;
; @param X pointer to received message
appRouterHandlePingRequest:
ld r17, X
lds r16, (netInterfaceData+NET_IFACE_OFFS_ADDRESS)
cp r16, r17
breq appRouterHandlePingRequest_forMe
cpi r17, 0xff
breq appRouterHandlePingRequest_forMe
clc
rjmp appRouterHandlePingRequest_end
appRouterHandlePingRequest_forMe:
adiw xh:xl, NETMSG_OFFS_SRCADDR
ld r17, X
sbiw xh:xl, NETMSG_OFFS_SRCADDR
push r17
bigcall NET_Buffer_Alloc ; (R16, R17, X)
pop r17
brcc appRouterHandlePingRequest_end ; jmp on error
push r16 ; buffer num
mov r16, r17 ; DEST addr
adiw xh:xl, 1
ldi yl, LOW(netInterfaceData)
ldi yh, HIGH(netInterfaceData)
bigcall NETMSG_Pong_Write ; (R16, R17, R18, R19, R20, X)
sbiw xh:xl, 1
pop r16 ; buffer num
bigcall NET_Interface_AddOrReleaseOutMsg ; (R16, R17, R18, X)
appRouterHandlePingRequest_end:
ret
; ---------------------------------------------------------------------------
; @routine appRouterCheckRecvdMsg
;
; Read messages from either interface and forward to the other one.
;
; @return CFLAG set if something done, cleared otherwise
appRouterCheckRecvdMsg:
rcall NET_PeekNextIncomingMsgNum ; check read queue (bufNum->r16)
brcc appRouterCheckRecvdMsg_end ; no msg, jmp
rcall NET_Buffer_Locate ; (R17)
rcall appRouterHandleRouterMsgWithHdr ; filter out router msgs
brcs appRouterCheckRecvdMsg_removeMsg ; handled by router code, don't forward
; let system handle incoming messages
adiw xh:xl, 1
rcall appRouterLetSysHandleMsg
sbiw xh:xl, 1
; forward to other interface
ld r17, X
andi r17, (1<<NET_IFACE_BUFFER_IFACENUM1_BIT) | (1<<NET_IFACE_BUFFER_IFACENUM0_BIT)
rcall appRouterReverseInterfaceNum ; (R16, R17)
rcall appRouterAddMsgToInterface
brcc appRouterCheckRecvdMsg_end ; could not add, jmp
rcall NET_GetNextIncomingMsgNum ; take off the queue
sec
ret
appRouterCheckRecvdMsg_removeMsg:
rcall NET_GetNextIncomingMsgNum ; take off the queue
rcall NET_Buffer_ReleaseByNum ; (R16, X)
sec
appRouterCheckRecvdMsg_end:
ret
; @end
; ---------------------------------------------------------------------------
; @routine appRouterHandleRouterMsgWithHdr
;
; @param X pointer to msg
; @return CFLAG set if msg handled, cleared otherwise
appRouterHandleRouterMsgWithHdr:
ld r17, X
adiw xh:xl, 1
andi r17, (1<<NET_IFACE_BUFFER_IFACENUM1_BIT) | (1<<NET_IFACE_BUFFER_IFACENUM0_BIT)
cpi r17, NETDEV1_IFACENUM
brne appRouterHandleRouterMsgWithHdr_any
#ifndef APP_ROUTER_NO_ADDR_MGR
rcall appRouterHandleDev1Msg ; handle messages from controlled subnet
brcs appRouterHandleRouterMsgWithHdr_msgHandled
#endif
appRouterHandleRouterMsgWithHdr_any:
rcall appRouterHandleMsgAnyDev ; handle any msg
brcs appRouterHandleRouterMsgWithHdr_msgHandled
sbiw xh:xl, 1 ; CFLAG cleared
rjmp appRouterHandleRouterMsgWithHdr_end
appRouterHandleRouterMsgWithHdr_msgHandled:
sbiw xh:xl, 1
sec
appRouterHandleRouterMsgWithHdr_end:
ret
; @end
; ---------------------------------------------------------------------------
; @routine appRouterLetSysHandleMsg
;
; @param X pointer to msg to handle (point behind the buffer header!)
; @clobbers any, !X
appRouterLetSysHandleMsg:
ld r16, X
cpi r16, 0xff
breq appRouterLetSysHandleMsg_forMe
lds r17, netInterfaceData+NET_IFACE_OFFS_ADDRESS
cp r16, r17
brne appRouterLetSysHandleMsg_end
appRouterLetSysHandleMsg_forMe:
push xl
push xh
rcall onMessageReceived
pop xh
pop xl
push xl
push xh
rcall mainModulesOnPacketReceived
pop xh
pop xl
push xl
push xh
rcall mainAppsOnPacketReceived
pop xh
pop xl
appRouterLetSysHandleMsg_end:
ret
; @end
; ---------------------------------------------------------------------------
; @routine appRouterReverseInterfaceNum
;
; @param r17 buffer num
; @return r17 reversed interface number
; @clobbers r16, r17
appRouterReverseInterfaceNum:
ldi r16, (1<<NET_IFACE_BUFFER_IFACENUM1_BIT) | (1<<NET_IFACE_BUFFER_IFACENUM0_BIT)
eor r17, r16
and r17, r16
ret
; @end
; ---------------------------------------------------------------------------
; @routine appRouterAddMsgToInterface
; @param r16 buffer num
; @param r17 interface num
appRouterAddMsgToInterface:
cpi r17, NETDEV0_IFACENUM
brne appRouterAddMsgToInterface_notNetDev0
ldi yl, LOW(netInterfaceData)
ldi yh, HIGH(netInterfaceData)
rjmp NET_Interface_AddOutgoingMsgNum ; try to add msg to interface
appRouterAddMsgToInterface_notNetDev0:
ldi yl, LOW(netInterfaceData2)
ldi yh, HIGH(netInterfaceData2)
rjmp NET_Interface_AddOutgoingMsgNum ; try to add msg to interface
appRouterAddMsgToInterface_end:
clc
ret
; @end
; ---------------------------------------------------------------------------
; @routine appRouterWriteConfToEeprom
;
; @clobbers R16, X (R17)
appRouterWriteConfToEeprom:
; write range begin
ldi xl, LOW(EEPROM_OFFS_ROUTER_RANGE_BEGIN)
ldi xh, HIGH(EEPROM_OFFS_ROUTER_RANGE_BEGIN)
lds r16, appRouterRangeBegin
rcall Eeprom_WriteByteIfChanged ; (R17)
brcc appRouterWriteConfToEeprom_end
; write range end
ldi xl, LOW(EEPROM_OFFS_ROUTER_RANGE_END)
ldi xh, HIGH(EEPROM_OFFS_ROUTER_RANGE_END)
lds r16, appRouterRangeEnd
rcall Eeprom_WriteByteIfChanged ; (R17)
appRouterWriteConfToEeprom_end:
ret
; @end
; ---------------------------------------------------------------------------
; @routine appRouterReadConfFromEeprom
;
; @clobbers R16, X (R17)
appRouterReadConfFromEeprom:
; read range begin
ldi xl, LOW(EEPROM_OFFS_ROUTER_RANGE_BEGIN)
ldi xh, HIGH(EEPROM_OFFS_ROUTER_RANGE_BEGIN)
rcall Eeprom_ReadByte
brcc appRouterReadConfFromEeprom_end
cpi r16, 0xff
breq appRouterReadConfFromEeprom_okay ; not set, jmp
cpi r16, 2 ; range should at least start at 2 to assign 1 to router
brcs appRouterReadConfFromEeprom_okay
sts appRouterRangeBegin, r16
dec r16
sts netInterfaceData+NET_IFACE_OFFS_ADDRESS, r16 ; use addr rangeBegin-1 for router itself
sts netInterfaceData2+NET_IFACE_OFFS_ADDRESS, r16 ; use same address for both interfaces to save on addresses
; read range end
ldi xl, LOW(EEPROM_OFFS_ROUTER_RANGE_END)
ldi xh, HIGH(EEPROM_OFFS_ROUTER_RANGE_END)
rcall Eeprom_ReadByte
brcc appRouterReadConfFromEeprom_end
cpi r16, 0xff
breq appRouterReadConfFromEeprom_okay
sts appRouterRangeEnd, r16
appRouterReadConfFromEeprom_okay:
sec
appRouterReadConfFromEeprom_end:
ret
; @end

View File

@@ -11,7 +11,7 @@
; ***************************************************************************
; defines
.equ APP_STATS_INTERVAL_MINS = 11
.equ APP_STATS_INTERVAL_MINS = 30
@@ -53,6 +53,15 @@ AppStats_Fini:
;
AppStats_OnEveryMinute:
push r15
in r15, SREG
cli
rcall AppStats_OnEveryMinute_noIrq
out SREG, r15
pop r15
ret
AppStats_OnEveryMinute_noIrq:
lds r16, appStatsTimer
inc r16
cpi r16, APP_STATS_INTERVAL_MINS
@@ -60,97 +69,68 @@ AppStats_OnEveryMinute:
clr r16
AppStats_OnEveryMinute_store:
sts appStatsTimer, r16
mov r17, r16
andi r17, 3
brne AppStats_OnEveryMinute_sendStats
; send device announcement
push yl
push yh
ldi yl, LOW(netInterfaceData)
ldi yh, HIGH(netInterfaceData)
rcall AppNetwork_SendDevice
#ifdef APP_STATS_NETDEV2
ldi yl, LOW(netInterfaceData2)
ldi yh, HIGH(netInterfaceData2)
rcall AppNetwork_SendDevice
#endif
pop yh
pop yl
AppStats_OnEveryMinute_sendStats:
cpi r16, 2
breq AppStats_OnEveryMinute_sendMemStats
cpi r16, 3
breq AppStats_OnEveryMinute_sendRecvStats1
cpi r16, 5
breq AppStats_OnEveryMinute_sendSendStats1
#ifdef APP_STATS_NETDEV2
cpi r16, 6
breq AppStats_OnEveryMinute_sendRecvStats2
cpi r16, 7
breq AppStats_OnEveryMinute_sendSendStats2
#endif
; add more here
ret
AppStats_OnEveryMinute_sendMemStats:
rjmp AppNetwork_SendMemStats
AppStats_OnEveryMinute_sendRecvStats1:
ldi yl, LOW(netInterfaceData)
ldi yh, HIGH(netInterfaceData)
cpi r16, 1
breq AppStats_OnEveryMinute_sendDevice
cpi r16, 2
breq AppStats_OnEveryMinute_sendPacketsIn
cpi r16, 3
breq AppStats_OnEveryMinute_sendPacketsOut
cpi r16, 4
breq AppStats_OnEveryMinute_sendContentErrs
cpi r16, 5
breq AppStats_OnEveryMinute_sendIoErrs
cpi r16, 6
breq AppStats_OnEveryMinute_sendNoBufErrs
cpi r16, 7
breq AppStats_OnEveryMinute_sendCollisionErrs
cpi r16, 8
breq AppStats_OnEveryMinute_sendBusyErrs
#ifdef MODULES_HEAP
cpi r16, 9
breq AppStats_OnEveryMinute_sendHeapUsed
cpi r16, 10
breq AppStats_OnEveryMinute_sendHeapfree
#endif
rjmp AppNetwork_SendRxdStats
AppStats_OnEveryMinute_sendDevice:
rjmp AppNetwork_SendDevice
AppStats_OnEveryMinute_sendSendStats1:
ldi yl, LOW(netInterfaceData)
ldi yh, HIGH(netInterfaceData)
rjmp AppNetwork_SendTxdStats
AppStats_OnEveryMinute_sendPacketsIn:
ldi r17, AQHOME_VALUEID_STATS_PACKETS_IN
ldd r18, Y+NET_IFACE_OFFS_PACKETSIN_LOW
ldd r19, Y+NET_IFACE_OFFS_PACKETSIN_HIGH
rjmp appStatsSend16BitValue
AppStats_OnEveryMinute_sendPacketsOut:
ldi r17, AQHOME_VALUEID_STATS_PACKETS_OUT
ldd r18, Y+NET_IFACE_OFFS_PACKETSOUT_LOW
ldd r19, Y+NET_IFACE_OFFS_PACKETSOUT_HIGH
rjmp appStatsSend16BitValue
AppStats_OnEveryMinute_sendContentErrs:
ldi r17, AQHOME_VALUEID_STATS_ERRS_CONTENT
ldd r18, Y+NET_IFACE_OFFS_ERR_CONTENT_LOW
ldd r19, Y+NET_IFACE_OFFS_ERR_CONTENT_HIGH
rjmp appStatsSend16BitValue
AppStats_OnEveryMinute_sendIoErrs:
ldi r17, AQHOME_VALUEID_STATS_ERRS_IO
ldd r18, Y+NET_IFACE_OFFS_ERR_IO_LOW
ldd r19, Y+NET_IFACE_OFFS_ERR_IO_HIGH
rjmp appStatsSend16BitValue
AppStats_OnEveryMinute_sendNoBufErrs:
ldi r17, AQHOME_VALUEID_STATS_ERRS_NOBUF
ldd r18, Y+NET_IFACE_OFFS_ERR_NOBUF_LOW
ldd r19, Y+NET_IFACE_OFFS_ERR_NOBUF_HIGH
rjmp appStatsSend16BitValue
AppStats_OnEveryMinute_sendCollisionErrs:
ldi r17, AQHOME_VALUEID_STATS_ERRS_COLLISIONS
ldd r18, Y+NET_IFACE_OFFS_ERR_COLLISIONS_LOW
ldd r19, Y+NET_IFACE_OFFS_ERR_COLLISIONS_HIGH
rjmp appStatsSend16BitValue
AppStats_OnEveryMinute_sendBusyErrs:
ldi r17, AQHOME_VALUEID_STATS_ERRS_BUSY
ldd r18, Y+NET_IFACE_OFFS_ERR_BUSY_LOW
ldd r19, Y+NET_IFACE_OFFS_ERR_BUSY_HIGH
rjmp appStatsSend16BitValue
#ifdef MODULES_HEAP
AppStats_OnEveryMinute_sendHeapUsed:
ldi r17, AQHOME_VALUEID_STATS_HEAP_USED
lds r18, heapUsed
lds r19, heapUsed+1
rjmp appStatsSend16BitValue
AppStats_OnEveryMinute_sendHeapfree:
ldi r17, AQHOME_VALUEID_STATS_HEAP_FREE
lds r18, heapFree
lds r19, heapFree+1
rjmp appStatsSend16BitValue
#ifdef APP_STATS_NETDEV2
AppStats_OnEveryMinute_sendRecvStats2:
ldi yl, LOW(netInterfaceData2)
ldi yh, HIGH(netInterfaceData2)
rjmp AppNetwork_SendRxdStats
AppStats_OnEveryMinute_sendSendStats2:
ldi yl, LOW(netInterfaceData2)
ldi yh, HIGH(netInterfaceData2)
rjmp AppNetwork_SendTxdStats
#endif
; @end
; ---------------------------------------------------------------------------
; @routine AppStats_OnEveryMinute @global
;
; @param R17 value id
; @param R19:R18 value
appStatsSend16BitValue:
ldi r20, 1
clr r21
ldi r22, AQHOME_VALUETYPE_STATS
rjmp Main_SendValueReport
; @end

View File

@@ -27,7 +27,11 @@
wait_50us.asm
watchdog.asm
list.asm
list_t.asm
tree.asm
tree_t.asm
eeprom-r.asm
eeprom-w.asm
</extradist>
</gwbuild>

30
avr/common/calls.asm Normal file
View File

@@ -0,0 +1,30 @@
; ***************************************************************************
; copyright : (C) 2025 by Martin Preuss
; email : martin@libchipcard.de
;
; ***************************************************************************
; * This file is part of the project "AqHome". *
; * Please see toplevel file COPYING of that project for license details. *
; ***************************************************************************
.macro bigcall
.if FLASHEND >= 0x1000
call @0
.else
rcall @0
.endif
.endmacro
.macro bigjmp
.if FLASHEND >= 0x1000
jmp @0
.else
rjmp @0
.endif
.endmacro

View File

@@ -38,6 +38,8 @@ DEBUG2:
; @clobbers (R16, R18, R22, R24, R25)
blinkLed:
sbi LED_SIMPLE_DDR, LED_SIMPLE_PINNUM ; out
blinkLed_loop:
cbi LED_SIMPLE_PORT, LED_SIMPLE_PINNUM ; on
mov r22, r20
rcall waitForMultiple100ms ; (R252
@@ -45,7 +47,7 @@ blinkLed:
mov r22, r21
rcall waitForMultiple100ms ; (R22)
dec r19
brne blinkLed
brne blinkLed_loop
ret
@@ -74,7 +76,7 @@ waitFor10ms:
ldi r22, 100
waitFor10ms_loop:
push r22
rcall Utils_WaitFor100MicroSecs
bigcall Utils_WaitFor100MicroSecs
pop r22
dec r22
brne waitFor10ms_loop

80
avr/common/eeprom-r.asm Normal file
View File

@@ -0,0 +1,80 @@
; ***************************************************************************
; copyright : (C) 2025 by Martin Preuss
; email : martin@libchipcard.de
;
; ***************************************************************************
; * This file is part of the project "AqHome". *
; * Please see toplevel file COPYING of that project for license details. *
; ***************************************************************************
#ifndef AQH_AVR_COMMON_EEPROM_R_H
#define AQH_AVR_COMMON_EEPROM_R_H
; ---------------------------------------------------------------------------
; @routine Eeprom_ReadByte
;
; Read a byte from EEPROM (see example in ATtiny24/44/84 manual p.19).
;
; @param X EEPROM Address to read from
; @return CFLAG set if address okay, cleared if out of range
; @return R16 byte read
; @return X EEPROM Address incremented
; @clobbers none
Eeprom_ReadByte:
rcall Eeprom_CheckAddr
brcs Eeprom_ReadByte_addrOk
ret
Eeprom_ReadByte_addrOk:
; call routine with IRQs disabled
push r15
inr r15, SREG
cli
rcall Eeprom_ReadByte_noirq
outr SREG, r15
pop r15
sec
ret
Eeprom_ReadByte_noirq:
; wait until EEPROM ready
Eeprom_ReadByte_waitLoop:
.ifdef EEPE
sbic EECR, EEPE ; wait for previous write to complete (if any)
.else
sbic EECR, EEWE ; wait for previous write to complete (if any)
.endif
rjmp Eeprom_ReadByte_waitLoop
; read from EEPROM
out EEARH, xh ; set EEPROM address
out EEARL, xl
sbi EECR, EERE ; start EEPROM read by writing EERE
in r16, EEDR ; read data from data register
ret
; @end
; ---------------------------------------------------------------------------
; @routine Eeprom_CheckAddr
;
;
; @param X EEPROM Address to validate
; @return CFLAG set if address okay, cleared if out of range
; @clobbers r16
Eeprom_CheckAddr:
mov r16, xl
subi r16, LOW(EEPROMEND)
mov r16, xh
sbci r16, HIGH(EEPROMEND)
ret
; @end
#endif ; AQH_AVR_COMMON_EEPROM_R_H

100
avr/common/eeprom-w.asm Normal file
View File

@@ -0,0 +1,100 @@
; ***************************************************************************
; copyright : (C) 2025 by Martin Preuss
; email : martin@libchipcard.de
;
; ***************************************************************************
; * This file is part of the project "AqHome". *
; * Please see toplevel file COPYING of that project for license details. *
; ***************************************************************************
#ifndef AQH_AVR_COMMON_EEPROM_W_H
#define AQH_AVR_COMMON_EEPROM_W_H
; ---------------------------------------------------------------------------
; Utils_WriteEepromIncr
;
; Write a byte to EEPROM (see example in ATtiny24/44/84 manual p.18).
;
; @param R16 byte to write
; @param X EEPROM Address to write to
; @return CFLAG set if data written, cleared on error
; @clobbers R17
Eeprom_WriteByte:
mov r17, r16
rcall Eeprom_CheckAddr ; (r16)
mov r16, r17
brcs Eeprom_WriteByte_addrOk
ret
Eeprom_WriteByte_addrOk:
; call routine with IRQs disabled
push r15
inr r15, SREG
cli
rcall Eeprom_WriteByte_noirq
outr SREG, r15
pop r15
sec
ret
Eeprom_WriteByte_noirq:
; wait for EEPROM to be ready
Eeprom_WriteByte_waitLoop:
.ifdef EEPE
sbic EECR, EEPE ; wait for previous write to complete (if any)
.else
sbic EECR, EEWE ; wait for previous write to complete (if any)
.endif
rjmp Eeprom_WriteByte_waitLoop
; write data
.ifdef EEPM1
ldi r17, (0<<EEPM1) | (0<<EEPM0) ; set programming mode
.endif
out EECR, r17
out EEARH, xh ; set EEPROM address
out EEARL, xl
out EEDR, r16 ; write data to data register
.ifdef EEMPE
sbi EECR, EEMPE ; write logical one to EEMPE
.else
sbi EECR, EEMWE ; write logical one to EEMWE
.endif
.ifdef EEPE
sbi EECR, EEPE ; start EEPROM write by setting EEPE
.else
sbi EECR, EEWE ; start EEPROM write by setting EEWE
.endif
ret
; @end
; ---------------------------------------------------------------------------
; @routine Eeprom_WriteByteIfChanged @global
;
; Only write EEPROM byte if changed from current value (save on write cycles)
;
; @param r16 byte to write
; @param X eeprom address to write to
; @clobbers r17
Eeprom_WriteByteIfChanged:
mov r17, r16
rcall Eeprom_ReadByte
brcc Eeprom_WriteByteIfChanged_end
cp r16, r17
sec
breq Eeprom_WriteByteIfChanged_end
mov r16, r17
rcall Eeprom_WriteByte ; (R17)
Eeprom_WriteByteIfChanged_end:
ret
; @end
#endif ; AQH_AVR_COMMON_EEPROM_W_H

135
avr/common/eeprom_tlv.asm Normal file
View File

@@ -0,0 +1,135 @@
; ***************************************************************************
; copyright : (C) 2025 by Martin Preuss
; email : martin@libchipcard.de
;
; ***************************************************************************
; * This file is part of the project "AqHome". *
; * Please see toplevel file COPYING of that project for license details. *
; ***************************************************************************
#ifndef AQH_AVR_COMMON_EEPROM_TLV_H
#define AQH_AVR_COMMON_EEPROM_TLV_H
; ---------------------------------------------------------------------------
; @routine EepromTlv_AddTlv @global
;
; @param r16 type (must not be 0xff!)
; @param r17 length
; @return CFLAG set if free bytes found, cleared on error
; @return X EEPROM address of data for TLV (behind TLV header)
; @clobbers R16, R20, R21, X (R18)
EepromTlv_AddTlv:
mov r20, r16
mov r21, r17
ldi r16, 0xff
rcall EepromTlv_FindFirst ; (R18)
brcc EepromTlv_AddTlv_end
cpi r17, 0xff
brne EepromTlv_AddTlv_clcEnd
add xl, r21 ; wanted size
adc xh, r21
sub xh, r21
rcall Eeprom_CheckAddr ; check end address
brcc EepromTlv_AddTlv_end
sub xl, r21
sbc xh, r21
add xh, r21
sbiw xh:xl, 2
mov r16, r20 ; type
rcall Eeprom_WriteByte
adiw xh:xl, 1
mov r16, r21 ; length
rcall Eeprom_WriteByte
adiw xh:xl, 1
sec
rjmp EepromTlv_AddTlv_end
EepromTlv_AddTlv_clcEnd:
clc
EepromTlv_AddTlv_end:
ret
; @end
; ---------------------------------------------------------------------------
; @routine EepromTlv_FindFirst @global
;
; Find first matching TLV
;
; @param r16 TLV type to find
; @return CFLAG set if TLV found, cleared otherwise
; @return r16 type
; @return r17 length
; @return X points to begin of TLV data
; @clobbers r18
EepromTlv_FindFirst:
ldi xl, LOW(EEPROM_OFFS_TLV)
ldi xh, HIGH(EEPROM_OFFS_TLV)
rjmp EepromTlv_Find
; @end
; ---------------------------------------------------------------------------
; @routine EepromTlv_Find @global
;
; @param r16 TLV type to find
; @param X EEPROM address to start search
; @return CFLAG set if TLV found, cleared otherwise
; @return r16 type
; @return r17 length
; @return X points to begin of TLV data
; @clobbers r18
EepromTlv_Find:
mov r18, r16
EepromTlv_Find_loop:
rcall EepromTlv_ReadHeader
brcc EepromTlv_Find_notFound
cpi r16, r18 ; the one we wanted?
breq EepromTlv_Find_found
add xl, r17 ; skip TLV data
adc xh, r17
sub xh, r17
rjmp EepromTlv_Find_loop
EepromTlv_Find_notFound:
clc
rjmp EepromTlv_Find_end
EepromTlv_Find_found:
sec
EepromTlv_Find_end:
ret
; @end
; ---------------------------------------------------------------------------
; @routine EepromTlv_ReadHeader
;
; @param X EEPROM address
; @return CFLAG set if address okay, cleared if out of range
; @return r16 type
; @return r17 length (only data length, can be 0)
; @return X EEPROM address behind TLV header
; @clobbers none
EepromTlv_ReadHeader:
rcall Eeprom_ReadByteIncr ; read type
brcc EepromTlv_ReadHeader
adiw xh:xl, 1
push r16
rcall Eeprom_ReadByteIncr ; read length
mov r17, r16
pop r16
brcc EepromTlv_ReadHeader
adiw xh:xl, 1
sec
EepromTlv_ReadHeader:
ret
; @end

View File

@@ -54,11 +54,26 @@ List_InitObject:
; ---------------------------------------------------------------------------
; @routine List_FiniObject @global
;
; @param Y pointer to object
; @clobbers r16
List_FiniObject:
clr r16 ; set this->NEXT to NULL
std Y+LIST_OFFS_NEXT_LO, r16
std Y+LIST_OFFS_NEXT_HI, r16
ret
; @end
; ---------------------------------------------------------------------------
; @routine List_GetNextObject @global
; @param Y pointer to object
; @return X pointer to parent object
; @return X pointer to successor object
; @clobbers none
List_GetNextObject:
@@ -101,13 +116,13 @@ List_GetPredecessorFor:
cp r16, yl
brne List_GetPredecessorFor_next
cp r17, yh
breq List_GetLastObject_haveIt
breq List_GetPredecessorFor_haveIt
List_GetPredecessorFor_next:
mov xl, r16
mov xh, r17
rjmp List_GetPredecessorFor
List_GetPredecessorFor_haveIt:
sbiw xh:xl, 1
sbiw xh:xl, 2
List_GetPredecessorFor_ret:
ret
; @end
@@ -127,8 +142,14 @@ List_AddObject:
rcall List_GetLastObject ; (r16, r17, X, Y)
pop yh
pop yl
st X+, yl ; WID_OFFS_WNEXT_LO
st X+, yh ; WID_OFFS_WNEXT_HI
mov r16, xl
or r16, xh
clc
breq List_AddObject_ret
st X+, yl ; LIST_OFFS_NEXT_LO
st X+, yh ; LIST_OFFS_NEXT_HI
sec
List_AddObject_ret:
ret
; @end
@@ -163,4 +184,68 @@ List_UnlinkObject_ret:
; ---------------------------------------------------------------------------
; @routine List_UnlinkAllObjects
; @param Y pointer to first object in a list
; @clobbers r16, r17, r18, Y
List_UnlinkAllObjects:
clr r18
List_UnlinkAllObjects_loop:
ldd r16, Y+LIST_OFFS_NEXT_LO
ldd r17, Y+LIST_OFFS_NEXT_HI
std Y+LIST_OFFS_NEXT_LO, r18
std Y+LIST_OFFS_NEXT_HI, r18
mov yl, r16
mov yh, r17
or r16, r17
brne List_UnlinkAllObjects_loop
ret
; @end
; ---------------------------------------------------------------------------
; @routine List_ForEveryObject
;
; Calls the given function for every object until it
; returns with a set carry flag or until the full list
; is handled.
; Registers that can be used by the given function are all
; except R16, R18 and R19 (those are used by this routine).
;
; @param Y pointer to first object in a list
; @param Z routine to call for every object
; @clobbers r16, r17, r18, r19, X, Y (r24, r25)
List_ForEveryObject:
List_ForEveryObject_loop:
ldd r18, Y+LIST_OFFS_NEXT_LO ; next
ldd r19, Y+LIST_OFFS_NEXT_HI ; next
clr r16
std Y+LIST_OFFS_NEXT_LO, r16
std Y+LIST_OFFS_NEXT_HI, r16
push r18 ; next
push r19 ; next
rcall List_ForEveryObject_callZ
pop r19 ; next
pop r18 ; next
brcs List_ForEveryObject_ret
mov yl, r18
mov yh, r19
or r18, r19
brne List_ForEveryObject_loop
List_ForEveryObject_ret:
ret
List_ForEveryObject_callZ:
ijmp
; @end
#endif ; AQH_AVR_COMMON_LIST_H

240
avr/common/list_t.asm Normal file
View File

@@ -0,0 +1,240 @@
; ***************************************************************************
; copyright : (C) 2025 by Martin Preuss
; email : martin@libchipcard.de
;
; ***************************************************************************
; * This file is part of the project "AqHome". *
; * Please see toplevel file COPYING of that project for license details. *
; ***************************************************************************
#ifndef AQH_AVR_COMMON_LIST_T_H
#define AQH_AVR_COMMON_LIST_T_H
.equ LIST_TEST_OBJECT_OFFS_LIST = 0
.equ LIST_TEST_OBJECT_OFFS_VALUE1 = LIST_SIZE
.equ LIST_TEST_OBJECT_OFFS_VALUE2 = LIST_SIZE+1
.equ LIST_TEST_OBJECT_SIZE = LIST_SIZE+2
.dseg
listTest_list: .byte 2
listTest_object1: .byte LIST_TEST_OBJECT_SIZE
listTest_object2: .byte LIST_TEST_OBJECT_SIZE
listTest_object3: .byte LIST_TEST_OBJECT_SIZE
.cseg
; ---------------------------------------------------------------------------
; @routine listTest_Object_Init
; @param Y pointer to object to init
; @param r18 value 1
; @param r19 value 2
listTest_Object_Init:
bigcall List_InitObject ; (R16)
std Y+LIST_TEST_OBJECT_OFFS_VALUE1, r18
std Y+LIST_TEST_OBJECT_OFFS_VALUE2, r19
ret
; @end
listTest1:
ldi yl, LOW(listTest_object1)
ldi yh, HIGH(listTest_object1)
ldi r18, 1
ldi r19, 2
rcall listTest_Object_Init
sts listTest_list, yl
sts listTest_list+1, yh
mov xl, yl ; X=listTest_object1
mov xh, yh
ldi yl, LOW(listTest_object2)
ldi yh, HIGH(listTest_object2)
ldi r18, 3
ldi r19, 4
rcall listTest_Object_Init
; X=object 1, Y=object 2
bigcall List_AddObject ; (r16, r17, x)
lds yl, listTest_list
lds yh, listTest_list+1
ldd r16, Y+LIST_TEST_OBJECT_OFFS_VALUE1
cpi r16, 1
ldi r16, 1
brne listTest1_error
ldd r16, Y+LIST_TEST_OBJECT_OFFS_VALUE2
cpi r16, 2
ldi r16, 2
brne listTest1_error
bigcall List_GetNextObject
ldi r16, 3
cpi xl, LOW(listTest_object2)
brne listTest1_error
cpi xh, HIGH(listTest_object2)
brne listTest1_error
mov yl, xl
mov yh, xh
ldd r16, Y+LIST_TEST_OBJECT_OFFS_VALUE1
cpi r16, 3
ldi r16, 4
brne listTest1_error
ldd r16, Y+LIST_TEST_OBJECT_OFFS_VALUE2
cpi r16, 4
ldi r16, 5
brne listTest1_error
bigcall List_GetNextObject
mov r16, xh
or r16, xl
ldi r16, 6
brne listTest1_error
sec
ret
listTest1_error:
clc
ret
; @end
listTest2:
lds xl, listTest_list
lds xh, listTest_list+1
ldi yl, LOW(listTest_object3)
ldi yh, HIGH(listTest_object3)
ldi r18, 5
ldi r19, 6
rcall listTest_Object_Init
; X=object 1, Y=object 2
bigcall List_AddObject ; (r16, r17, x)
mov xl, yl
mov xh, yh
bigcall List_GetLastObject
ldi r16, 1
cpi xl, LOW(listTest_object3)
brne listTest2_error
cpi xh, HIGH(listTest_object3)
brne listTest2_error
mov yl, xl
mov yh, xh
ldd r16, Y+LIST_TEST_OBJECT_OFFS_VALUE1
cpi r16, 5
ldi r16, 2
brne listTest2_error
ldd r16, Y+LIST_TEST_OBJECT_OFFS_VALUE2
cpi r16, 6
ldi r16, 3
brne listTest2_error
sec
ret
listTest2_error:
clc
ret
; @end
listTest3:
lds xl, listTest_list
lds xh, listTest_list+1
ldi yl, LOW(listTest_object2)
ldi yh, HIGH(listTest_object2)
bigcall List_UnlinkObject
lds yl, listTest_list
lds yh, listTest_list+1
ldd r16, Y+LIST_TEST_OBJECT_OFFS_VALUE1
cpi r16, 1
ldi r16, 1
brne listTest3_error
ldd r16, Y+LIST_TEST_OBJECT_OFFS_VALUE2
cpi r16, 2
ldi r16, 2
brne listTest3_error
bigcall List_GetNextObject
ldi r16, 3
cpi xl, LOW(listTest_object3)
brne listTest3_error
cpi xh, HIGH(listTest_object3)
brne listTest3_error
mov yl, xl
mov yh, xh
ldd r16, Y+LIST_TEST_OBJECT_OFFS_VALUE1
cpi r16, 5
ldi r16, 4
brne listTest3_error
ldd r16, Y+LIST_TEST_OBJECT_OFFS_VALUE2
cpi r16, 6
ldi r16, 5
brne listTest3_error
bigcall List_GetNextObject
mov r16, xh
or r16, xl
ldi r16, 6
brne listTest3_error
sec
ret
listTest3_error:
clc
ret
; @end
listTest4:
lds xl, listTest_list
lds xh, listTest_list+1
ldi yl, LOW(listTest_object3)
ldi yh, HIGH(listTest_object3)
bigcall List_UnlinkObject
lds yl, listTest_list
lds yh, listTest_list+1
ldd r16, Y+LIST_TEST_OBJECT_OFFS_VALUE1
cpi r16, 1
ldi r16, 1
brne listTest4_error
ldd r16, Y+LIST_TEST_OBJECT_OFFS_VALUE2
cpi r16, 2
ldi r16, 2
brne listTest4_error
bigcall List_GetNextObject
mov r16, xh
or r16, xl
ldi r16, 6
brne listTest4_error
sec
ret
listTest4_error:
clc
ret
; @end
#endif

View File

@@ -52,7 +52,7 @@ l_loop:
brcs l_loop
rjmp l_end ; use cleared CFLAG
l_foundfree:
ldi r16, 0x80 ; mark as used
ldi r16, 0x10 ; mark as used
st X, r16
mov r16, r17
sec ; set CFLAG, return bufnum in r16, pointre in X

View File

@@ -120,7 +120,7 @@ RingBufferY_Reset:
;
; @return CFLAG on success, cleared on error
; @return r16 byte read
; @param Y pointer to start of interface data
; @param Y base address of ringbuffer struct
; @clobbers R17, R18, X
RingBufferY_ReadByteGuarded:

View File

@@ -17,10 +17,10 @@
; defs
.equ TREE_OFFS_LIST = 0
.equ TREE_OFFS_WPARENT_LO = TREE_OFFS_LIST+LIST_SIZE
.equ TREE_OFFS_WPARENT_HI = TREE_OFFS_LIST+LIST_SIZE+1
.equ TREE_OFFS_WCHILD_LO = TREE_OFFS_LIST+LIST_SIZE+2
.equ TREE_OFFS_WCHILD_HI = TREE_OFFS_LIST+LIST_SIZE+3
.equ TREE_OFFS_PARENT_LO = TREE_OFFS_LIST+LIST_SIZE
.equ TREE_OFFS_PARENT_HI = TREE_OFFS_LIST+LIST_SIZE+1
.equ TREE_OFFS_CHILD_LO = TREE_OFFS_LIST+LIST_SIZE+2
.equ TREE_OFFS_CHILD_HI = TREE_OFFS_LIST+LIST_SIZE+3
.equ TREE_SIZE = TREE_OFFS_LIST+LIST_SIZE+4
@@ -42,10 +42,10 @@
Tree_InitObject:
rcall List_InitObject ; (R16)
clr r16 ; clear this->TREE data
std Y+TREE_OFFS_LIST+TREE_OFFS_WPARENT_LO, r16
std Y+TREE_OFFS_LIST+TREE_OFFS_WPARENT_HI, r16
std Y+TREE_OFFS_LIST+TREE_OFFS_WCHILD_LO, r16
std Y+TREE_OFFS_LIST+TREE_OFFS_WCHILD_HI, r16
std Y+TREE_OFFS_LIST+TREE_OFFS_PARENT_LO, r16
std Y+TREE_OFFS_LIST+TREE_OFFS_PARENT_HI, r16
std Y+TREE_OFFS_LIST+TREE_OFFS_CHILD_LO, r16
std Y+TREE_OFFS_LIST+TREE_OFFS_CHILD_HI, r16
ret
; @end
@@ -59,8 +59,8 @@ Tree_InitObject:
; @clobbers none
Tree_GetParentObject:
ldd xl, Y+TREE_OFFS_WPARENT_LO
ldd xh, Y+TREE_OFFS_WPARENT_HI
ldd xl, Y+TREE_OFFS_PARENT_LO
ldd xh, Y+TREE_OFFS_PARENT_HI
ret
; @end
@@ -74,13 +74,46 @@ Tree_GetParentObject:
; @clobbers none
Tree_GetFirstChildObject:
ldd xl, Y+TREE_OFFS_WCHILD_LO
ldd xh, Y+TREE_OFFS_WCHILD_HI
ldd xl, Y+TREE_OFFS_CHILD_LO
ldd xh, Y+TREE_OFFS_CHILD_HI
ret
; @end
; ---------------------------------------------------------------------------
; @routine Tree_GetLastChildObject @global
; @param Y pointer to object
; @return X pointer to last child object
; @clobbers none
Tree_GetLastChildObject:
ldd xl, Y+TREE_OFFS_CHILD_LO
ldd xh, Y+TREE_OFFS_CHILD_HI
mov r16, xl
or r16, xh
breq Tree_GetLastChildObject_ret
rcall List_GetLastObject
Tree_GetLastChildObject_ret:
ret
; @end
; ---------------------------------------------------------------------------
; @routine Tree_GetNextSibling @global
; @param Y pointer to object
; @return X pointer to successor object
; @clobbers none
Tree_GetNextSibling:
rjmp List_GetNextObject
; @end
; ---------------------------------------------------------------------------
; @routine Tree_GetObjectBelow @global
@@ -101,7 +134,7 @@ treeGetObjectBelow:
mov r16, xl
or r16, xh
brne treeGetObjectBelow_ret ; got one
rcall List_GetNextObject
rcall Tree_GetNextSibling
mov r16, xl
or r16, xh
brne treeGetObjectBelow_ret ; got one
@@ -122,27 +155,27 @@ treeGetObjectBelow_ret:
; ---------------------------------------------------------------------------
; @routine Tree_AddChildObject @global
; @param X pointer to parent to add to
; @param Y pointer to object to add
; @param X pointer to parent to add to
; @clobbers r16, r17, r18, x
Tree_AddChildObject:
std Y+TREE_OFFS_WPARENT_LO, xl ; immediately store parent pointer
std Y+TREE_OFFS_WPARENT_HI, xh
adiw xh:xl, TREE_OFFS_WCHILD_LO
std Y+TREE_OFFS_PARENT_LO, xl ; immediately store parent pointer
std Y+TREE_OFFS_PARENT_HI, xh
adiw xh:xl, TREE_OFFS_CHILD_LO ; read pointer to first child
ld r16, X+
ld r17, X
mov r18, r16
or r18, r17
brne Tree_AddChildObject_addToChildList
st X, yh ; no child, set THIS as first
st X, yh ; no child, set THIS as first
st -X, yl
sbiw xh:xl, WID_OFFS_TREE+TREE_OFFS_WCHILD_LO
sbiw xh:xl, TREE_OFFS_CHILD_LO
ret
Tree_AddChildObject_addToChildList:
mov xl, r16
mov xl, r16 ; X=first child
mov xh, r17
rjmp List_AddObject
rjmp List_AddObject ; add Y as new object
; @end
@@ -154,17 +187,17 @@ Tree_AddChildObject_addToChildList:
; @clobbers r16, r17, x
Tree_UnlinkObject:
ldd xl, Y+TREE_OFFS_WPARENT_LO
ldd xh, Y+TREE_OFFS_WPARENT_HI
ldd xl, Y+TREE_OFFS_PARENT_LO
ldd xh, Y+TREE_OFFS_PARENT_HI
mov r16, xl
or r16, xh
breq Tree_UnlinkObject_ret ; not part of a tree
adiw xh:xl, TREE_OFFS_WCHILD_LO ; get parent's first child to R17:R16
adiw xh:xl, TREE_OFFS_CHILD_LO ; get parent's first child to R17:R16
ld r16, X+
ld r17, X
cp r16, yl ; same as THIS?
brne Tree_UnlinkObject_inList ; nope, need to check childList
cp r17, yh
cp r17, yh ; same as THIS?
brne Tree_UnlinkObject_inList ; nope, need to check childList
ldd r16, Y+TREE_OFFS_LIST+LIST_OFFS_NEXT_HI ; is first child, set this->NEXT as new first child
st X, r16
@@ -177,8 +210,8 @@ Tree_UnlinkObject_inList:
rcall List_UnlinkObject ; (R16, R17, X)
Tree_UnlinkObject_clrParentAndSibling:
clr r16 ; clear this->PARENT
std Y+TREE_OFFS_LIST+TREE_OFFS_WPARENT_LO, r16
std Y+TREE_OFFS_LIST+TREE_OFFS_WPARENT_HI, r16
std Y+TREE_OFFS_PARENT_LO, r16
std Y+TREE_OFFS_PARENT_HI, r16
std Y+LIST_OFFS_NEXT_LO, r16
std Y+LIST_OFFS_NEXT_HI, r16 ; clear this->NEXT
Tree_UnlinkObject_ret:
@@ -188,5 +221,6 @@ Tree_UnlinkObject_ret:
#endif ; AQH_AVR_COMMON_TREE_H

381
avr/common/tree_t.asm Normal file
View File

@@ -0,0 +1,381 @@
; ***************************************************************************
; copyright : (C) 2025 by Martin Preuss
; email : martin@libchipcard.de
;
; ***************************************************************************
; * This file is part of the project "AqHome". *
; * Please see toplevel file COPYING of that project for license details. *
; ***************************************************************************
.equ TREE_TEST_OBJECT_OFFS_LIST = 0
.equ TREE_TEST_OBJECT_OFFS_VALUE1 = TREE_SIZE
.equ TREE_TEST_OBJECT_OFFS_VALUE2 = TREE_SIZE+1
.equ TREE_TEST_OBJECT_SIZE = TREE_SIZE+2
.dseg
testTree_root: .byte TREE_TEST_OBJECT_SIZE
testTree_child1: .byte TREE_TEST_OBJECT_SIZE
testTree_child2: .byte TREE_TEST_OBJECT_SIZE
testTree_child3: .byte TREE_TEST_OBJECT_SIZE
.cseg
; ---------------------------------------------------------------------------
; @routine treeTest_Object_Init
; @param Y pointer to object to init
; @param r18 value 1
; @param r19 value 2
treeTest_Object_Init:
bigcall Tree_InitObject ; (R16)
std Y+TREE_TEST_OBJECT_OFFS_VALUE1, r18
std Y+TREE_TEST_OBJECT_OFFS_VALUE2, r19
ret
; @end
treeTest1:
ldi yl, LOW(testTree_root)
ldi yh, HIGH(testTree_root)
ldi r18, 1
ldi r19, 2
rcall treeTest_Object_Init
mov xl, yl
mov xh, yh
rcall treeTestCreateAndAddChild1
ldi r16, 1 ; error code
brcc treeTest1_error
rcall treeTestCreateAndAddChild2
ldi r16, 2 ; error code
brcc treeTest1_error
rcall treeTestCreateAndAddChild3
ldi r16, 3 ; error code
brcc treeTest1_error
ldi yl, LOW(testTree_root)
ldi yh, HIGH(testTree_root)
bigcall Tree_GetFirstChildObject
ldi r16, 4 ; error code
cpi xl, LOW(testTree_child1)
brne treeTest1_error
cpi xh, HIGH(testTree_child1)
brne treeTest1_error
mov yl, xl
mov yh, xh
ldd r16, Y+TREE_TEST_OBJECT_OFFS_VALUE1
cpi r16, 3
ldi r16, 5 ; error code
brne treeTest1_error
ldd r16, Y+TREE_TEST_OBJECT_OFFS_VALUE2
cpi r16, 4
ldi r16, 6 ; error code
brne treeTest1_error
rcall treeTestNextSiblingIs2
ldi r16, 7 ; error code
brcc treeTest1_error
rcall treeTestNextSiblingIs3
ldi r16, 8 ; error code
brcc treeTest1_error
rcall treeTestNextSiblingIsNull
ldi r16, 9 ; error code
brcc treeTest1_error
sec
ret
treeTest1_error:
clc
ret
; @end
treeTest2:
ldi yl, LOW(testTree_child2)
ldi yh, HIGH(testTree_child2)
bigcall Tree_UnlinkObject
rcall treeTestCheckParentNull
ldi r16, 1 ; error code
brcc treeTest2_error
bigcall Tree_GetNextSibling
mov r16, xl
or r16, xh
ldi r16, 2 ; error code
brne treeTest2_error
ldi yl, LOW(testTree_root)
ldi yh, HIGH(testTree_root)
bigcall Tree_GetFirstChildObject
ldi r16, 3 ; error code
cpi xl, LOW(testTree_child1)
brne treeTest2_error
cpi xh, HIGH(testTree_child1)
brne treeTest2_error
mov yl, xl
mov yh, xh
rcall treeTestNextSiblingIs3
ldi r16, 4 ; error code
brcc treeTest2_error
sec
ret
treeTest2_error:
clc
ret
; @end
treeTest3:
ldi yl, LOW(testTree_child1)
ldi yh, HIGH(testTree_child1)
bigcall Tree_UnlinkObject
rcall treeTestCheckParentNull
ldi r16, 1 ; error code
brcc treeTest3_error
bigcall Tree_GetNextSibling
mov r16, xl
or r16, xh
ldi r16, 2 ; error code
brne treeTest3_error
ldi yl, LOW(testTree_root)
ldi yh, HIGH(testTree_root)
bigcall Tree_GetFirstChildObject
ldi r16, 3 ; error code
cpi xl, LOW(testTree_child3)
brne treeTest3_error
cpi xh, HIGH(testTree_child3)
brne treeTest3_error
mov yl, xl
mov yh, xh
rcall treeTestNextSiblingIsNull
ldi r16, 4 ; error code
brcc treeTest3_error
sec
ret
treeTest3_error:
clc
ret
; @end
treeTest4:
ldi yl, LOW(testTree_child1)
ldi yh, HIGH(testTree_child1)
ldi xl, LOW(testTree_root)
ldi xh, HIGH(testTree_root)
bigcall Tree_AddChildObject
rcall treeTestCheckParentRoot
ldi r16, 1 ; error code
brcc treeTest4_error
ldi yl, LOW(testTree_root)
ldi yh, HIGH(testTree_root)
bigcall Tree_GetFirstChildObject
ldi r16, 2 ; error code
cpi xl, LOW(testTree_child3)
brne treeTest4_error
cpi xh, HIGH(testTree_child3)
brne treeTest4_error
mov yl, xl
mov yh, xh
rcall treeTestNextSiblingIs1
ldi r16, 3 ; error code
brcc treeTest4_error
rcall treeTestNextSiblingIsNull
ldi r16, 4 ; error code
brcc treeTest4_error
sec
ret
treeTest4_error:
clc
ret
; @end
treeTestCreateAndAddChild1:
ldi yl, LOW(testTree_child1)
ldi yh, HIGH(testTree_child1)
ldi r18, 3
ldi r19, 4
rcall treeTest_Object_Init
ldi xl, LOW(testTree_root)
ldi xh, HIGH(testTree_root)
bigcall Tree_AddChildObject
rcall treeTestCheckParentRoot
brcc treeTestCreateAndAddChild1_error
sec
ret
treeTestCreateAndAddChild1_error:
clc
ret
treeTestCreateAndAddChild2:
ldi yl, LOW(testTree_child2)
ldi yh, HIGH(testTree_child2)
ldi r18, 5
ldi r19, 6
rcall treeTest_Object_Init
ldi xl, LOW(testTree_root)
ldi xh, HIGH(testTree_root)
bigcall Tree_AddChildObject
rcall treeTestCheckParentRoot
brcc treeTestCreateAndAddChild2_error
sec
ret
treeTestCreateAndAddChild2_error:
clc
ret
treeTestCreateAndAddChild3:
ldi yl, LOW(testTree_child3)
ldi yh, HIGH(testTree_child3)
ldi r18, 7
ldi r19, 8
rcall treeTest_Object_Init
ldi xl, LOW(testTree_root)
ldi xh, HIGH(testTree_root)
bigcall Tree_AddChildObject
rcall treeTestCheckParentRoot
brcc treeTestCreateAndAddChild3_error
sec
ret
treeTestCreateAndAddChild3_error:
clc
ret
treeTestCheckParentNull:
bigcall Tree_GetParentObject
mov r16, xl
or r16, xh
brne treeTestCheckParentNull_error
sec
ret
treeTestCheckParentNull_error:
clc
ret
treeTestCheckParentRoot:
bigcall Tree_GetParentObject
cpi xl, LOW(testTree_root)
brne treeTestCheckParentRoot_error
cpi xh, HIGH(testTree_root)
brne treeTestCheckParentRoot_error
sec
ret
treeTestCheckParentRoot_error:
clc
ret
treeTestNextSiblingIs1:
bigcall Tree_GetNextSibling
cpi xl, LOW(testTree_child1)
brne treeTestNextSiblingIs1_error
cpi xh, HIGH(testTree_child1)
brne treeTestNextSiblingIs1_error
mov yl, xl
mov yh, xh
sec
ret
treeTestNextSiblingIs1_error:
clc
ret
treeTestNextSiblingIs2:
bigcall Tree_GetNextSibling
cpi xl, LOW(testTree_child2)
brne treeTestNextSiblingIs2_error
cpi xh, HIGH(testTree_child2)
brne treeTestNextSiblingIs2_error
mov yl, xl
mov yh, xh
sec
ret
treeTestNextSiblingIs2_error:
clc
ret
treeTestNextSiblingIs3:
bigcall Tree_GetNextSibling
cpi xl, LOW(testTree_child3)
brne treeTestNextSiblingIs3_error
cpi xh, HIGH(testTree_child3)
brne treeTestNextSiblingIs3_error
mov yl, xl
mov yh, xh
sec
ret
treeTestNextSiblingIs3_error:
clc
ret
treeTestNextSiblingIsNull:
bigcall Tree_GetNextSibling
mov r16, xl
or r16, xh
brne treeTestNextSiblingIsNull_error
sec
ret
treeTestNextSiblingIsNull_error:
clc
ret

View File

@@ -7,29 +7,6 @@
; * Please see toplevel file COPYING of that project for license details. *
; ***************************************************************************
#if 0
; M_IO_READ DEST, SRC
.macro M_IO_READ
.if @1 < 64
in @0, @1
.else
lds @0, @1
.endif
.endmacro
; M_IO_WRITE DEST, SRC
.macro M_IO_WRITE
.if @0 < 64
out @0, @1
.else
sts @0, @1
.endif
.endmacro
#endif
; inr DEST, SRC
.macro inr

View File

@@ -18,7 +18,8 @@
; @clobbers r22
Utils_WaitFor50MicroSecs:
Utils_WaitNanoSecs 50000, 7, r22 ; wait for 50us (minus RCALL and RET)
Utils_WaitNanoSecs 25000, 0, r22 ; wait for 25us
Utils_WaitNanoSecs 25000, 7, r22 ; wait for 25us (minus RCALL and RET)
ret
; @end

View File

@@ -7,6 +7,7 @@
; * Please see toplevel file COPYING of that project for license details. *
; ***************************************************************************
.error "Dont include this, use devices/all/defs.asm instead"
.equ AQHOME_VALUETYPE_UNKNOWN = 0

View File

@@ -25,12 +25,20 @@
<subdirs>
all
c01
c02
n14
n16
n21
n22
n23
n24
n25
n26
n27
n28
r05
r06
s03
t03
t04
</subdirs>
</gwbuild>

View File

@@ -26,27 +26,39 @@ initApps:
#ifdef APPS_NETWORK
ldi yl, LOW(netInterfaceData)
ldi yh, HIGH(netInterfaceData)
rcall AppNetwork_Init
bigcall AppNetwork_Init
#endif
#ifdef APPS_ROUTER
bigcall AppRouter_Init
#endif
#ifdef APPS_HUB
bigcall AppHub_Init
#endif
#ifdef APPS_FORWARDER
bigcall AppForwarder_Init
#endif
#ifdef APPS_MOTION
rcall AppMotion_Init
bigcall AppMotion_Init
#endif
#ifdef APPS_DOOR
rcall AppDoor_Init
bigcall AppDoor_Init
#endif
#ifdef APPS_REPORTSENSORS
rcall AppReportSensors_Init
bigcall AppReportSensors_Init
#endif
#ifdef APPS_STATS
rcall AppStats_Init
bigcall AppStats_Init
#endif
#ifdef APPS_MA_LIGHT
rcall AppMotionLight_Init
bigcall AppMotionLight_Init
#endif
; done
@@ -62,9 +74,37 @@ initApps:
;
runApps:
clr r16
#ifdef APPS_ROUTER
push r16
bigcall AppRouter_Run
pop r16
sbci r16, 0 ; decrease r16 only if CFLAG set
#endif
#ifdef APPS_HUB
push r16
bigcall AppHub_Run
pop r16
sbci r16, 0 ; decrease r16 only if CFLAG set
#endif
#ifdef APPS_FORWARDER
push r16
bigcall AppForwarder_Run
pop r16
sbci r16, 0 ; decrease r16 only if CFLAG set
#endif
; add more modules here
tst r16
clc
breq runApps_end
sec
runApps_end:
ret
; @end
@@ -78,11 +118,11 @@ mainAppsOnPacketReceived:
; handle messages
ldi yl, LOW(netInterfaceData)
ldi yh, HIGH(netInterfaceData)
rcall AppNetwork_HandleMsg
bigcall AppNetwork_HandleMsg
#endif
#ifdef APPS_MA_LIGHT
rcall AppMotionLight_OnPacketReceived
bigcall AppMotionLight_OnPacketReceived
#endif
; add more here

View File

@@ -1,278 +0,0 @@
; ***************************************************************************
; copyright : (C) 2025 by Martin Preuss
; email : martin@libchipcard.de
;
; ***************************************************************************
; * This file is part of the project "AqHome". *
; * Please see toplevel file COPYING of that project for license details. *
; ***************************************************************************
; not used for now
; ***************************************************************************
; macros
.macro mAttnInitInt0
; select falling edge of ATTN
inr r16, MCUCR
cbr r16, (1<<ISC01) | (1<<ISC00)
sbr r16, (1<<ISC01) | (0<<ISC00)
outr MCUCR, r16
.endmacro
.macro mAttnInitPCI
in r16, GIMSK ; enable pin change irq PCIE0 or PCIE1
ori r16, (1<<COM_IRQ_GIMSK_ATTN)
out GIMSK, R16
ldi r16, (1<<COM_IRQ_GIFR_ATTN) ; clear pending irq by writing 1 to ATTN bit
out GIFR, r16
.endmacro
.macro mAttnEnableIrqInt0
inr r16, COM_IRQ_ADDR_ATTN
sbr r16, (1<<ATTN_IRQ_BIT)
outr COM_IRQ_ADDR_ATTN, r16
.endmacro
.macro mAttnEnableIrqPCI
sbi COM_IRQ_ADDR_ATTN, ATTN_IRQ_BIT ; enable pin change irq for ATTN line
.endmacro
.macro mAttnDisableIrqInt0
inr r16, COM_IRQ_ADDR_ATTN ; disable irq for ATTN line
cbr r16, (1<<ATTN_IRQ_BIT)
outr COM_IRQ_ADDR_ATTN, r16
.endmacro
.macro mAttnDisableIrqPCI
cbi COM_IRQ_ADDR_ATTN, ATTN_IRQ_BIT ; disable pin change irq for ATTN line
.endmacro
; ***************************************************************************
; code
.cseg
; ---------------------------------------------------------------------------
; @routine ATTN_Init @global
;
; @clobbers R16
ATTN_Init:
.ifdef INT0
.if ATTN_IRQ_BIT == INT0
mAttnInitInt0
.else
mAttnInitPCI
.endif
.else
mAttnInitPCI
.endif
rcall ATTN_SetHighEnableIrq
sec
ret
; @end
; ---------------------------------------------------------------------------
; @routine ATTN_SetLowDisableIrq @global
;
; @clobbers R16
ATTN_SetLowDisableIrq:
.ifdef INT0
.if ATTN_IRQ_BIT == INT0
mAttnDisableIrqInt0
.else
mAttnDisableIrqPCI
.endif
.else
mAttnDisableIrqPCI
.endif
sbi ATTN_DDR, ATTN_PIN ; set ATTN as output
cbi ATTN_OUTPUT, ATTN_PIN ; set ATTN low
ret
; @end
; ---------------------------------------------------------------------------
; @routine ATTN_SetHighEnableIrq @global
;
; @clobbers R16
ATTN_SetHighEnableIrq:
cbi ATTN_DDR, ATTN_PIN ; set ATTN as input
.ifdef ATTN_PUE
cbi ATTN_PUE, ATTN_PIN ; disable pullup on ATTN
.else
cbi ATTN_OUTPUT, ATTN_PIN ; disable pullup on ATTN
.endif
.ifdef INT0
.if ATTN_IRQ_BIT == INT0
mAttnEnableIrqInt0
.else
mAttnEnableIrqPCI
.endif
.else
mAttnEnableIrqPCI
.endif
ret
; @end
; ---------------------------------------------------------------------------
; @routine ATTN_IsHigh @global
;
; @return CFLAG set if ATTN is high
; @clobbers none
ATTN_IsHigh:
clc
sbic ATTN_INPUT, ATTN_PIN ; ATTN low?
sec ; yes, set CF
ret
; @end
; ---------------------------------------------------------------------------
; @routine ATTN_WaitForStateSeconds
;
; Wait for ATTN state max given seconds
;
; @return CFLAG set if okay (data available), cleared on error
; @param R16 expected state (0xff for high, 0 for low)
; @param r20 maximum number of seconds to wait
; @clobbers: r20, r22
ATTN_WaitForStateSeconds:
ATTN_WaitForStateSeconds_loop:
push r20
rcall ATTN_WaitForState1s ; (r20, r22)
pop r20
brcs ATTN_WaitForStateSeconds_gotit
sbi LED_PIN, LED_PINNUM ; toggle (doen't work on AtMega8515)
dec r20
brne ATTN_WaitForStateSeconds_loop
clc
ATTN_WaitForStateSeconds_gotit:
ret
; @end
; ---------------------------------------------------------------------------
; @routine ATTN_WaitForState1s
;
; Wait for ATTN state for max 1s
;
; @param R16 expected state (0xff for high, 0 for low)
; @return CFLAG set if okay (ATTN state reached), cleared on error
; @clobbers: r20, r22
ATTN_WaitForState1s:
ldi r20, 100
ATTN_WaitForState1s_loop:
push r20
rcall ATTN_WaitForState10ms ; (R20, R22)
pop r20
brcs ATTN_WaitForState1s_gotit
dec r20
brne ATTN_WaitForState1s_loop
clc
ATTN_WaitForState1s_gotit:
ret
; @end
; ---------------------------------------------------------------------------
; @routine ATTN_WaitForState10ms
;
; Wait for ATTN state for max 10 milliseconds.
;
; @param R16 expected state (0xff for high, 0 for low)
; @return CFLAG set if okay (ATTN state reached), cleared on error
; @clobbers: r20, r22
ATTN_WaitForState10ms:
.if clock == 8000000
ldi r20, 80
.endif
.if clock == 1000000
ldi r20, 10
.endif
ATTN_WaitForState10ms_loop:
push r20
rcall ATTN_WaitForState1000Cycles ; (r20, r22)
pop r20
brcs ATTN_WaitForState10ms_gotit
dec r20
brne ATTN_WaitForState10ms_loop
clc
ATTN_WaitForState10ms_gotit:
ret
; @end
; ---------------------------------------------------------------------------
; @routine ATTN_WaitForStateState1000Cycles
;
; Wait for ATTN state for max 1000 clock cycles
; (about 1ms at 1MHz, 0.125ms at 8MHz)
;
; @param R16 expected state (0xff for high, 0 for low)
; @return CFLAG set if okay (packet received), cleared on error
; @clobbers: r20, r22
ATTN_WaitForState1000Cycles:
ldi r20, 90 ; 1
ATTN_WaitForState1000Cycles_loop:
push r17 ; +2
in r17, COM_ATTN_INPUT ; +1
eor r17, r16 ; +1
andi r17, (1<<COM_ATTN_PIN) ; +1
pop r17 ; +2
breq ATTN_WaitForState1000Cycles_stateReached ; +1
dec r20 ; +1
brne ATTN_WaitForState1000Cycles_loop ; +2
clc
ret
ATTN_WaitForState1000Cycles_stateReached:
sec
ret
; @end

View File

@@ -7,7 +7,6 @@
; * Please see toplevel file COPYING of that project for license details. *
; ***************************************************************************
; ---------------------------------------------------------------------------
; Value types
@@ -21,6 +20,7 @@
.equ AQHOME_VALUETYPE_CO2 = 7
.equ AQHOME_VALUETYPE_TVOC = 8
.equ AQHOME_VALUETYPE_STATS = 9
.equ AQHOME_VALUETYPE_LIGHT = 10
; Value Ids
@@ -32,11 +32,21 @@
.equ AQHOME_VALUEID_STATS_ERRS_NOBUF = 0xe4
.equ AQHOME_VALUEID_STATS_ERRS_COLLISIONS = 0xe5
.equ AQHOME_VALUEID_STATS_ERRS_BUSY = 0xe6
.equ AQHOME_VALUEID_STATS_HEAP_USED = 0xe7
.equ AQHOME_VALUEID_STATS_HEAP_FREE = 0xe8
.equ AQHOME_VALUEID_STATS_ERRS_MSGSIZE = 0xe7
.equ AQHOME_VALUEID_STATS_ERRS_MISSED = 0xe8
.equ AQHOME_VALUEID_STATS_PACKETS_IN2 = 0xe9
.equ AQHOME_VALUEID_STATS_PACKETS_OUT2 = 0xea
.equ AQHOME_VALUEID_STATS_ERRS_CONTENT2 = 0xeb
.equ AQHOME_VALUEID_STATS_ERRS_IO2 = 0xec
.equ AQHOME_VALUEID_STATS_ERRS_NOBUF2 = 0xed
.equ AQHOME_VALUEID_STATS_ERRS_COLLISIONS2 = 0xee
.equ AQHOME_VALUEID_STATS_ERRS_BUSY2 = 0xef
.equ AQHOME_VALUEID_STATS_ERRS_MSGSIZE2 = 0xf0
.equ AQHOME_VALUEID_STATS_ERRS_MISSED2 = 0xf1
.equ AQHOME_VALUEID_STATS_HEAP_USED = 0xf2
.equ AQHOME_VALUEID_STATS_HEAP_FREE = 0xf3
; ---------------------------------------------------------------------------
@@ -47,6 +57,12 @@
.equ TLV_OFFS_LEN = 0
.equ TLV_OFFS_TYPE = 1
.equ TLV_OFFS_VALUE = 2
; ---------------------------------------------------------------------------
; EEPROM positions
@@ -66,5 +82,18 @@
.equ EEPROM_OFFS_MAL_CONF_SRC2_VALUEID = 19 ; 1 byte
.equ EEPROM_OFFS_MAL_CONF_RGBWVALUE = 20 ; 4 bytes
; next is 24
.equ EEPROM_OFFS_ROUTER_RANGE_BEGIN = 24 ; 1 byte
.equ EEPROM_OFFS_ROUTER_RANGE_END = 25 ; 1 byte
; next is 26
.equ EEPROM_OFFS_TLV = 32 ; begin TLV area here
.equ EEPROM_TLV_ROUTER_RANGE = 0x10 ; 2 bytes (range begin, range end)

View File

@@ -0,0 +1,152 @@
; ***************************************************************************
; copyright : (C) 2025 by Martin Preuss
; email : martin@libchipcard.de
;
; ***************************************************************************
; * This file is part of the project "AqHome". *
; * Please see toplevel file COPYING of that project for license details. *
; ***************************************************************************
; Hardware routine for AtMega 644P devices
.cseg
; ---------------------------------------------------------------------------
; @routine systemInitHardware
;
systemInitHardware:
; set all ports as inputs and enable internal pull-up resistors
ldi r16, 0xff
clr r17
out DDRA, r17 ; all input
out PORTA, r16 ; enable pull-up on all
out DDRB, r17 ; all input
out PORTB, r16 ; enable pull-up on all
out DDRC, r17 ; all input
out PORTC, r16 ; enable pull-up on all
out DDRD, r17 ; all input
out PORTD, r16 ; enable pull-up on all
ret
; @end
; ---------------------------------------------------------------------------
; @routine systemSetSpeed
;
systemSetSpeed:
ret
; @end
; ---------------------------------------------------------------------------
; @routine systemSleep
;
systemSleep:
; only modify SE, SM2, SM1 and SM0
cli
inr r16, SMCR
cbr r16, (1<<SE) | (0<<SM2) | (0<<SM1) | (0<<SM0)
outr SMCR, r16
sei ; make sure interrupts really are enabled
inr r16, SMCR ; enable sleep mode
sbr r16, (1<<SE)
outr SMCR, r16
sleep ; sleep, wait for interrupt
inr r16, SMCR ; disable sleep mode
cbr r16, (1<<SE)
outr SMCR, r16
ret
; @end
; ---------------------------------------------------------------------------
; @routine systemSetupTimer0
;
systemSetupTimer0: ; setup timer for IRQ every 100ms
ldi r16, (1<<WGM01) | (0<<WGM00) ; Prescaler 1024, CTC mode
outr TCCR0A, r16
ldi r16, (1<<CS02) | (0<<CS01) | (1<<CS00) | (0<<WGM02) ; Prescaler 1024, CTC mode
outr TCCR0B, r16
;
; Settings for clock 1Mhz (default)
; use timer0 with OCR0A=98-1 (irq every 97.65625 millisecs), baseTimerModuleReloadValue 1
;
.if clock == 1000000
; CMP-A interrupt about every 100ms
ldi r16, 98-1 ; (1,000,000/1024)/10 = 97.65625
outr OCR0A, r16
ldi r16, 1
sts baseTimerModuleReloadValue, r16
sts baseTimerModuleTickCounter, r16
; Settings for clock 8Mhz
; use timer0 with OCR0=78 (irq every 9.984 millisecs), baseTimerModuleReloadValue 10
;
.elif clock == 8000000
; CMP interrupt about every 10ms
ldi r16, 78-1
outr OCR0A, r16
ldi r16, 10
sts baseTimerModuleReloadValue, r16
sts baseTimerModuleTickCounter, r16
.elif clock == 10000000
; CMP-A interrupt about every 10ms
ldi r16, 98-1 ; (10,000,000/1024)/10 = 97.65625
outr OCR0A, r16
ldi r16, 10
sts baseTimerModuleReloadValue, r16
sts baseTimerModuleTickCounter, r16
.elif clock == 20000000
; CMP-A interrupt about every 5ms
ldi r16, 98-1
outr OCR0A, r16
ldi r16, 20
sts baseTimerModuleReloadValue, r16
sts baseTimerModuleTickCounter, r16
.else
.error "Unhandled clock frequency"
.endif
ldi r16, (1<<OCF0A) ; clear pending interrupts
outr TIFR0, r16
inr r16, TIMSK0
sbr r16, (1<<OCIE0A) ; Timer/Counter0 Output Compare Match A Interrupt Enable
outr TIMSK0, r16
.endif
sec
ret
; @end

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