178 Commits

Author SHA1 Message Date
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
Martin Preuss
af75532ba7 More planning on GUI. 2025-05-28 19:03:14 +02:00
Martin Preuss
335163f887 temporarily disabled writing text on startup (need the flash space). 2025-05-28 00:52:40 +02:00
Martin Preuss
b3274466a3 smaller changes to make debugging easier. 2025-05-28 00:52:06 +02:00
Martin Preuss
064e84f5e8 added a more compact uart module. 2025-05-28 00:51:37 +02:00
Martin Preuss
18bc231951 added some test code. 2025-05-28 00:51:07 +02:00
Martin Preuss
9a19bf739d fixed a bug (wrong register). 2025-05-28 00:50:52 +02:00
Martin Preuss
9e6feecb88 bootloader: decreased waiting times for LED blinking on bootup. 2025-05-28 00:50:26 +02:00
Martin Preuss
baf77ed182 fixed defs file. 2025-05-28 00:49:44 +02:00
Martin Preuss
b2f7232422 c01: use alternative uart module. 2025-05-28 00:49:28 +02:00
Martin Preuss
961568f721 renamed makros M_IO_READ and M_IO_WRITE to inr and outr 2025-05-28 00:49:07 +02:00
Martin Preuss
042db13994 avr/apps/stats: send VALUE_REPORT messages instead of individual stats messages.
this makes it easier to add some more stats later and it removes some
messages.
2025-05-28 00:47:19 +02:00
Martin Preuss
ba434d88a2 improved output. 2025-05-28 00:45:10 +02:00
Martin Preuss
d32e2f4b81 handle stats values in nodes server. added missing code. 2025-05-28 00:44:58 +02:00
Martin Preuss
e40139fee2 add missing files. 2025-05-26 21:41:41 +02:00
Martin Preuss
d8612a01ca adapted to latest changes. 2025-05-26 21:41:30 +02:00
Martin Preuss
474e63c395 fixed documentation. 2025-05-26 21:40:45 +02:00
Martin Preuss
ceaeb756fb let old file include new files. 2025-05-26 21:40:28 +02:00
Martin Preuss
603a8e93d0 avr/flash: removed unnecessary code. 2025-05-26 21:10:12 +02:00
Martin Preuss
4fdfd77a54 put wait routines into their own files.
helps with reducing size of bootloader.
2025-05-26 21:09:45 +02:00
Martin Preuss
6b5f5e877d avr/heap: fixed some bugs. 2025-05-24 21:01:59 +02:00
Martin Preuss
77bb64fbb6 Merge branch 'mp-2025_05-uart2' 2025-05-24 17:49:44 +02:00
300 changed files with 23062 additions and 3247 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.12" so_current="0" so_age="0" so_revision="12" 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

@@ -0,0 +1,7 @@
<?xml?>
<gwbuild>
<subdirs>
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

@@ -117,6 +117,26 @@ void _handleMsgValue(AQH_NODE_SERVER *xo, const AQH_MESSAGE *msg)
if (ni==NULL) {
DBG_INFO(AQH_LOGDOMAIN, "Error handling message");
}
else {
uint8_t valueId;
uint16_t val;
val=AQH_ValueMessage_GetValueNom(msg);
valueId=AQH_ValueMessage_GetValueId(msg);
switch(valueId) {
case AQH_ENDPOINT_VID_STATS_PACKETS_IN: AQH_NodeInfo_SetStatsPacketsIn(ni, val); AQH_NodeDb_SetModified(xo->nodeDb); break;
case AQH_ENDPOINT_VID_STATS_PACKETS_OUT: AQH_NodeInfo_SetStatsPacketsOut(ni, val); AQH_NodeDb_SetModified(xo->nodeDb); break;
case AQH_ENDPOINT_VID_STATS_ERRS_CONTENT: AQH_NodeInfo_SetStatsCrcErrors(ni, val); AQH_NodeDb_SetModified(xo->nodeDb); break;
case AQH_ENDPOINT_VID_STATS_ERRS_IO: AQH_NodeInfo_SetStatsIoErrors(ni, val); AQH_NodeDb_SetModified(xo->nodeDb); break;
case AQH_ENDPOINT_VID_STATS_ERRS_NOBUF: break;
case AQH_ENDPOINT_VID_STATS_ERRS_COLLISIONS: AQH_NodeInfo_SetStatsCollisions(ni, val); AQH_NodeDb_SetModified(xo->nodeDb); break;
case AQH_ENDPOINT_VID_STATS_ERRS_BUSY: AQH_NodeInfo_SetStatsBusy(ni, val); AQH_NodeDb_SetModified(xo->nodeDb); break;
case AQH_ENDPOINT_VID_STATS_HEAP_USED: break;
case AQH_ENDPOINT_VID_STATS_HEAP_FREE: break;
default:
}
}
}
@@ -145,11 +165,13 @@ void _handleMsgComSendStat(AQH_NODE_SERVER *xo, const AQH_MESSAGE *msg)
if (ni==NULL) {
DBG_INFO(AQH_LOGDOMAIN, "Error handling message");
}
AQH_NodeInfo_SetStatsPacketsOut(ni, AQH_SendStatsMessage_GetPacketsOut(msg));
AQH_NodeInfo_SetStatsCollisions(ni, AQH_SendStatsMessage_GetCollisions(msg));
AQH_NodeInfo_SetStatsBusy(ni, AQH_SendStatsMessage_GetBusyErrors(msg));
AQH_NodeDb_SetModified(xo->nodeDb);
_updateTimestampLastChange(ni);
else {
AQH_NodeInfo_SetStatsPacketsOut(ni, AQH_SendStatsMessage_GetPacketsOut(msg));
AQH_NodeInfo_SetStatsCollisions(ni, AQH_SendStatsMessage_GetCollisions(msg));
AQH_NodeInfo_SetStatsBusy(ni, AQH_SendStatsMessage_GetBusyErrors(msg));
AQH_NodeDb_SetModified(xo->nodeDb);
_updateTimestampLastChange(ni);
}
}

View File

@@ -34,6 +34,18 @@
#define AQH_ENDPOINT_VID_STATS_PACKETS_IN 0xe0
#define AQH_ENDPOINT_VID_STATS_PACKETS_OUT 0xe1
#define AQH_ENDPOINT_VID_STATS_ERRS_CONTENT 0xe2
#define AQH_ENDPOINT_VID_STATS_ERRS_IO 0xe3
#define AQH_ENDPOINT_VID_STATS_ERRS_NOBUF 0xe4
#define AQH_ENDPOINT_VID_STATS_ERRS_COLLISIONS 0xe5
#define AQH_ENDPOINT_VID_STATS_ERRS_BUSY 0xe6
#define AQH_ENDPOINT_VID_STATS_HEAP_USED 0xe7
#define AQH_ENDPOINT_VID_STATS_HEAP_FREE 0xe8
AQH_OBJECT *AQH_NodeServer_new(AQH_EVENT_LOOP *eventLoop);
int AQH_NodeServer_Init(AQH_OBJECT *o, int argc, char **argv);
void AQH_NodeServer_Fini(AQH_OBJECT *o);

