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)