software/arduino/ubus: ubus code dump
This commit is contained in:
parent
ba3e9a19b4
commit
43952c14ab
155
protocol.md
Normal file
155
protocol.md
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
UBus
|
||||||
|
====
|
||||||
|
|
||||||
|
Minimal hdlc-over-half-duplex-rs485 bus protcol.
|
||||||
|
|
||||||
|
Bus
|
||||||
|
---
|
||||||
|
|
||||||
|
Bus is running half-duplex 115200bps UART over RS485 line.
|
||||||
|
|
||||||
|
Framing
|
||||||
|
-------
|
||||||
|
|
||||||
|
All frames transmitted over a bus are encoded using HDLC rules:
|
||||||
|
* Start byte (0x7e)
|
||||||
|
* Message payload
|
||||||
|
* 0x7d or 0x7e bytes in message payload are substituted with [0x7e, byte ^ 0x20]
|
||||||
|
* Last 2 bytes of message payload are CRC16 `CRC-CCITT` checksum
|
||||||
|
* NOTE: byte escaping applies here as well!
|
||||||
|
* NOTE: checksumming over all message bytes including the checksum should result in checksum 0... (this is a feature of pretty much all CRC algorithms, that, it seems, not many people are aware of)
|
||||||
|
* Message delimiter (end byte, 0x7e)
|
||||||
|
* Next message may follow immediately (but won't, see below...)
|
||||||
|
|
||||||
|
Addressing
|
||||||
|
----------
|
||||||
|
Bus supports 126 addresses
|
||||||
|
0x00 = bus master
|
||||||
|
0x01 - 0x3e = bus slave
|
||||||
|
|
||||||
|
Most significant bit indicates message direction. Slaves responsing to requests
|
||||||
|
need to set 0b10000000.
|
||||||
|
|
||||||
|
Messages
|
||||||
|
--------
|
||||||
|
|
||||||
|
### Master → slave
|
||||||
|
```
|
||||||
|
struct msg {
|
||||||
|
uint8_t target;
|
||||||
|
uint8_t request_type;
|
||||||
|
uint8_t request_payload[]; // depends on message type, may be ommitted
|
||||||
|
uint16_t checksum; // crc16 ccitt
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Slave → master
|
||||||
|
```
|
||||||
|
struct msg {
|
||||||
|
uint8_t source; // target ^ 0x80
|
||||||
|
uint8_t response_type;
|
||||||
|
uint8_t response_payload[]; // depends on message type, may be ommitted
|
||||||
|
uint16_t checksum; // crc16 ccitt
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Broadcast messages
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Broadcast messages (target 0xff) are handled TDMA-style. Receiving nodes get
|
||||||
|
assigned _broadcast_timeslot (20ms) each for their response. Every slave needs
|
||||||
|
to respond after (addr * _broadcast_timeslot) ms after final request delimiter
|
||||||
|
byte (0x7e). (TODO: this needs to be properly implemented in serial interrupt
|
||||||
|
by peeking at last byte received... This is impossible unless we patch standard
|
||||||
|
Arduino Serial implementation)
|
||||||
|
|
||||||
|
20ms is enough for approximately 200 bytes of effective application payload
|
||||||
|
response for every node.
|
||||||
|
|
||||||
|
Message type
|
||||||
|
------------
|
||||||
|
|
||||||
|
|
||||||
|
### 0x0x System Block
|
||||||
|
#### 0x00 - Status Poll
|
||||||
|
Basic bus status poll.
|
||||||
|
|
||||||
|
```
|
||||||
|
struct req { };
|
||||||
|
|
||||||
|
struct resp {
|
||||||
|
uint8_t status;
|
||||||
|
// 00h - ok
|
||||||
|
// 01h - ffh = error
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 0x01 - Identify
|
||||||
|
Light up status LED for 0.5s.
|
||||||
|
|
||||||
|
```
|
||||||
|
struct req { };
|
||||||
|
struct resp { };
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 0x02 - Query Supported Commands
|
||||||
|
```
|
||||||
|
struct req { };
|
||||||
|
struct resp {
|
||||||
|
uint8_t commands[]; // implemented message IDs
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### 0x1x IO Block
|
||||||
|
#### 0x10 - GPIO Write
|
||||||
|
```
|
||||||
|
struct req {
|
||||||
|
struct {
|
||||||
|
uint8_t io_num;
|
||||||
|
uint8_t value;
|
||||||
|
} pins[];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct resp {
|
||||||
|
uint8_t status;
|
||||||
|
// 00h = ok
|
||||||
|
// 01h - ffh error;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 0x11 - GPIO Read
|
||||||
|
```
|
||||||
|
struct req {
|
||||||
|
uint8_t pins[];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct resp {
|
||||||
|
uint8_t values[];
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 0x12 - WS2812 Write
|
||||||
|
```
|
||||||
|
struct req {
|
||||||
|
uint8_t pin;
|
||||||
|
uint8_t data; // packed RGB data, num_leds * 3
|
||||||
|
};
|
||||||
|
|
||||||
|
struct resp {
|
||||||
|
uint8_t status;
|
||||||
|
// 00h = ok
|
||||||
|
// 01h - ffh error;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### 0x2x Peripheral Block
|
||||||
|
#### 0x20 - NFC Read
|
||||||
|
Read NFC reader.
|
||||||
|
|
||||||
|
```
|
||||||
|
struct req { };
|
||||||
|
|
||||||
|
struct resp {
|
||||||
|
uint8_t uid[]; // empty if no card read
|
||||||
|
};
|
||||||
|
```
|
2
software/arduino/ubus/.gitignore
vendored
Normal file
2
software/arduino/ubus/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
.pioenvs
|
||||||
|
.piolibdeps
|
224
software/arduino/ubus/lib/UBus/UBus.cpp
Normal file
224
software/arduino/ubus/lib/UBus/UBus.cpp
Normal file
@ -0,0 +1,224 @@
|
|||||||
|
#include "UBus.h"
|
||||||
|
|
||||||
|
#include <PN532_HSU.h>
|
||||||
|
#include <PN532.h>
|
||||||
|
|
||||||
|
PN532_HSU pn532hsu(Serial1);
|
||||||
|
PN532 nfc(pn532hsu);
|
||||||
|
|
||||||
|
#include <FastLED.h>
|
||||||
|
#define NUM_LEDS 10
|
||||||
|
#define DATA_PIN 12
|
||||||
|
CRGB leds[NUM_LEDS];
|
||||||
|
|
||||||
|
void UBus::begin() {
|
||||||
|
RS485Class::begin(115200);
|
||||||
|
|
||||||
|
Serial.print("UBus starting up on address ");
|
||||||
|
Serial.println(addr);
|
||||||
|
|
||||||
|
if (addr >= 0x80) {
|
||||||
|
Serial.println("**** This device is unprovisioned! Disabling responder. ****");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Peripherals
|
||||||
|
FastLED.addLeds<WS2811, DATA_PIN, RGB>(leds, NUM_LEDS);
|
||||||
|
FastLED.show();
|
||||||
|
|
||||||
|
// PN532 UART needs a pullup
|
||||||
|
pinMode(11, INPUT_PULLUP);
|
||||||
|
pinMode(12, INPUT_PULLUP);
|
||||||
|
|
||||||
|
nfc.begin();
|
||||||
|
uint32_t versiondata = nfc.getFirmwareVersion();
|
||||||
|
|
||||||
|
// Got ok data, print it out!
|
||||||
|
Serial.print("Found chip PN5"); Serial.println((versiondata>>24) & 0xFF, HEX);
|
||||||
|
Serial.print("Firmware ver. "); Serial.print((versiondata>>16) & 0xFF, DEC);
|
||||||
|
Serial.print('.'); Serial.println((versiondata>>8) & 0xFF, DEC);
|
||||||
|
|
||||||
|
nfc.setPassiveActivationRetries(0xFF);
|
||||||
|
nfc.SAMConfig();
|
||||||
|
// Blink on bootup
|
||||||
|
identify_ts = 1;
|
||||||
|
|
||||||
|
receive();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UBus::poll() {
|
||||||
|
if (identify_ts != 0) {
|
||||||
|
if (identify_ts + 200 > millis()) {
|
||||||
|
digitalWrite(13, false);
|
||||||
|
} else {
|
||||||
|
identify_ts = 0;
|
||||||
|
digitalWrite(13, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while(available() > 0) parse(read());
|
||||||
|
|
||||||
|
uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; // Buffer to store the returned UID
|
||||||
|
uint8_t uidLength; // Length of the UID (4 or 7 bytes depending on ISO14443A card type)
|
||||||
|
|
||||||
|
// Wait for an ISO14443A type cards (Mifare, etc.). When one is found
|
||||||
|
// 'uid' will be populated with the UID, and uidLength will indicate
|
||||||
|
// if the uid is 4 bytes (Mifare Classic) or 7 bytes (Mifare Ultralight)
|
||||||
|
bool success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, &uid[0], &uidLength);
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
for (uint8_t i=0; i < uidLength; i++)
|
||||||
|
{
|
||||||
|
Serial.print(uid[i], HEX);
|
||||||
|
Serial.print(" ");
|
||||||
|
}
|
||||||
|
Serial.println("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encodes a single byte and writes it into am output buffer
|
||||||
|
void UBus::sendByte(uint8_t b) {
|
||||||
|
if (b == HDLC_START || b == HDLC_ESC) {
|
||||||
|
write(HDLC_ESC);
|
||||||
|
write(b ^ 0x20);
|
||||||
|
} else {
|
||||||
|
write(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Builds and sends a response message
|
||||||
|
void UBus::send(uint8_t msg_type, uint8_t* buf, size_t size) {
|
||||||
|
beginTransmission();
|
||||||
|
uint16_t crc = CRC16_CCITT_INIT_VAL;
|
||||||
|
write(HDLC_START);
|
||||||
|
|
||||||
|
sendByte(addr | 0x80); crc = _crc_ccitt_update(crc, addr | 0x80);
|
||||||
|
sendByte(msg_type); crc = _crc_ccitt_update(crc, msg_type);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < size; i++) {
|
||||||
|
sendByte(buf[i]); crc = _crc_ccitt_update(crc, buf[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
sendByte(crc & 0xff);
|
||||||
|
sendByte(crc >> 8);
|
||||||
|
write(HDLC_START);
|
||||||
|
endTransmission();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle message residing currently in the buffer
|
||||||
|
void UBus::handleRequest() {
|
||||||
|
uint8_t target = buffer[0];
|
||||||
|
uint8_t msg_type = buffer[1];
|
||||||
|
|
||||||
|
uint8_t* payload = buffer + 2;
|
||||||
|
uint8_t payload_len = buf_pos - 4;
|
||||||
|
uint8_t resp[16];
|
||||||
|
|
||||||
|
// Serial.println(msg_type);
|
||||||
|
|
||||||
|
switch (msg_type) {
|
||||||
|
case SYS_StatusPoll:
|
||||||
|
resp[0] = 0x00;
|
||||||
|
sendResponse(msg_type, resp, 1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SYS_Identify:
|
||||||
|
identify_ts = millis();
|
||||||
|
sendResponse(msg_type, resp, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SYS_SupportedCommands:
|
||||||
|
resp[0] = 0x00;
|
||||||
|
resp[1] = 0x01;
|
||||||
|
resp[2] = 0x02;
|
||||||
|
|
||||||
|
sendResponse(msg_type, resp, 3);
|
||||||
|
// SYS_StatusPoll, SYS_Identify, SYS_SupportedCommands,
|
||||||
|
// ...
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GPIO_Write:
|
||||||
|
for (uint8_t i = 0; i < payload_len / 2; i++) {
|
||||||
|
pinMode(payload[2 * i], OUTPUT);
|
||||||
|
digitalWrite(payload[2 * i], payload[2 * i + 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
resp[0] = 0x00;
|
||||||
|
sendResponse(msg_type, resp, 1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GPIO_Read:
|
||||||
|
for (uint8_t i = 0; i < payload_len; i++) {
|
||||||
|
pinMode(payload[i], INPUT_PULLUP);
|
||||||
|
resp[i] = digitalRead(payload[i]);
|
||||||
|
}
|
||||||
|
sendResponse(msg_type, resp, payload_len);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GPIO_WS2812:
|
||||||
|
// TODO memcpy?
|
||||||
|
for (uint8_t i = 0; i < payload_len / 3; i++) {
|
||||||
|
leds[i].r = payload[3*i];
|
||||||
|
leds[i].g = payload[3*i + 1];
|
||||||
|
leds[i].b = payload[3*i + 2];
|
||||||
|
}
|
||||||
|
FastLED.show();
|
||||||
|
sendResponse(msg_type, {}, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sends a response while also handling delayed response in case of broadcast
|
||||||
|
// messages
|
||||||
|
void UBus::sendResponse(uint8_t msg_type, uint8_t buf[], size_t size) {
|
||||||
|
if (buffer[0] == 0xff) {
|
||||||
|
uint32_t _broadcast_timeslot = 20;
|
||||||
|
uint32_t offset = 0; // millis() - _hdlc_start;
|
||||||
|
if (_broadcast_timeslot * addr > offset) {
|
||||||
|
delay(_broadcast_timeslot * addr - offset); // (millis() - _hdlc_start));
|
||||||
|
send(msg_type, buf, size);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
send(msg_type, buf, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UBus::parse(uint8_t c) {
|
||||||
|
if (c == HDLC_ESC) {
|
||||||
|
escape = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c == HDLC_START) {
|
||||||
|
escape = false;
|
||||||
|
// Serial.println(millis() - _hdlc_start);
|
||||||
|
// 1 byte address, 1 byte msg_type, 2 bytes checksum
|
||||||
|
if (buf_pos >= 4) {
|
||||||
|
if (checksum == 0) {
|
||||||
|
// frame finish - verified
|
||||||
|
// digitalWrite(13, !digitalRead(13));
|
||||||
|
if ((buffer[0] == 0xff || buffer[0] == addr) && addr != 0xff) {
|
||||||
|
handleRequest();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Serial.println("Invalid frame: ");
|
||||||
|
for (int i = 0; i < buf_pos; i++) { Serial.print(buffer[i], HEX); Serial.print(" "); }
|
||||||
|
Serial.println(checksum);
|
||||||
|
// frame invalid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf_pos = 0;
|
||||||
|
checksum = CRC16_CCITT_INIT_VAL;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buf_pos < HDLC_BUF_SIZE) {
|
||||||
|
// Serial.println(c);
|
||||||
|
buffer[buf_pos] = escape ? c ^ 0x20 : c;
|
||||||
|
checksum = _crc_ccitt_update(checksum, buffer[buf_pos]);
|
||||||
|
buf_pos += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
escape = false;
|
||||||
|
}
|
50
software/arduino/ubus/lib/UBus/UBus.h
Normal file
50
software/arduino/ubus/lib/UBus/UBus.h
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#ifndef UBUS_H
|
||||||
|
#define UBUS_H
|
||||||
|
|
||||||
|
#include <ArduinoRS485.h>
|
||||||
|
#include <util/crc16.h>
|
||||||
|
|
||||||
|
extern unsigned long _hdlc_start;
|
||||||
|
|
||||||
|
#define HDLC_START 0x7E
|
||||||
|
#define HDLC_ESC 0x7D
|
||||||
|
#define HDLC_BUF_SIZE 32
|
||||||
|
#define CRC16_CCITT_INIT_VAL 0xFFFF
|
||||||
|
|
||||||
|
class UBus: public RS485Class {
|
||||||
|
public:
|
||||||
|
enum MessageType : uint8_t {
|
||||||
|
SYS_StatusPoll = 0x00,
|
||||||
|
SYS_Identify = 0x01,
|
||||||
|
SYS_SupportedCommands = 0x02,
|
||||||
|
|
||||||
|
GPIO_Write = 0x10,
|
||||||
|
GPIO_Read = 0x11,
|
||||||
|
GPIO_WS2812 = 0x12,
|
||||||
|
|
||||||
|
PERI_NFC = 0x20,
|
||||||
|
};
|
||||||
|
|
||||||
|
uint8_t addr = 0;
|
||||||
|
uint32_t identify_ts = 0;
|
||||||
|
|
||||||
|
bool escape = false;
|
||||||
|
|
||||||
|
uint8_t buf_pos = 0;
|
||||||
|
uint8_t buffer[HDLC_BUF_SIZE];
|
||||||
|
uint16_t checksum = CRC16_CCITT_INIT_VAL;
|
||||||
|
|
||||||
|
UBus(): RS485Class(Serial, 1, 3, 2) { }
|
||||||
|
|
||||||
|
void begin();
|
||||||
|
|
||||||
|
void sendByte(uint8_t b);
|
||||||
|
void send(uint8_t msg_type, uint8_t* buf, size_t size);
|
||||||
|
void sendResponse(uint8_t msg_type, uint8_t* buf, size_t size);
|
||||||
|
|
||||||
|
void handleRequest();
|
||||||
|
|
||||||
|
void poll();
|
||||||
|
void parse(uint8_t c);
|
||||||
|
};
|
||||||
|
#endif
|
25
software/arduino/ubus/platformio.ini
Normal file
25
software/arduino/ubus/platformio.ini
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
; PlatformIO Project Configuration File
|
||||||
|
;
|
||||||
|
; Build options: build flags, source filter
|
||||||
|
; Upload options: custom upload port, speed and extra flags
|
||||||
|
; Library options: dependencies, extra library storages
|
||||||
|
; Advanced options: extra scripting
|
||||||
|
;
|
||||||
|
; Please visit documentation for the other options and examples
|
||||||
|
; https://docs.platformio.org/page/projectconf.html
|
||||||
|
|
||||||
|
[env:uno]
|
||||||
|
platform = atmelavr
|
||||||
|
board = atmega328pb
|
||||||
|
build_flags = -D SERIAL_PORT_HARDWARE=Serial
|
||||||
|
framework = arduino
|
||||||
|
upload_flags = -F
|
||||||
|
monitor_port = /dev/serial/by-id/usb-FTDI_TTL232R_FTF48YOZ-if00-port0
|
||||||
|
upload_port = /dev/serial/by-id/usb-FTDI_TTL232R_FTF48YOZ-if00-port0
|
||||||
|
targets = upload, monitor
|
||||||
|
monitor_speed = 115200
|
||||||
|
lib_deps =
|
||||||
|
Wire
|
||||||
|
arduino-libraries/ArduinoRS485@^1.0.0
|
||||||
|
fastled/FastLED@^3.4.0
|
||||||
|
https://github.com/yoshitake-hamano/PN532
|
42
software/arduino/ubus/src/main.ino
Normal file
42
software/arduino/ubus/src/main.ino
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
#include <EEPROM.h>
|
||||||
|
#include <UBus.h>
|
||||||
|
|
||||||
|
const int RS485_RE = 2;
|
||||||
|
const int RS485_DE = 3;
|
||||||
|
|
||||||
|
UBus bus;
|
||||||
|
|
||||||
|
void provision() {
|
||||||
|
long start_ts = millis();
|
||||||
|
|
||||||
|
Serial.begin(115200);
|
||||||
|
Serial.println("This device is unprovisioned!");
|
||||||
|
|
||||||
|
while (start_ts + 10000 > millis()) {
|
||||||
|
Serial.print("Address: ");
|
||||||
|
long addr = Serial.parseInt(SKIP_WHITESPACE);
|
||||||
|
if (addr < 0x01 || addr > 0x80) {
|
||||||
|
Serial.println("Invalid address...");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
EEPROM.write(0x00, addr);
|
||||||
|
bus.addr = addr;
|
||||||
|
Serial.println("Finished.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
pinMode(13, OUTPUT);
|
||||||
|
bus.addr = EEPROM.read(0);
|
||||||
|
|
||||||
|
if (bus.addr == 0xff) {
|
||||||
|
provision();
|
||||||
|
}
|
||||||
|
bus.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
bus.poll();
|
||||||
|
}
|
132
software/raspi/ubus.py
Normal file
132
software/raspi/ubus.py
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
from serial import Serial
|
||||||
|
# from PyCRC.CRCCCITT import CRCCCITT
|
||||||
|
import time
|
||||||
|
import struct
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def lo8(b): return b & 0xff
|
||||||
|
def hi8(b): return (b >> 8) & 0xff
|
||||||
|
|
||||||
|
def crc16(buf):
|
||||||
|
'''
|
||||||
|
uint16_t
|
||||||
|
crc_ccitt_update (uint16_t crc, uint8_t data)
|
||||||
|
{
|
||||||
|
data ^= lo8 (crc);
|
||||||
|
data ^= data << 4;
|
||||||
|
|
||||||
|
return ((((uint16_t)data << 8) | hi8 (crc)) ^ (uint8_t)(data >> 4)
|
||||||
|
^ ((uint16_t)data << 3));
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
|
||||||
|
crc = 0xffff
|
||||||
|
for data in buf:
|
||||||
|
data ^= (crc) & 0xff
|
||||||
|
data ^= (data << 4) & 0xff
|
||||||
|
crc = (((data << 8) | (crc >> 8) & 0xff) ^ (data >> 4) ^ (data << 3)) & 0xffff
|
||||||
|
|
||||||
|
return crc
|
||||||
|
|
||||||
|
HDLC_START = b'\x7e'
|
||||||
|
HDLC_ESC = b'\x7d'
|
||||||
|
|
||||||
|
class UBus(Serial):
|
||||||
|
def _send(self, bar):
|
||||||
|
self.write(HDLC_START)
|
||||||
|
checksum = crc16(bar)
|
||||||
|
# CRCCCITT("FFFF").calculate(bar)
|
||||||
|
for b in bar + struct.pack('<H', checksum):
|
||||||
|
if bytes([b]) in [HDLC_START, HDLC_ESC]:
|
||||||
|
self.write(HDLC_ESC)
|
||||||
|
self.write(bytes([b ^ 0x20]))
|
||||||
|
else:
|
||||||
|
self.write(bytes([b]))
|
||||||
|
self.write(HDLC_START)
|
||||||
|
self.flush()
|
||||||
|
|
||||||
|
def request(self, target, msg_type, payload=bytearray(), retry=0):
|
||||||
|
for n in range(retry + 1):
|
||||||
|
try:
|
||||||
|
self._send(bytearray([target, msg_type]) + bytearray(payload))
|
||||||
|
return self.read_message()[2:]
|
||||||
|
except TimeoutError as exc:
|
||||||
|
if n == retry:
|
||||||
|
raise exc
|
||||||
|
|
||||||
|
def request_broadcast(self, msg_type, payload=bytearray()):
|
||||||
|
self._send(bytearray([0xff, msg_type]) + bytearray(payload))
|
||||||
|
|
||||||
|
responses = []
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
yield self.read_message()
|
||||||
|
except TimeoutError:
|
||||||
|
return
|
||||||
|
|
||||||
|
buf = bytearray()
|
||||||
|
escape = False
|
||||||
|
|
||||||
|
def read_message(self):
|
||||||
|
res = None
|
||||||
|
|
||||||
|
while res is None:
|
||||||
|
res = self._read()
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
def _read(self):
|
||||||
|
b = self.read(1)
|
||||||
|
if not b:
|
||||||
|
raise TimeoutError
|
||||||
|
|
||||||
|
if b == HDLC_ESC:
|
||||||
|
self.escape = True
|
||||||
|
return None
|
||||||
|
|
||||||
|
if b == HDLC_START:
|
||||||
|
res = None
|
||||||
|
if len(self.buf) >= 2:
|
||||||
|
if crc16(self.buf) == 0:
|
||||||
|
res = self.buf[:-2]
|
||||||
|
else:
|
||||||
|
print('INVALID CRC:', self.buf, crc16(self.buf))
|
||||||
|
|
||||||
|
self.escape = False
|
||||||
|
self.buf = bytearray()
|
||||||
|
return res
|
||||||
|
|
||||||
|
self.buf.append(ord(b) ^ 0x20 if self.escape else ord(b))
|
||||||
|
self.escape = False
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# bus = UBus('/dev/serial/by-id/usb-1a86_USB2.0-Serial-if00-port0', 115200)
|
||||||
|
bus = UBus('/dev/serial/by-id/usb-1a86_USB2.0-Ser_-if00-port0', 115200, timeout=0.1)
|
||||||
|
# bus = UBus('/dev/serial/by-id/usb-FTDI_TTL232R_FTF48YOZ-if00-port0', 115200)
|
||||||
|
while True:
|
||||||
|
#try:
|
||||||
|
# print(bus.request(0x01,0x01))
|
||||||
|
#except Exception as exc:
|
||||||
|
# print(repr(exc))
|
||||||
|
#time.sleep(1.0)
|
||||||
|
#for m in list(bus.request_broadcast(0x00)):
|
||||||
|
# print(m)
|
||||||
|
#print(list(bus.request_broadcast(0x00))) # , bytearray([13]))))
|
||||||
|
#print(list(bus.request_broadcast(0x11, bytearray([13]))))
|
||||||
|
try:
|
||||||
|
#print(list(bus.request_broadcast(0x00))) # , bytearray([13]))))
|
||||||
|
resp = bus.request(0x04, 0x11, [9, 10,11,12])
|
||||||
|
print(resp)
|
||||||
|
colors = sum([[255, 0, 0] if b else [0, 0, 0] for b in resp], [])
|
||||||
|
print(colors)
|
||||||
|
bus.request(0x02, 0x12, colors) # [32] * 24, 3) # [0,0,0,0,0,0,0,0,0,255,0,255,255,255,0,0,255,255])
|
||||||
|
except TimeoutError as exc:
|
||||||
|
print(repr(exc))
|
||||||
|
#print('bep')
|
||||||
|
#bus._send(bytearray([0xff, 0x00]))
|
||||||
|
#time.sleep(0.1)
|
||||||
|
#while bus.inWaiting():
|
||||||
|
# frame = bus._read()
|
||||||
|
# if frame:
|
||||||
|
# print(frame)
|
Loading…
x
Reference in New Issue
Block a user