View File

@@ -37,6 +37,9 @@
getdevices.h
adddata.h
getdatapoints.h
getfirstdata.h
getlastdata.h
getperioddata.h
setdata.h
moddevice.h
watch.h
@@ -49,6 +52,9 @@
getdevices.c
adddata.c
getdatapoints.c
getfirstdata.c
getlastdata.c
getperioddata.c
setdata.c
moddevice.c
watch.c

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,6 +17,9 @@
#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"
@@ -92,6 +95,9 @@ 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")),

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,14 +164,18 @@ 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);
if (ts)
fprintf(stdout, "last seen %s, ", GWEN_Timestamp_GetString(ts));
fprintf(stdout, "last seen %04d/%02d/%02d-%02d:%02d:%02d, ",
GWEN_Timestamp_GetYear(ts),
GWEN_Timestamp_GetMonth(ts),
GWEN_Timestamp_GetDay(ts),
GWEN_Timestamp_GetHour(ts),
GWEN_Timestamp_GetMinute(ts),
GWEN_Timestamp_GetSecond(ts));
fprintf(stdout, "pkg out: %d, pkg in: %d, collisions: %d, busy: %d, crc: %d, io: %d\n",
AQH_NodeInfo_GetStatsPacketsOut(ni),
AQH_NodeInfo_GetStatsPacketsIn(ni),
@@ -183,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

@@ -261,6 +261,14 @@ int AQH_ValueModality_fromString(const char *s)
return AQH_ValueModality_RGBW;
else if (strcasecmp(s, "motion")==0)
return AQH_ValueModality_Motion;
else if (strcasecmp(s, "co2")==0)
return AQH_ValueModality_Co2;
else if (strcasecmp(s, "tvoc")==0)
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;
}
@@ -276,6 +284,10 @@ const char *AQH_ValueModality_toString(int i)
case AQH_ValueModality_RGB: return "rgb";
case AQH_ValueModality_RGBW: return "rgbw";
case AQH_ValueModality_Motion: return "motion";
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

@@ -42,7 +42,11 @@ enum {
AQH_ValueModality_Door,
AQH_ValueModality_RGB,
AQH_ValueModality_RGBW,
AQH_ValueModality_Motion
AQH_ValueModality_Motion,
AQH_ValueModality_Co2,
AQH_ValueModality_TVOC,
AQH_ValueModality_Stats,
AQH_ValueModality_Light
};

View File

@@ -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;

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_ERROR(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, 45456);
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

@@ -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

@@ -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

@@ -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

@@ -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

@@ -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,7 @@
network
reportsensors
stats
router
</subdirs>
<extradist>

View File

