software/arduino/ubus: ubus code dump

This commit is contained in:
2022-06-17 20:01:36 +02:00
parent ba3e9a19b4
commit 43952c14ab
7 changed files with 630 additions and 0 deletions

2
software/arduino/ubus/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
.pioenvs
.piolibdeps

View 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;
}

View 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

View 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

View 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
View 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)