MODULE_INFO
- descr:
- one MODULE_INFO for every module
- can share code (e.g. use multiple MODULE_INFOs for different pins in PinOut module)
- def:
- name[16]
- id
- handlerFunctionAddr()
- code:
.equ MODULE_INFO_HANDLER_LO = 0
.equ MODULE_INFO_HANDLER_HI = 1
.equ MODULE_INFO_ID = 2
.equ MODULE_INFO_NAME = 4
.equ MODULE_INFO_SIZE = 12
SIGNAL_TABLE:
- descr:
- one table for every signal a module can emit
- def:
- array of SLOT objects (end of array: 0x0000)
SLOT:
- descr:
- one SLOT for every module which is waiting for this signal
- def:
- MODULE_INFO *modInfoPtr (16 bit)
- signalIdForCaller (8 bit)
- signalIdForCallee (8 bit)
Initial module:
- OS
- sent signals:
- init
- fini
- receivable signals:
- sleep
- hardReset
- softReset
Other modules:
- Timer
- sent signals:
- 1Hz
- 10Hz
- 100Hz
- PinOut
- receivable signals:
- setValue
- setOnDuration
- setOffDuration
- sent signals:
- none
Module_EmitSignal:
- params:
- senderId (8 bit) R16
- signalIdForSender (8 bit, unique in the realm of the sender) R17
- slotIdForRecipient (8 bit, unique in the realm of the recipient) R18
- paramLo (8 bit) R26 (YL)
- paramHi (8 bit) R27 (YH)
- recipientModule R30 (ZL), R31 (ZH) (16 bit)
Notes:
- R24 und R25: 16 register (supports ADIW and SBIW)
- for RAM access: R26-R31 (X, Y, Z)
- for flash memory access: R0 and Z (R30/31)
- in INT: use R15 for flags
- for everything else: R1-R14
- LDI only for upper 16 regs
Generic signals from OS module:
- init
- fini
- request (REQUEST flag for a module can be set by an interrupt handler to request that the corresponding
module be called as soon as the event handler loop is reached)
irq handler:
- for every port:
- is port change mask 0? -> SET new mask, otherwise OR new mask with old mask
-> gracefully handling overrun (e.g. a new PIN change before the previous one was handled)
req handler:
- for every port:
- is port change mask 0? -> emit aPortChanged(currentPortValue, portChangeMask), clear mask
File format:
IRQ-Table
.include "os/rqhandlers.inc"
.include "timer/irqhandlers.inc"
.include "toggleport/irqhandlers.inc"
.equ MODULE_ID_OS = 0
.equ MODULE_ID_TIMER = 1
.equ MODULE_ID_TOGGLEPORT = 2
.equ TIMER_USE_100Hz = 1
.equ TIMER_USE_10Hz = 1
.equ TIMER_USE_10z = 1
; in file timer/modinfo.inc
; timer module
modInfoTimer:
.db "TIMER ", 0 ; name
.db MODULE_ID_TIMER, 0 ; id, reserved byte (0)
.dw timerSignalHandler ; handler
.equ TIMER_SIG_1HZ = 1
.equ TIMER_SLOT_INIT = 0
.equ TIMER_SLOT_FINI = 1
; in file toggleport/module.inc
; toggleport module
.equ TOGGLEPORT_SLOT_OS_INIT = 0
.equ TOGGLEPORT_SLOT_OS_FINI = 1
.equ TOGGLEPORT_SLOT_TIMER_1HZ = 2
; in main source file
; signal table for timer
timerSignalTable:
.db TIMER_SIG_1HZ, TOGGLEPORT_SLOT_TIMER_1HZ, LOW(modInfoTogglePort), HIGH(modInfoTogglePort)
.db 0, 0, 0, 0 ; last entry
Assembler:
- generate automatic list of modified registers
.object test1 {
.sram {
Testdata:
.bytes 16
}
.eprom {
}
.code {
}
}
function test1 {
ld r16, 10
l1:
dec r16
brne l1
ret
}
Hardware:
AtTiny 84
=========
----------
VCC + 1 14 + GND
(PCINT8, XTAL1, CLKI) PB0 + 2 13 + PA0 (ADC0, AREF, PCINT0) IN/OUT
(PCINT9, XTAL2) PB1 + 3 12 + PA1 (ADC1, AIN0, PCINT1) WAN-DATA
(PCINT11, RESET, dW) PB3 + 4 11 + PA2 (ADC2, AIN1, PCINT2) IN/OUT [LED (red)]
KEY->IRQ_PCI1 (PCINT10, INT0, OC0A, CKOUT) PB2 + 5 10 + PA3 (ADC3, T0, PCINT3) LED (green)
WAN-ATTN(PCI0)(PCINT7, ICP1, OC0B, ADC7) PA7 + 6 9 + PA4 (ADC4, USCK, SCL, T1, PCINT4) I2C, SPI
I2C, SPI (PCINT6, OC1A, SDA, MOSI, DI, ADC6) PA6 + 7 8 + PA5 (ADC5, DO, MISO, OC1B, PCINT5) SPI
----------
AtTiny 85
=========
----------
(PCINT5, /RESET, ADC0, dW) PB5 + 1 8 + VCC
WAN-ATTN (PCINT3, XTAL1, CLKI, /OC1B, ADC3) PB3 + 2 7 + PB2 (SCK, USCK, SCL, ADC1, T0, INT0, PCINT2)
WAN-DATA (PCINT4, XTAL2, CLKO, OC1B, ADC2) PB4 + 3 6 + PB1 (MISO, DO, AIN1, OC0B, OC1A, PCINT1)
GND + 4 5 + PB0 (MOSI, DI, SDA, AIN0, OC0A, /OC1A, AREF, PCINT0)
----------
ADC = ADC Input Channel
AREF = External Analog Reference
PCINT = Pin Change Interrupt
AIN = Analog Comparator Input (0=positive, 1=negative input)
T0 = Timer/Counter0 Clock Source
T1 = Timer/Counter1 Clock Source
USCK = USI Clock (Three Wire Mode)
SCL = USI Clock (I2C Mode=
DO = USI Data Output (Three Wire Mode)
MISO = SPI Master Data Input / Slave Data Output (Three Wire Mode)
OC0A = Timer/Counter0 Compare Match A Output
OC0B = Timer/Counter0 Compare Match B Output
OC1A = Timer/Counter1 Compare Match A Output
OC1B = Timer/Counter1 Compare Match B Output
SDA = USI Data Input (I2C Mode)
MOSI = SPI Data Master Output/Slave Data Input (Three Wire Mode)
ICP1 = Timer/Counter1 Input Capture Pin
WAN-ATTN -> irq PCI0
KEY -> irq PCI1
PA0 -> in/out1
PA2 -> in/out2
PA3 -> LED
PA4-6: I2C, SPI
WAN-Communication:
WAN-Protocol (UART 9600bps 8N1)
104us pro bit bei 9600bps, about 1040us per byte, about 8.3ms per 8 bytes
52us 104us 104us 104us 104us 104us 104us 104us 104us 104us 104us
INACTIVE START 0 1 2 3 4 5 6 7 STOP INACTIVE
- DATA ~11111111 00000000 00000000 00000000 11111111 00000000 11111111 00000000 11111111 00000000 11111111 11111111 11~
- /ACTIVE ~11100000000000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000 11111111 11~
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX (repeat for every byte)
Sensors:
- Erd-Feuchtigkeitssensor:
https://www.mouser.de/ProductDetail/M5Stack/U019?qs=sGAEpiMZZMu2rInN%2FZRmFkkq%2FqtywxsmOLlgBBfclSVLB6YzCank4Q%3D%3D
- rename WAN -> inc (inter-node-communication)
- INC messages:
- 1 byte : dest address [not saved]
- 1 byte : msg length (not including dest address, msg length and xor byte) [not saved]
- n bytes: data
- 1 byte : XOR checksum [not saved]
Packet in buffer:
- 1 byte: buffer length
- 1 byte: flags
- 1 byte: dest address
- 1 byte: msg data
- receivePacket:
- recv dest address
- matches?
- no: finished
- recv msg size
- too big?
- yes: ABORT
- alloc buffer (msg size +1)
- error?
- yes: ABORT
- write to buffer: size+0x80 (0x80: READ)
- for every msg size byte: receive byte
- receive XOR byte
- check XOR checksum
- 0?
- no: ABORT, DeallocBufferBack
Infrastructure, what's next
===========================
- build PC module to read and write control messages
- make WAN code in AVR stable
- implement command to write debug messages to PC via WAN
- node
- unit (e.g. environment, presence, door, power) [defines per unit type, use uint8_t values]
- value (e.g. temp, humidity, pressure, light)
- addresses:
- 8-bits total, but:
- bit 7=0: bit 0-6 contains a node address (i.e. max 127 node devices)
- bit 7=1, bit 6=0: bits 0-5 contain id of a group:
- nodes with window/door status
- nodes with alarm output (e.g. horn, bell or or sms)
- nodes with environmental readouts (temp, humidity etc.)
- 240+: special ids
- 252: timekeeper node
- 253: id of database node
- 254: id of a router node to upper layers (e.g. room -> appartement -> floor -> house)
- 255: broadcast (every node)
- 0: only unassigned nodes (used when assigning an address)
- nodes:
- database node
- contains a list of known nodes and their modules and addresses on a bus
- router node
- forwards upwards in the hierarchy (e.g. from room to appartement level)
- commands:
- ping
- announce device
- used to assign a 8-bit device id on a bus
- needs a 8-byte serial number for the device
- assign device id
- response from a database node to "announce device"
- announce module
- make information about a module on a node available to a central node (or other interested nodes)
- announce value
- make info about data available
- value data
- announce changes in a given module
- RQ: set value
- allow to change a value
- RSP: set value (response to set value request)
- request value info
- make the node send the current value of a given module
- time (date, time, day/night flag)
Bauteile fuer Platine:
- Widerstaende:
- 1K fuer LEDs
- 1K fuer Pull-up (UART lines)
- 100 Ohm serial inline (UART lines)
- 4K7/10K fuer Pull-up (I2C lines)
- Kondensatoren:
- 10 microF (fuer Spannungsregler)
- 100nF (Abblockkondensatoren fuer Chips)
- Chips:
- HT7333 (3V3 Spannungsregler
- 74LS21: Dual 4-Port AND Gate (OpenCollector Outputs)
- 74LS03, 74LS04: Interface for usbserial (OpenCollector Outputs)
Memory Layout:
- 0x0000-0x003f: Reset and IRQ vectors
- 0x0020-0x04ff: base system (1248 words, starting with irq table)
- 0x0500-end : main system starting with IRQ table (2816 words)
Interrupt-Vektoren:
- bei aktivem base system:
- rjmp PC+0x020 (0xc01f)
- bei aktivem main system:
- rjmp PC+0x500 (0xc4ff)
; with temp1
; AtTiny84
; --------
; VCC 1 14 GND
; PB0 2 13 PA0
; PB1 3 12 PA1 COM-DATA
; /RESET PB3 4 11 PA2
; KEY1 PB2 5 10 PA3 LED
; COM_ATTN PA7 6 9 PA4 TWI-SCL
; TWI-SDA PA6 7 8 PA5
; --------
; with temp2
; AtTiny84
; --------
; VCC 1 14 GND
; PB0 2 13 PA0
; PB1 3 12 PA1 COM-DATA
; /RESET PB3 4 11 PA2 OWI
; KEY1 PB2 5 10 PA3 LED
; COM_ATTN PA7 6 9 PA4
; PA6 7 8 PA5
; --------
; with pwm
; AtTiny84
; --------
; VCC 1 14 GND
; PB0 2 13 PA0
; FAN1-IN PB1 3 12 PA1 COM-DATA
; /RESET PB3 4 11 PA2 OWI
; KEY1 PB2 5 10 PA3 LED
; COM_ATTN PA7 6 9 PA4 FAN2-IN
; PWM1-OUT PA6 7 8 PA5 PWM2-OUT
; --------
; with optic door/window sensors
; AtTiny84
; --------
; VCC 1 14 GND
; PB0 2 13 PA0 DOOR-ADC1
; DOOR-ADC2 PB1 3 12 PA1 COM-DATA
; /RESET PB3 4 11 PA2
; KEY1 PB2 5 10 PA3 LED
; COM_ATTN PA7 6 9 PA4 TWI-SCL
; TWI-SDA PA6 7 8 PA5
; --------
; generic node 5
; AtTiny84
; --------
; VCC 1 14 GND
; PB0 2 13 PA0
; DOOR-ADC1 PB1 3 12 PA1 COM-DATA
; /RESET PB3 4 11 PA2
; PB2 5 10 PA3 LED
; COM_ATTN PA7 6 9 PA4 TWI-SCL
; TWI-SDA PA6 7 8 PA5 DOOR-ADC0 (CNY70 Collector)
; --------
; AtTiny85
; --------
; /RESET PB5 1 8 VCC
; COM-ATTN PB3 2 7 PB2 TWI-SCL
; COM-DATA PB4 3 6 PB1 LED
; GND 4 5 PB0 TWI-SDA
; --------
format of an intel hex file:
:02 0000 02 0000 FC
| | | | |
| | | | checksum = -(len + addrlow+addrHigh+every_data_byte)
| addr | data
length record type (02=extended segment address record)
:10 0000 00 34 C8 18 95 7E C1 18 95 18 95 18 95 18 95 18 95 A7
:10 0100 00 02 E0 09 BF 08 95 08 95 FF B6 F8 94 00 27 10 91 02
see https://developer.arm.com/documentation/ka003292/latest
record types:
00 data record;
01 End of file record. Usually, it is 00000001FF
02 Extended Segment address record. This indicates segment base address when 16 bits is not enough for addressing memory;
[ 03 Start segment address record. Indicates initial segment base address.]
04 Extended Linear Address Record allows 32 bit addressing.
05 Start Linear Address Record.
for record type 2:
- should be 0 for attiny
- real address is segment address from record type 2 left-shifted by 4 bits + address in data segments, e.g.:
adress from data record: 2462
type 2 address: 1200 <- shift left by 4
resulting address: 00014462
for type 4 (start address of appication):
- data part contains full 32-bit address
int writehex( FILE *fp, ulong adr, uchar *data, int len )
{
if( len ){
int pruef;
fprintf( fp, ":%02X%04lX00", len, adr );
pruef = len + (adr & 0xFF) + (adr >> 8);
for(; len--; data++){
fprintf( fp, "%02X", *data );
pruef += *data;
}
fprintf( fp, "%02X\r\n", -pruef & 0xFF );
return 1;
}
fputs( ":00000001FF\r\n", fp );
return 1;
}
Das Attiny-Projekt O Der Bootloader 4
ldi temp, LOW(RAMEND) ; Stackpointer auf RAMEND setzen
out SPL, temp
GuckObStarten:
sbis pinD, DTR ; DTR = 0 bzw. Taster Ta1 geschlossen -> BOOTLOADER,
; sonst Anwender-Programm
rjmp Start
rjmp $079F ; Sprung zum Anwender-Programm
Start:
sbi ddrd, LED ; für Anzeige LED
sbi portD, LED ; Anzeige: LED an D.5 an, wenn Bootloading
sbis pinD, DTR ; warten bis DTR = 1 bzw. Ta1 offen
rjmp Start
; UART initialisieren:
sbi UCSRB, 4 ; UCR=UCSRB=0x0B RXEN=Bit4 RX aktivieren
sbi UCSRB, 3 ; UCR=UCSRB=0x0B UDRE=Bit3 TX aktiv
ldi uartparam, 4000000/(9600*16)-1 ;Baudrate 9600 einstellen
out UBRRL, uartparam
rcall rdcom ; warten auf Startbyte
cbi portd, LED ; LED an D.5 aus, wenn Startbyte erhalten
ldi param, 105 ; ACK senden
rcall wrcom
MainSchlaufe:
rcall rdcom
mov command, param
B201: ; Block (Page) schreiben
cpi command, 201
brne B203
rcall Schreib_Block
ldi param, 1 ; ACK Block_Schreiben
rcall wrcom
B203: ; Anwender-Programm starten
cpi command, 203
brne zurueck ; Wenn keine Zahl zutrifft zum Anfang
ldi param, 11 ; ACK Ende des Uploads
rcall wrcom
rcall warte1ms ; 1 ms warten
cbi UCSRB, 4 ; RX deaktivieren
cbi UCSRB, 3 ; TX deaktivieren
rjmp $03AF
zurueck:
rjmp MainSchlaufe
Schreib_Block:
ldi r23, PageSize4313
rcall rdcom ; Block-Adresse vom Master
mov ZH, r16
rcall rdcom
mov ZL, r16
Loeschen: ; Block loeschen
ldi r22, 3
out spmcsr, r22
spm
Puffer_Schreib_Schleife: ; alle Woerter des Blocks einzeln in den Puffer
; r1:r0 uebertragen
rcall rdcom
mov r0, r16
rcall rdcom
mov r1, r16
ldi r22, 1 ; Puffer schreiben
out spmcsr, r22
spm
adiw ZL, 2 ; Flash-Adresse um 1 Word erhoehen
mov r16, r0 ; ACK fuer die Uebernahme erhoehen
rcall wrcom
mov r16, r1
rcall wrcom
dec r23
brne Puffer_Schreib_Schleife ; Ende der Puffer_Schreib_Schleife
subi ZL, PageSize4313 ; Startwert fuer Page in Z-Register restaurieren, um...
subi ZL, PageSize4313
ldi r22, 5 ; ... gesamten Puffer in FLASH schreiben
out spmcsr, r22
spm
ret
; temp1, temp2, looplo, loophi, spmcrval
; in: Z=address in FLASH (byte address like for LPM!)
; Y=address in RAM
.equ PAGESIZEB = PAGESIZE*2 ; PAGESIZEB is page size in BYTES, not words
write_page:
; page erase
ldi r20, (1<6 bytes overhead
-> 10 usable bytes left
flash data msg:
- 2 bytes msg num
- remaining data bytes: 8
Part list N03:
- Resistors
- R1: 100 Ohm
- R2: 100 Ohm
- R3: 10K Ohm
- R4: 1K Ohm
- R5: 10K Ohm
- R6: 10K Ohm
- R7: 120 Ohm (restrict current to 27mA at 3.3V)
- R8: 220K Ohm
- Capacitors
- C1: 10 uF
- C2: 10 uF
- C3: 100 nF
Part list N04, N05, N06:
- Resistors
- R1: 100 Ohm serial COM_ATTN
- R2: 100 Ohm serial COM_DATA
- R3: 10K Ohm pullup reset
- R4: 1K Ohm serial LED
- R5: 10K Ohm pullup SDA
- R6: 10K Ohm pullup SCL
- R7: 120 Ohm (restrict CNY70 LED current to 27mA at 3.3V)
- R8: 220K Ohm pullup CNY70 photo transistor
- Capacitors
- C1: 10 uF
- C2: 10 uF
- C3: 100 nF
Node header:
- baseFirmwareVersion (2 bytes)
- mainFirmwareVersion (2 bytes)
- modules mask: (2 bytes)
Registered firmware modules:
- 0x01: LED
- 0x02: SI7021
- 0x03: CNY70
- etc.
Next:
- aqhome:
- add IPC client
- add IPC command to set accepted msg groups
- add IPC command to query nodeinfo database
- add IPC command for sending value change
- node (UID, addr?)
- value id
- value type
- new value
- avr:
- add firmware header
- add msg which sends that header
- value msg: send uid instead of timestamp
- maybe use fletcher16? (https://en.wikipedia.org/wiki/Fletcher%27s_checksum)
- or crc8 (https://stackoverflow.com/questions/51752284/how-to-calculate-crc8-in-c)? [use this!]
3 4 2 1
1 2 3 4 1: GND x
x x x 2: ATTN
x 3: DATA x
4: 5V
Gehaeuse:
- Abstandshalter in Gehaeuse?
- Platine verkehrt herum einbauen, einschrauben (Abstandshuelsen?)
- oder: LED und Schalter auf der Rueckseite einbauen, Platine mit Abstandshuelsen einschrauben
- Deckel an der Wand befestigen
- Kabel innerhalb der Dose verlegen (sauberere Anschluesse)
Kabel:
- ROT : 5V
- SCHWARZ: GND
- GRUEN : DATA
- BLAU : ATTN
Tasmota-Steckdosen melden alle 300s:
tele/tasmota/plug01/STATE
{
"Time":"1970-01-01T09:01:09",
"Uptime":"0T09:00:41",
"UptimeSec":32441,
"Heap":29,
"SleepMode":"Dynamic",
"Sleep":50,
"LoadAvg":19,
"MqttCount":1,
"POWER":"ON",
"Wifi":{
"AP":1,
"SSId":"CAMELOT_IOT",
"BSSId":"74:DA:38:EF:8B:DC",
"Channel":4,
"Mode":"11n",
"RSSI":98,
"Signal":-51,
"LinkCount":1,
"Downtime":"0T00:00:06"
}
}
tele/tasmota/plug01/SENSOR
{
"Time":"1970-01-01T09:01:09",
"ENERGY": {
"TotalStartTime":"1970-01-01T00:00:00",
"Total":0.131,
"Yesterday":0.000,
"Today":0.131,
"Period": 4,
"Power":49,
"ApparentPower":55,
"ReactivePower":25,
"Factor":0.89,
"Voltage":232,
"Current":0.235
}
}
node 2: no messages received again???
rewrite COM:
- send message:
- alloc buffer
- message vorbereiten lassen
- send message
- dealloc buffer
- wenn fehler (z.B. line busy): spaeter alles wiederholen (dann aber durch das anfordernde modul, nicht das COM modul)
- kein fire and forget mehr!
- evtl. einen buffer fuer notfall-meldungen
- reihenfolge aendern bei comInterrupt (counter hinterher erhoehen)