@@ -21,36 +21,36 @@ This app can watch for up to two node-value pairs.
----------------
RGB Value for activated light:
aqhome-tool setValue -N nodes/XXXXXXXX/MALRGBWVALUE -v GRWB
aqhome-tool setdata -N nodes/XXXXXXXX/MALRGBWVALUE -v GRWB
Example:
Set color of LED strip to blue (half intensity)
aqhome-tool setValue -N nodes/12345678/MALRGBWVALUE -v 0x80
aqhome-tool setdata -N nodes/12345678/MALRGBWVALUE -v 0x80
1.2 MALONTIME
-------------
On-Time after activation:
aqhome-tool setValue -N nodes/XXXXXXXX/MALONTIME TIME_IN_1/10_SECS
aqhome-tool setdata -N nodes/XXXXXXXX/MALONTIME TIME_IN_1/10_SECS
Example:
Keep light on after activation for 20 seconds
aqhome-tool setValue -N nodes/12345678/MALONTIME -v 200
aqhome-tool setdata -N nodes/12345678/MALONTIME -v 200
1.3. MALSOURCE1, MALSOURCE2
---------------------------
Sources for Motion Messages:
aqhome-tool setValue -N nodes/XXXXXXXX/MALSOURCE1 -v VVNN
aqhome-tool setValue -N nodes/XXXXXXXX/MALSOURCE2 -v VVNN
aqhome-tool setdata -N nodes/XXXXXXXX/MALSOURCE1 -v VVNN
aqhome-tool setdata -N nodes/XXXXXXXX/MALSOURCE2 -v VVNN
VVNN is a 16-bit value, lower 8 bit contain the source node address, higher 8 bit contain the
value id to react to.
Example:
React to value report messages with value id 7 from node with address 2
aqhome-tool setValue -N nodes/12345678/MALSOURCE1 -v 0x0702
aqhome-tool setdata -N nodes/12345678/MALSOURCE1 -v 0x0702

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,57 @@ 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:
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
rcall 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 +276,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 +291,6 @@ appNetworkHandleStateUp:
ret
; ---------------------------------------------------------------------------
; @routine appNetworkSendMsgNextState
;
@@ -241,7 +298,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 +363,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 +387,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 +402,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 +422,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 +453,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 +481,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,14 @@
; @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)
bigcall NET_Interface_AddOrReleaseOutMsg ; (R16, R17, R18, X)
AppNetwork_SendTxdStats_end:
ret
; @end
@@ -39,14 +39,14 @@ AppNetwork_SendTxdStats_end:
; @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)
bigcall NET_Interface_AddOrReleaseOutMsg ; (R16, R17, R18, X)
AppNetwork_SendRxdStats_end:
ret
; @end
@@ -60,14 +60,14 @@ AppNetwork_SendRxdStats_end:
; @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)
bigcall NET_Interface_AddOrReleaseOutMsg ; (R16, R17, R18, X)
AppNetwork_SendMemStats_end:
ret
; @end
@@ -81,14 +81,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 = 10
.equ APP_STATS_INTERVAL_MINS = 31
@@ -53,6 +53,19 @@ AppStats_Fini:
;
AppStats_OnEveryMinute:
push r15
in r15, SREG
cli
rcall AppStats_OnEveryMinute_noIrq
out SREG, r15
pop r15
ret
AppStats_OnEveryMinute_noIrq:
; ldi yl, LOW(netInterfaceData)
; ldi yh, HIGH(netInterfaceData)
; rcall AppNetwork_SendRxdStats ; debug
lds r16, appStatsTimer
inc r16
cpi r16, APP_STATS_INTERVAL_MINS
@@ -62,23 +75,156 @@ AppStats_OnEveryMinute_store:
sts appStatsTimer, r16
ldi yl, LOW(netInterfaceData)
ldi yh, HIGH(netInterfaceData)
ldi r17, AQHOME_VALUEID_STATS_PACKETS_IN
ldi xl, LOW(netInterfaceData)
ldi xh, HIGH(netInterfaceData)
cpi r16, 1
breq AppStats_OnEveryMinute_sendRxdStats
cpi r16, 2
breq AppStats_OnEveryMinute_sendTxdStats
cpi r16, 3
breq AppStats_OnEveryMinute_sendDevice
cpi r16, 4
breq AppStats_OnEveryMinute_sendMemStats
cpi r16, 3
breq AppStats_OnEveryMinute_sendPacketsIn
cpi r16, 5
breq AppStats_OnEveryMinute_sendPacketsOut
cpi r16, 6
breq AppStats_OnEveryMinute_sendDevice
cpi r16, 7
breq AppStats_OnEveryMinute_sendContentErrs
cpi r16, 9
breq AppStats_OnEveryMinute_sendIoErrs
cpi r16, 10
breq AppStats_OnEveryMinute_sendDevice
cpi r16, 11
breq AppStats_OnEveryMinute_sendNoBufErrs
cpi r16, 13
breq AppStats_OnEveryMinute_sendCollisionErrs
cpi r16, 14
breq AppStats_OnEveryMinute_sendDevice
cpi r16, 15
breq AppStats_OnEveryMinute_sendBusyErrs
#ifdef APP_STATS_NETDEV2
ldi r17, AQHOME_VALUEID_STATS_PACKETS_IN2
ldi xl, LOW(netInterfaceData2)
ldi xh, HIGH(netInterfaceData2)
cpi r16, 16
breq AppStats_OnEveryMinute_sendPacketsIn
cpi r16, 17
breq AppStats_OnEveryMinute_sendPacketsOut
cpi r16, 18
breq AppStats_OnEveryMinute_sendDevice
cpi r16, 19
breq AppStats_OnEveryMinute_sendContentErrs
cpi r16, 20
breq AppStats_OnEveryMinute_sendIoErrs
cpi r16, 21
breq AppStats_OnEveryMinute_sendDevice
cpi r16, 22
breq AppStats_OnEveryMinute_sendNoBufErrs
cpi r16, 23
breq AppStats_OnEveryMinute_sendDevice
cpi r16, 24
breq AppStats_OnEveryMinute_sendCollisionErrs
cpi r16, 25
breq AppStats_OnEveryMinute_sendBusyErrs
#endif
#ifdef MODULES_HEAP
cpi r16, 26
breq AppStats_OnEveryMinute_sendHeapUsed
cpi r16, 27
breq AppStats_OnEveryMinute_sendHeapfree
#endif
cpi r16, 28
breq AppStats_OnEveryMinute_sendDevice
ret
AppStats_OnEveryMinute_sendTxdStats:
rjmp AppNetwork_SendTxdStats
AppStats_OnEveryMinute_sendRxdStats:
rjmp AppNetwork_SendRxdStats
AppStats_OnEveryMinute_sendMemStats:
rjmp AppNetwork_SendMemStats
AppStats_OnEveryMinute_sendDevice:
rjmp AppNetwork_SendDevice
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
ret
AppStats_OnEveryMinute_sendPacketsIn:
ldi r16, 0
rjmp appStatsSendDeviceStat
AppStats_OnEveryMinute_sendPacketsOut:
ldi r16, 1
rjmp appStatsSendDeviceStat
AppStats_OnEveryMinute_sendContentErrs:
ldi r16, 2
rjmp appStatsSendDeviceStat
AppStats_OnEveryMinute_sendIoErrs:
ldi r16, 3
rjmp appStatsSendDeviceStat
AppStats_OnEveryMinute_sendNoBufErrs:
ldi r16, 4
rjmp appStatsSendDeviceStat
AppStats_OnEveryMinute_sendCollisionErrs:
ldi r16, 5
rjmp appStatsSendDeviceStat
AppStats_OnEveryMinute_sendBusyErrs:
ldi r16, 6
rjmp appStatsSendDeviceStat
#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
#endif
; @end
; ---------------------------------------------------------------------------
; @routine AppStats_OnEveryMinute @global
;
; @param r16 index into device table (e.g. 0 for NET_IFACE_OFFS_PACKETSIN_LOW)
; @param r17 offset to first value id (e.g. AQHOME_VALUEID_STATS_PACKETS_IN for device 0,
; AQHOME_VALUEID_STATS_PACKETS_IN2 for device 1)
; @param Y pointer to device to send data to
; @param X pointer to device to inspect
appStatsSendDeviceStat:
add r17, r16
lsl r16
adiw xh:xl, NET_IFACE_OFFS_PACKETSIN_LOW
add xl, r16
adc xh, r16
sub xh, r16
ld r18, X+
ld r19, X
rjmp appStatsSend16BitValue
; @end
; ---------------------------------------------------------------------------
; @routine AppStats_OnEveryMinute @global
;
; @param R17 value id
; @param R19:R18 value
appStatsSend16BitValue:
ldi r20, 1
clr r21
ldi r22, AQHOME_VALUETYPE_STATS
bigjmp Main_SendValueReport
; @end

View File

@@ -21,9 +21,17 @@
utils_wait.asm
utils_wait_fixed.asm
utils_wait_pin.asm
wait_100us.asm
wait_10us.asm
wait_1ms.asm
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

@@ -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

@@ -8,8 +8,8 @@
; ***************************************************************************
; M_IO_READ DEST, SRC
.macro M_IO_READ
; inr DEST, SRC
.macro inr
.if @1 < 64
in @0, @1
.else
@@ -19,8 +19,8 @@
; M_IO_WRITE DEST, SRC
.macro M_IO_WRITE
; outr DEST, SRC
.macro outr
.if @0 < 64
out @0, @1
.else

View File

@@ -8,84 +8,10 @@
; ***************************************************************************
; ---------------------------------------------------------------------------
; @routine Utils_WaitFor10MicroSecs @global
;
; wait for 10 microsecs (minus cycles for call and ret).
;
; @clobbers r22
Utils_WaitFor10MicroSecs:
Utils_WaitNanoSecs 10000, 7, r22 ; wait for 10us (minus RCALL and RET)
ret
; @end
; ---------------------------------------------------------------------------
; @routine Utils_WaitFor50MicroSecs @global
;
; wait for 50 microsecs (minus cycles for call and ret).
;
; @clobbers r22
Utils_WaitFor50MicroSecs:
Utils_WaitNanoSecs 50000, 7, r22 ; wait for 50us (minus RCALL and RET)
ret
; @end
; ---------------------------------------------------------------------------
; @routine Utils_WaitFor100MicroSecs @global
;
; wait for about 100 microsecs.
;
; @clobbers r22
Utils_WaitFor100MicroSecs:
rcall Utils_WaitFor50MicroSecs
rcall Utils_WaitFor50MicroSecs
ret
; @end
; ---------------------------------------------------------------------------
; @routine Utils_WaitFor1MilliSec @global
;
; wait for about 1ms.
;
; @clobbers r22
Utils_WaitFor1MilliSec:
push r21
ldi r21, 10
Utils_WaitFor1MilliSec_loop:
rcall Utils_WaitFor100MicroSecs ; (R22)
dec r21
brne Utils_WaitFor1MilliSec_loop
pop r21
ret
; @end
; ---------------------------------------------------------------------------
; @routine Utils_WaitForMilliSecs @global
;
; wait for given amount of milliseconds
; @param r16 number of millisecs to wait
; @clobbers r22
Utils_WaitForMilliSecs:
rcall Utils_WaitFor100MicroSecs ; (R22)
dec r16
brne Utils_WaitForMilliSecs
ret
; @end
.include "common/wait_100us.asm"
.include "common/wait_10us.asm"
.include "common/wait_1ms.asm"
.include "common/wait_50us.asm"

27
avr/common/wait_100us.asm Normal file
View File

@@ -0,0 +1,27 @@
; ***************************************************************************
; copyright : (C) 2023 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. *
; ***************************************************************************
; ---------------------------------------------------------------------------
; @routine Utils_WaitFor100MicroSecs @global
;
; wait for about 100 microsecs.
;
; @clobbers r22
Utils_WaitFor100MicroSecs:
rcall Utils_WaitFor50MicroSecs
rcall Utils_WaitFor50MicroSecs
ret
; @end

25
avr/common/wait_10us.asm Normal file
View File

@@ -0,0 +1,25 @@
; ***************************************************************************
; copyright : (C) 2023 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. *
; ***************************************************************************
; ---------------------------------------------------------------------------
; @routine Utils_WaitFor10MicroSecs @global
;
; wait for 10 microsecs (minus cycles for call and ret).
;
; @clobbers r22
Utils_WaitFor10MicroSecs:
Utils_WaitNanoSecs 10000, 7, r22 ; wait for 10us (minus RCALL and RET)
ret
; @end

47
avr/common/wait_1ms.asm Normal file
View File

@@ -0,0 +1,47 @@
; ***************************************************************************
; copyright : (C) 2023 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. *
; ***************************************************************************
; ---------------------------------------------------------------------------
; @routine Utils_WaitFor1MilliSec @global
;
; wait for about 1ms.
;
; @clobbers r22
Utils_WaitFor1MilliSec:
push r21
ldi r21, 10
Utils_WaitFor1MilliSec_loop:
rcall Utils_WaitFor100MicroSecs ; (R22)
dec r21
brne Utils_WaitFor1MilliSec_loop
pop r21
ret
; @end
; ---------------------------------------------------------------------------
; @routine Utils_WaitForMilliSecs @global
;
; wait for given amount of milliseconds
; @param r16 number of millisecs to wait
; @clobbers r22
Utils_WaitForMilliSecs:
rcall Utils_WaitFor100MicroSecs ; (R22)
dec r16
brne Utils_WaitForMilliSecs
ret
; @end

24
avr/common/wait_50us.asm Normal file
View File

@@ -0,0 +1,24 @@
; ***************************************************************************
; copyright : (C) 2023 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. *
; ***************************************************************************
; ---------------------------------------------------------------------------
; @routine Utils_WaitFor50MicroSecs @global
;
; wait for 50 microsecs (minus cycles for call and ret).
;
; @clobbers r22
Utils_WaitFor50MicroSecs:
Utils_WaitNanoSecs 50000, 7, r22 ; wait for 50us (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,11 +25,20 @@
<subdirs>
all
c01
c02
n14
n16
n19
n20
n21
n22
n23
n24
n25
n26
n27
r04
r05
t03
</subdirs>

View File

@@ -26,27 +26,31 @@ 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_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 +66,23 @@ initApps:
;
runApps:
clr r16
#ifdef APPS_ROUTER
push r16
bigcall AppRouter_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 +96,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

@@ -7,7 +7,6 @@
; * Please see toplevel file COPYING of that project for license details. *
; ***************************************************************************
; ---------------------------------------------------------------------------
; Value types
@@ -20,8 +19,35 @@
.equ AQHOME_VALUETYPE_MOTION = 6
.equ AQHOME_VALUETYPE_CO2 = 7
.equ AQHOME_VALUETYPE_TVOC = 8
.equ AQHOME_VALUETYPE_STATS = 9
.equ AQHOME_VALUETYPE_LIGHT = 10
; Value Ids
.equ AQHOME_VALUEID_STATS_PACKETS_IN = 0xe0
.equ AQHOME_VALUEID_STATS_PACKETS_OUT = 0xe1
.equ AQHOME_VALUEID_STATS_ERRS_CONTENT = 0xe2
.equ AQHOME_VALUEID_STATS_ERRS_IO = 0xe3
.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_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
; ---------------------------------------------------------------------------
; Firmware types (deprecated)
@@ -31,6 +57,12 @@
.equ TLV_OFFS_LEN = 0
.equ TLV_OFFS_TYPE = 1
.equ TLV_OFFS_VALUE = 2
; ---------------------------------------------------------------------------
; EEPROM positions
@@ -50,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,136 @@
; ***************************************************************************
; 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
.endif
;
; Settings for clock 8Mhz
; use timer0 with OCR0=78 (irq every 9.984 millisecs), baseTimerModuleReloadValue 10
;
.if clock == 8000000
; CMP interrupt about every 10ms
ldi r16, 78-1
outr OCR0A, r16
ldi r16, 10
sts baseTimerModuleReloadValue, r16
sts baseTimerModuleTickCounter, r16
.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

View File

@@ -69,25 +69,25 @@ systemSleep:
; only modify SE, SM2, SM1 and SM0
cli
M_IO_READ r16, MCUCR
inr r16, MCUCR
cbr r16, (1<<SE) | (1<<SM2) | (1<<SM1)
M_IO_WRITE MCUCR, r16
outr MCUCR, r16
M_IO_READ r16, EMCUCR
inr r16, EMCUCR
cbr r16, (1<<SM0)
M_IO_WRITE EMCUCR, r16
outr EMCUCR, r16
sei ; make sure interrupts really are enabled
M_IO_READ r16, MCUCR ; enable sleep mode
inr r16, MCUCR ; enable sleep mode
sbr r16, (1<<SE)
M_IO_WRITE MCUCR, r16
outr MCUCR, r16
sleep ; sleep, wait for interrupt
M_IO_READ r16, MCUCR ; disable sleep mode
inr r16, MCUCR ; disable sleep mode
cbr r16, (1<<SE)
M_IO_WRITE MCUCR, r16
outr MCUCR, r16
ret
; @end
@@ -140,13 +140,13 @@ systemSetupTimer0: ; setup timer for IRQ every 100ms
.endif
.ifdef TIMSK0
M_IO_READ r16, TIMSK0
inr r16, TIMSK0
sbr r16, (1<<OCIE0) ; Timer/Counter0 Output Compare Match A Interrupt Enable
M_IO_WRITE TIMSK0, r16
outr TIMSK0, r16
.else
M_IO_READ r16, TIMSK
inr r16, TIMSK
sbr r16, (1<<OCIE0) ; Timer/Counter0 Output Compare Match A Interrupt Enable
M_IO_WRITE TIMSK, r16
outr TIMSK, r16
.endif
sec

View File

@@ -72,13 +72,14 @@ systemSleep:
; only modify SE, SM1 and SM0
cli
in r16, MCUCR
ldi r17, (1<<SE) | (1<<SM1) | (1<<SM0)
neg r17
and r16, r17
ori r16, (1<<SE) ; sleep mode "idle", enable
cbr r16, (1<<SE) | (1<<SM1) | (1<<SM0)
out MCUCR, r16
sbr r16, (1<<SE) | (0<<SM1) | (0<<SM0) ; sleep mode "idle", enable
out MCUCR, r16
sei ; make sure interrupts really are enabled
sleep ; sleep, wait for interrupt
cbr r16, (1<<SE) | (1<<SM1) | (1<<SM0)
out MCUCR, r16
ret
; @end
@@ -90,7 +91,7 @@ systemSleep:
;
systemSetupTimer0: ; setup timer for IRQ every 100ms
ldi r16, (1<<CS02) | (0<<CS01) | (1<<CS00) ; Prescaler 1024
ldi r16, (0<<WGM02) | (1<<CS02) | (0<<CS01) | (1<<CS00) ; Prescaler 1024
out TCCR0B, r16
ldi r16, (1<<WGM01) | (0<<WGM00) ; CTC mode
@@ -125,19 +126,12 @@ systemSetupTimer0: ; setup timer for IRQ every 100ms
ldi r16, (1<<OCF0A) ; clear pending interrupts
.ifdef TIFR0
out TIFR0, r16
.else
out TIFR, r16
.endif
ldi r16, (1<<OCIE0A) ; Timer/Counter0 Output Compare Match A Interrupt Enable
.ifdef TIMSK0
out TIMSK0, r16
.else
out TIMSK, r16
.endif
sec
sec
ret
; @end

View File

@@ -19,14 +19,14 @@
systemInitHardware:
; set all ports as inputs and enable internal pull-up resistors
ldi r16, 0xff
; ldi r16, 0xff
clr r17
out DDRA, r17 ; all input
sts PUEA, r16 ; enable pull-up on all
outr DDRA, r17 ; all input
outr PUEA, r17 ; disable pull-up on all
out DDRB, r17 ; all input
sts PUEB, r16 ; enable pull-up on all
outr DDRB, r17 ; all input
outr PUEB, r17 ; disable pull-up on all
ret
; @end
@@ -50,6 +50,22 @@ systemSetSpeed:
clr r16 ; SUT=0, CLKPS=0
sts CCP, r17
sts CLKPR, r16
#if 0
; read and set calibration data
push r15
inr r15, SREG
cli
ldi zl, 0
ldi zh, 1
ldi r16, (1<<RSIG) | (1<<SPMEN)
outr SPMCSR, r16
lpm r17, Z
outr OSCCAL0, r17
outr SREG, r15
pop r15
#endif
.endif
ret
@@ -118,16 +134,16 @@ systemSetupTimer0: ; setup timer for IRQ every 100ms
ldi r16, (1<<OCF0A) ; clear pending interrupts
.ifdef TIFR0
out TIFR0, r16
outr TIFR0, r16
.else
out TIFR, r16
outr TIFR, r16
.endif
ldi r16, (1<<OCIE0A) ; Timer/Counter0 Output Compare Match A Interrupt Enable
.ifdef TIMSK0
out TIMSK0, r16
outr TIMSK0, r16
.else
out TIMSK, r16
outr TIMSK, r16
.endif
sec
ret

View File

@@ -54,7 +54,8 @@
#ifdef MODULES_UART_BITBANG
.include "modules/uart_bitbang2/defs.asm"
.include "modules/uart_bitbang2/iface.asm"
.include "modules/uart_bitbang2/lowlevel.asm"
.include "modules/uart_bitbang2/bytelevel.asm"
.include "modules/uart_bitbang2/msglevel.asm"
#endif
#ifdef MODULES_UART_HW
@@ -67,20 +68,54 @@
#ifdef MODULES_COMONUART0
.include "modules/uart_hw/defs.asm"
.include "modules/uart_hw/lowlevel.asm"
.include "modules/uart_hw/m_lowlevel_uart.asm"
.include "modules/uart_hw/comonuart0.asm"
;.include "modules/uart_hw/defs.asm"
;.include "modules/uart_hw/lowlevel.asm"
;.include "modules/uart_hw/m_lowlevel_uart.asm"
;.include "modules/uart_hw/comonuart0.asm"
.include "modules/uart_hw2/defs.asm"
.include "modules/uart_hw2/comonuart0.asm"
#endif
#ifdef MODULES_COMONUART1
;.include "modules/uart_hw/defs.asm"
;.include "modules/uart_hw/lowlevel.asm"
;.include "modules/uart_hw/m_lowlevel_uart.asm"
;.include "modules/uart_hw/comonuart1.asm"
.include "modules/uart_hw2/defs.asm"
.include "modules/uart_hw2/comonuart1.asm"
#endif
#ifdef MODULES_TTYONUART1
#ifndef MODULES_COMONUART0
.include "modules/uart_hw/defs.asm"
.include "modules/uart_hw/lowlevel.asm"
.include "modules/uart_hw/m_lowlevel_uart.asm"
#endif
.include "modules/uart_hw/ttyonuart1.asm"
;.include "modules/uart_hw2/defs.asm"
;.include "modules/uart_hw2/ttyonuart1.asm"
#endif
#ifdef MODULES_COM2W
.include "modules/com2w/defs.asm"
.include "modules/com2w/com2w.asm"
#endif
#ifdef MODULES_COM2W0
.include "modules/com2w/defs.asm"
.include "modules/com2w/common.asm"
.include "modules/com2w/com2w0.asm"
#endif
#ifdef MODULES_COM2W1
.include "modules/com2w/defs.asm"
.include "modules/com2w/common.asm"
.include "modules/com2w/com2w1.asm"
#endif
@@ -143,6 +178,9 @@
#ifdef MODULES_CCS811
.include "modules/ccs811/main.asm"
#ifdef MODULES_NETWORK
.include "modules/ccs811/send.asm"
#endif
#endif
#ifdef MODULES_TCRT1000
@@ -156,6 +194,10 @@
#endif
#endif
#ifdef MODULES_LCD
.include "modules/lcd/main.asm"
#endif
#ifdef MODULES_ILI9341
.include "modules/lcd2/ili9341/defs.asm"
.include "modules/lcd2/ili9341/main.asm"
@@ -164,6 +206,21 @@
.include "modules/lcd2/ili9341/text.asm"
#endif
#ifdef MODULES_FONT
.include "modules/lcd2/font/defs.asm"
.include "modules/lcd2/font/main.asm"
#endif
#ifdef MODULES_WIN
.include "common/list.asm"
.include "common/tree.asm"
.include "modules/lcd2/win/defs.asm"
.include "modules/lcd2/win/object.asm"
.include "modules/lcd2/win/widget.asm"
.include "modules/lcd2/win/gui.asm"
#endif
#ifdef MODULES_FONT_8X8
.include "modules/lcd2/font/defs.asm"
.include "modules/lcd2/font/font8x8.asm"
@@ -176,6 +233,14 @@
#endif
#ifdef MODULES_BRIGHTNESS
.include "modules/brightness/main.asm"
#ifdef MODULES_NETWORK
.include "modules/brightness/send.asm"
#endif
#endif
#ifdef APPS_MOTION
.include "modules/f_keepup/main.asm"
.include "modules/valsched/main.asm"
@@ -196,6 +261,24 @@
.include "apps/network/main.asm"
.include "modules/network/msg/reboot-d.asm"
.include "modules/network/msg/reboot-r.asm"
.include "modules/network/msg/pong-w.asm"
.include "modules/network/msg/range-d.asm"
.include "modules/network/msg/range-r.asm"
.include "common/eeprom-r.asm"
.include "common/eeprom-w.asm"
#endif
#ifdef APPS_ROUTER
.include "apps/router/main.asm"
.include "modules/network/msg/reboot-d.asm"
.include "modules/network/msg/reboot-r.asm"
.include "modules/network/msg/pong-w.asm"
.include "modules/network/msg/range-d.asm"
.include "modules/network/msg/range-r.asm"
.include "modules/network/msg/range-w.asm"
.include "common/eeprom-r.asm"
.include "common/eeprom-w.asm"
#endif

View File

@@ -8,6 +8,7 @@
; ***************************************************************************
; ***************************************************************************
; code
@@ -31,16 +32,16 @@ main:
; rcall watchdogOff ; turn off watchdog timer (sometimes it stays on after reboot)
rcall systemSetSpeed
rcall systemInitHardware
rcall Utils_Init
rcall Utils_SetupUid
rcall initModules
rcall initApps
rcall Utils_InitialWait
bigcall systemSetSpeed
bigcall systemInitHardware
bigcall Utils_Init
bigcall Utils_SetupUid
bigcall initModules
bigcall initApps
bigcall Utils_InitialWait
sei ; Enable interrupts
rcall onSystemStart
bigcall onSystemStart
#ifdef MODULES_LED
ldi xl, LOW(blinkPattern) ; debug: set blink pattern
@@ -53,12 +54,36 @@ main:
#endif
main_loop:
rcall systemSleep ; system-dependant
rcall runModules
rcall runApps
rcall onEveryLoop ; call into main app
bigcall systemSleep ; system-dependant
; run loop as long as some run function returns with CFLAG set
clr r17
main_runLoop:
push r17
clr r16
push r16
bigcall runModules
pop r16
sbci r16, 0
push r16
bigcall runApps
pop r16
sbci r16, 0
push r16
bigcall onEveryLoop ; call into main app
pop r16
pop r17
tst r16
breq main_endRunLoop
inc r17
cpi r17, 2
brcc main_endRunLoop
brne main_runLoop
main_endRunLoop:
#ifdef MODULES_NETWORK
#ifndef MAIN_WITHOUT_MSG_HANDLING
rcall mainHandleMessages
@@ -69,7 +94,7 @@ main_loop:
main_loop_reboot:
cli
rjmp BOOTLOADER_ADDR
bigjmp BOOTLOADER_ADDR
; @end
@@ -80,53 +105,73 @@ main_loop_reboot:
; Called every 100ms. No arguments, no results.
onSystemTimerTick:
rcall onEvery100ms
bigcall onEvery100ms
#ifdef MODULES_CLOCK
rcall Clock_Every100ms ; generates calls to onEverySecond/Minute/Hour/Day
bigcall Clock_Every100ms ; generates calls to onEverySecond/Minute/Hour/Day
#endif
#ifdef MODULES_LED_SIMPLE
rcall LedSimple_Every100ms
bigcall LedSimple_Every100ms
#endif
#ifdef MODULES_UART_BITBANG
rcall UART_BitBang_Every100ms
bigcall UART_BitBang_Every100ms
#endif
#ifdef MODULES_UART_HW
rcall NET_Uart_Every100ms
bigcall NET_Uart_Every100ms
#endif
#ifdef MODULES_TTYONUART1
rcall TtyOnUart1_Periodically
bigcall TtyOnUart1_Periodically
#endif
#ifdef MODULES_COMONUART0
rcall ComOnUart0_Periodically
bigcall ComOnUart0_Periodically
#endif
#ifdef MODULES_COMONUART1
bigcall ComOnUart1_Periodically
#endif
#ifdef MODULES_COM2W
bigcall COM2W_Every100ms
#endif
#ifdef MODULES_COM2W0
bigcall COM2W0_Periodically
#endif
#ifdef MODULES_COM2W1
bigcall COM2W1_Periodically
#endif
#ifdef MODULES_TCRT1000
rcall TCRT1K_Every100ms
bigcall TCRT1K_Every100ms
#endif
#ifdef MODULES_BRIGHTNESS
bigcall Brightness_Every100ms
#endif
#ifdef APPS_NETWORK
ldi yl, LOW(netInterfaceData)
ldi yh, HIGH(netInterfaceData)
rcall AppNetwork_Every100ms
bigcall AppNetwork_Every100ms
#endif
#ifdef APPS_MOTION
rcall AppMotion_Every100ms
bigcall AppMotion_Every100ms
#endif
#ifdef APPS_DOOR
rcall AppDoor_Every100ms
bigcall AppDoor_Every100ms
#endif
#ifdef APPS_MA_LIGHT
rcall AppMotionLight_Every100ms
bigcall AppMotionLight_Every100ms
#endif
ret
@@ -138,32 +183,51 @@ onSystemTimerTick:
sysOnEverySecond:
#ifdef MODULES_DS18B20
rcall Ds18b20_OnEverySecond
bigcall Ds18b20_OnEverySecond
#endif
#ifdef APPS_REPORTSENSORS
rcall AppReportSensors_OnEverySecond
bigcall AppReportSensors_OnEverySecond
#endif
rjmp onEverySecond
#ifdef MODULES_SGP30
bigcall SGP30_EverySecond
#endif
bigjmp onEverySecond
; @end
sysOnEveryMinute:
#ifdef APPS_STATS
rcall AppStats_OnEveryMinute
bigcall AppStats_OnEveryMinute
#endif
rjmp onEveryMinute
#ifdef MODULES_CCS811
bigcall CCS811_OnEveryMinute
#endif
bigjmp onEveryMinute
; @end
sysOnEveryHour:
rjmp onEveryHour
bigjmp onEveryHour
; @end
sysOnEveryDay:
rjmp onEveryDay
#ifdef APPS_NETWORK
ldi yl, LOW(netInterfaceData)
ldi yh, HIGH(netInterfaceData)
bigcall AppNetwork_EveryDay
#endif
#ifdef APPS_ROUTER
bigcall AppRouter_EveryDay
#endif
bigjmp onEveryDay
; @end
#endif
@@ -175,17 +239,17 @@ sysOnEveryDay:
; @routine mainHandleMessages
mainHandleMessages:
rcall NET_GetNextIncomingMsgNum ; R16=msg num
bigcall NET_GetNextIncomingMsgNum ; R16=msg num
brcc mainHandleMessages_end
rcall NET_Buffer_Locate ; X=buffer addr (R17)
bigcall NET_Buffer_Locate ; X=buffer addr (R17)
adiw xh:xl, 1
push r16
rcall onMessageReceived
rcall mainModulesOnPacketReceived
rcall mainAppsOnPacketReceived
bigcall onMessageReceived
bigcall mainModulesOnPacketReceived
bigcall mainAppsOnPacketReceived
pop r16
rcall NET_Buffer_ReleaseByNum
bigcall NET_Buffer_ReleaseByNum
sec
mainHandleMessages_end:
ret

View File

@@ -22,22 +22,22 @@
; Call init functions of the used modules. Add your routine calls here.
initModules:
rcall BaseTimer_Init ; unconditionally call this
bigcall BaseTimer_Init ; unconditionally call this
#ifdef MODULES_HEAP
rcall Heap_Init
bigcall Heap_Init
#endif
#ifdef MODULES_CLOCK
rcall Clock_Init
bigcall Clock_Init
#endif
#ifdef MODULES_TIMER
rcall Timer_Init
bigcall Timer_Init
#endif
#ifdef MODULES_XRAM
rcall XRAM_Init
bigcall XRAM_Init
#endif
#ifdef MODULES_LED
@@ -45,107 +45,127 @@ initModules:
ldi zh, HIGH(ledA3Flash)
ldi yl, LOW(ledA3Sram)
ldi yh, HIGH(ledA3Sram)
rcall Led_Init
bigcall Led_Init
#endif
#ifdef MODULES_LED_SIMPLE
rcall LedSimple_Init
bigcall LedSimple_Init
#endif
#ifdef MODULES_COM
rcall Com2_Init ; init COM module
rcall CPRO_Init ; init COM protocol module
bigcall Com2_Init ; init COM module
bigcall CPRO_Init ; init COM protocol module
#endif
#ifdef MODULES_NETWORK
rcall NET_Init
bigcall NET_Init
#endif
#ifdef MODULES_UART_BITBANG
rcall UART_BitBang_Init
bigcall UART_BitBang_Init
#endif
#ifdef MODULES_UART_HW
rcall NET_Uart_Init
bigcall NET_Uart_Init
#endif
#ifdef MODULES_TTYONUART1
rcall TtyOnUart1_Init
bigcall TtyOnUart1_Init
#endif
#ifdef MODULES_COMONUART0
rcall ComOnUart0_Init
bigcall ComOnUart0_Init
#endif
#ifdef MODULES_COMONUART1
bigcall ComOnUart1_Init
#endif
#ifdef MODULES_COM2W
bigcall COM2W_Init
#endif
#ifdef MODULES_COM2W0
bigcall COM2W0_Init
#endif
#ifdef MODULES_COM2W1
bigcall COM2W1_Init
#endif
#ifdef MODULES_MOTION
rcall Motion_Init
bigcall Motion_Init
#endif
#ifdef MODULES_TWI_MASTER
rcall TWI_Master_Init
bigcall TWI_Master_Init
#endif
#ifdef MODULES_OWI_MASTER
rcall OwiMaster_Init
bigcall OwiMaster_Init
#endif
#ifdef MODULES_SPI_HW
rcall SPIHW_Init
bigcall SPIHW_Init
#endif
#ifdef MODULES_LCD
rcall LCD_Init
bigcall LCD_Init
#endif
#ifdef MODULES_BMP280
rcall BMP280_Init
bigcall BMP280_Init
#endif
#ifdef MODULES_SI7021
rcall SI7021_Init
bigcall SI7021_Init
#endif
#ifdef MODULES_SGP30
rcall SGP30_Init
bigcall SGP30_Init
#endif
#ifdef MODULES_SGP40
rcall SGP40_Init
bigcall SGP40_Init
#endif
#ifdef MODULES_DS18B20
rcall Ds18b20_Init
bigcall Ds18b20_Init
#endif
#ifdef MODULES_STATS
rcall Stats_Init
bigcall Stats_Init
#endif
#ifdef MODULES_CNY70
rcall CNY70_Init
bigcall CNY70_Init
#endif
#ifdef MODULES_REED
rcall REED_Init
bigcall REED_Init
#endif
#ifdef MODULES_SK6812
rcall SK6812_Init
bigcall SK6812_Init
#endif
#ifdef MODULES_MOTION_LIGHT
rcall MotionLight_Init
bigcall MotionLight_Init
#endif
#ifdef MODULES_TCRT1000
rcall TCRT1K_Init
bigcall TCRT1K_Init
#endif
#ifdef MODULES_CCS811
rcall CCS811_Init
bigcall CCS811_Init
#endif
#ifdef MODULES_ILI9341
rcall ILI9341_Init
bigcall ILI9341_Init
#endif
#ifdef MODULES_BRIGHTNESS
bigcall Brightness_Init
#endif
@@ -167,51 +187,83 @@ initModules:
; USED: depending on called routines
runModules:
rcall BaseTimer_Run
clr r16
push r16
bigcall BaseTimer_Run
pop r16
#ifdef MODULES_COM
; COM module (call until carry flag cleared but at most 10 times to not starve other modules)
ldi r16, 10
runModules_Com:
push r16
rcall Com2_Run
pop r16
brcc runModules_ComEnd
dec r16
brne runModules_Com
runModules_ComEnd:
#endif
#ifdef MODULES_TTYONUART1
rcall TtyOnUart1_Run
push r16
bigcall TtyOnUart1_Run
pop r16
#endif
#ifdef MODULES_COMONUART0
rcall ComOnUart0_Run
#endif
#ifdef MODULES_STATS
rcall Stats_Run
#endif
#ifdef MODULES_REED
rcall REED_Run
#endif
#ifdef MODULES_CNY70
rcall CNY70_Run
#endif
#ifdef MODULES_MOTION_LIGHT
; rcall MotionLight_Run
#endif
#ifdef MODULES_TCRT1000
; rcall TCRT1K_Run
#endif
push r16
rcall runComModules
pop r16
sbci r16, 0
; add more modules here
; check for repeat request
tst r16
clc
breq runModules_end
sec
runModules_end:
ret
; run until every module idle
runComModules:
clr r17
runComModules_loop:
push r17
clr r16
#ifdef MODULES_COMONUART0
push r16
bigcall ComOnUart0_Run
pop r16
sbci r16, 0
#endif
#ifdef MODULES_COMONUART1
push r16
bigcall ComOnUart1_Run
pop r16
sbci r16, 0
#endif
#ifdef MODULES_COM2W0
push r16
bigcall COM2W0_Run
pop r16
sbci r16, 0
#endif
#ifdef MODULES_COM2W1
push r16
bigcall COM2W1_Run
pop r16
sbci r16, 0
#endif
pop r17
; check for repeat request
tst r16
clc
breq runComModules_loopEnd
inc r17
cpi r17, 2
brne runComModules_loop
runComModules_loopEnd:
tst r17
clc
breq runComModules_end
sec
runComModules_end:
ret
@@ -220,12 +272,12 @@ runModules_ComEnd:
mainModulesOnPacketReceived:
#ifdef MODULES_SK6812
rcall SK6812_OnPacketReceived
bigcall SK6812_OnPacketReceived
#endif
#ifdef MODULES_LED_SIMPLE
#ifdef MODULES_NETWORK
rcall LedSimple_OnPacketReceived
bigcall LedSimple_OnPacketReceived
#endif
#endif

View File

@@ -23,6 +23,7 @@
; @param R19:R18 value
; @param R21:R20 denom (e.g. 100, meaning value must be divided by 100)
; @param R22 value type
; @return CFLAG on success, cleared on error
Main_SendValueReport:
push r17
@@ -52,6 +53,7 @@ Main_SendValueReport_end:
; @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
Main_SendValueResponse:
push r17

View File

@@ -1,11 +1,24 @@
<device name="aqua_c01" driver="nodes">
<manufacturer>AQUA</manufacturer>
<devicetype>N</devicetype>
<devicetype>C</devicetype>
<deviceversion>1</deviceversion>
<values>
<value name="LEDTIMING" id="0x88" type="actor" dataType="uint16" />
<value name="stats_packets_in" id="0xe0" type="sensor" dataType="uint16" denom="1" />
<value name="stats_packets_out" id="0xe1" type="sensor" dataType="uint16" denom="1" />
<value name="stats_content_errors" id="0xe2" type="sensor" dataType="uint16" denom="1" />
<value name="stats_io_errors" id="0xe3" type="sensor" dataType="uint16" denom="1" />
<value name="stats_nobuf_errors" id="0xe4" type="sensor" dataType="uint16" denom="1" />
<value name="stats_collision_errors" id="0xe5" type="sensor" dataType="uint16" denom="1" />
<value name="stats_busy_errors" id="0xe6" type="sensor" dataType="uint16" denom="1" />
<value name="stats_heap_used" id="0xe7" type="sensor" dataType="uint16" denom="1" />
<value name="stats_heap_free" id="0xe8" type="sensor" dataType="uint16" denom="1" />
<value name="stats_noram_errors" id="0xe9" type="sensor" dataType="uint16" denom="1" />
</values>
</device>

View File

@@ -13,8 +13,9 @@
.list
.include "../defs.asm"
.include "defs_all.asm"
.include "devices/all/defs.asm"
.include "common/calls.asm"
.include "common/utils_wait.asm"
.include "common/utils_io.asm"
@@ -115,16 +116,19 @@ main:
; ***************************************************************************
; includes
.include "common/utils_wait_fixed.asm"
.include "common/wait_10us.asm"
.include "common/utils_copy_from_flash.asm"
.include "common/utils_copy_sdram.asm"
.include "modules/flash/defs.asm"
.include "modules/flash/eeprom.asm"
.include "modules/flash/io.asm"
.include "modules/flash/io_attn.asm"
;.include "modules/flash/io_attn.asm"
.include "modules/flash/io_uart.asm"
.include "modules/flash/io_uart_all_attn.asm"
;.include "modules/flash/io_uart_all_attn.asm"
.include "modules/flash/io_uart_attn_small.asm"
.include "modules/flash/flash1pmega.asm"
.include "modules/flash/flashxp.asm"
.include "modules/flash/flashprocess.asm"

View File

@@ -67,6 +67,11 @@
.equ COM_BIT_LENGTH = 52000 ; 104000ns=9600, 52000ns=19200, 26000ns=38400
.equ COM_HALFBIT_LENGTH = 26000 ; see https://de.wikipedia.org/wiki/Universal_Asynchronous_Receiver_Transmitter
.equ COM_DATA_DDR = DDRD
.equ COM_DATA_INPUT = PIND
.equ COM_DATA_OUTPUT = PORTD
.equ COM_DATA_PIN = PORTD0
.equ COM_ATTN_DDR = DDRD
.equ COM_ATTN_INPUT = PIND
.equ COM_ATTN_OUTPUT = PORTD

View File

@@ -16,10 +16,12 @@
.include "include/m8515def.inc" ; Define device ATmega8515
.list
.include "version.asm"
.include "../defs.asm"
.include "./data.asm"
.include "devices/all/defs.asm"
.include "common/calls.asm"
.include "common/utils_wait.asm"
.include "common/utils_io.asm"
@@ -48,11 +50,6 @@
; ---------------------------------------------------------------------------
; firmware settings including list of modules used
.equ FIRMWARE_VERSION_MAJOR = 0
.equ FIRMWARE_VERSION_MINOR = 0
.equ FIRMWARE_VERSION_PATCHLEVEL = 1
; #define MODULES_TIMER
#define MODULES_CLOCK
#define MODULES_XRAM
@@ -60,7 +57,8 @@
#define MODULES_LED_SIMPLE
#define MODULES_NETWORK
;#define MODULES_COMONUART0
#define MODULES_UART_HW
;#define MODULES_UART_HW
#define MODULES_UART_BITBANG
#define MODULES_SPI_HW
#define MODULES_ILI9341
;#define MODULES_FONT_8X8
@@ -121,7 +119,8 @@
; ---------------------------------------------------------------------------
; Reset and interrupt vectors
rjmp BOOTLOADER_ADDR ; 1: Reset vector RESET
rjmp NetUart_AttnChangeIsr ; 2: INT0 External Interrupt Request 0
; rjmp NetUart_AttnChangeIsr ; 2: INT0 External Interrupt Request 0
rjmp UART_BitBang_PcintIsr ; 2: INT0 External Interrupt Request 0
reti ; 3: INT1 External Interrupt Request 1
reti ; 4: TIMER1_CAPT Timer/Counter1 Capture Event
reti ; 5: TIMER1_COMPA Timer/Counter1 Compare Match A
@@ -220,7 +219,8 @@ onEveryLoop:
; ---------------------------------------------------------------------------
; defines for network interface
.equ netInterfaceData = netUartIface
;.equ netInterfaceData = netUartIface
.equ netInterfaceData = uart_bitbang_iface

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