3
0
Fork 0

Added project

This commit is contained in:
Willem Cazander 2022-11-13 01:46:38 +01:00
parent fe9aa14dfd
commit 2d73cc8845
186 changed files with 21174 additions and 0 deletions

5
xnode-base/Makefile Normal file
View file

@ -0,0 +1,5 @@
# Minimal makefile only includes the master and libs.
MASTER_LIBS = rfm69 spi dht ethercard xnode-shared
SKT_PDE_SRC = XnodeBase.cpp
include ../lib-build/make/Makefile.master

34
xnode-base/XnodeBase.cpp Normal file
View file

@ -0,0 +1,34 @@
/*
* Xnode Base
*/
#include <XnodeSerial.h>
#include <XnodeSystem.h>
#include <XnodeBaseHardware.h>
#include <XnodeBaseNetwork.h>
#include <XnodeBaseRadio.h>
void setup() {
// Build system modules
XSystem.registrateSystemModule(&XBNetwork);
XSystem.registrateSystemModule(&XBRadio);
// Init system modules
XSerial.begin();
XSystem.begin(&XBHardware);
XBHardware.begin();
XBNetwork.begin();
XBRadio.begin();
// Mark device booted
XSystem.bootDone();
}
void loop() {
XSerial.loop();
XSystem.loop();
XBHardware.loop();
XBNetwork.loop();
XBRadio.loop();
}

View file

@ -0,0 +1,32 @@
#ifndef XnodeBaseConfig_h
#define XnodeBaseConfig_h
#include <Arduino.h>
union NetKey_t {
unsigned long int u32[NET_BYTE_NET_KEY_SIZE / 4];
unsigned char u8[NET_BYTE_NET_KEY_SIZE];
};
typedef union NetKey_t NetKey;
// Config data
typedef struct {
uint16_t eeprom_struct_size; // Config size changes it data defaults to zeros
byte net_mac[NET_BYTE_MAC_SIZE];
byte net_ip[NET_BYTE_IP_SIZE];
byte net_mask[NET_BYTE_IP_SIZE];
byte net_gate[NET_BYTE_IP_SIZE];
byte net_dns[NET_BYTE_IP_SIZE];
NetKey net_key;
byte net_id[NET_BYTE_NET_ID_SIZE];
byte rf_key[RF_KEY_SIZE];
byte rf_next_node_id;
unsigned long sys_boot; // boot counter
} xnode_base_config_t;
#endif

View file

@ -0,0 +1,172 @@
#include <XnodeBaseHardware.h>
const char pmSystemHardwareType[] PROGMEM = "xnode-base";
const char pmSystemHardwareVersion[] PROGMEM = XNODE_VERSION;
const char pmSysTestLeds[] PROGMEM = "System leds test";
byte CONFIG_DEFAULT_NET_MAC[] = NET_XNODE_MAC_DEFAULT;
unsigned long blink_timer = ZERO;
uint8_t blink_led = ZERO;
uint8_t blink_status = ZERO;
xnode_base_config_t config;
xnode_base_config_t EEMEM eeprom;
XnodeBaseHardware XBHardware;
void XnodeBaseHardware::begin() {
pinMode(HW_PIN_ERROR_LED, OUTPUT);
pinMode(HW_PIN_RADIO_LED, OUTPUT);
pinMode(HW_PIN_NETWORK_LED, OUTPUT);
changeLed(SYS_LED_ERROR, SYS_LED_STATUS_ON);
changeLed(SYS_LED_RADIO, SYS_LED_STATUS_ON);
changeLed(SYS_LED_NETWORK, SYS_LED_STATUS_ON);
XSerial.printCommentLineP(pmSysTestLeds);
delay(SYS_LED_TEST_TIME); // let users see all leds
changeLed(SYS_LED_ERROR, SYS_LED_STATUS_OFF);
changeLed(SYS_LED_RADIO, SYS_LED_STATUS_OFF);
changeLed(SYS_LED_NETWORK, SYS_LED_STATUS_OFF);
}
void XnodeBaseHardware::loop() {
loopBlink();
}
bool XnodeBaseHardware::systemHardwareConfigBegin() {
eeprom_read_block((void*) &config, (void*) &eeprom, sizeof(xnode_base_config_t));
if (config.eeprom_struct_size != sizeof(xnode_base_config_t)) {
return true;
}
config.sys_boot++;
return false;
}
void XnodeBaseHardware::systemHardwareConfigSave() {
config.eeprom_struct_size = sizeof(xnode_base_config_t);
eeprom_write_block((const void*) &config, (void*) &eeprom, sizeof(xnode_base_config_t));
}
void XnodeBaseHardware::systemHardwareConfigReset() {
// Temp code way
config.net_mac[0] = ZERO;
config.net_mac[1] = ZERO;
config.net_mac[2] = ZERO;
config.net_mac[3] = ZERO;
config.net_mac[4] = ZERO;
config.net_mac[5] = ZERO;
config.net_ip[0] = ZERO;
config.net_ip[1] = ZERO;
config.net_ip[2] = ZERO;
config.net_ip[3] = ZERO;
config.net_mask[0] = ZERO;
config.net_mask[1] = ZERO;
config.net_mask[2] = ZERO;
config.net_mask[3] = ZERO;
config.net_gate[0] = ZERO;
config.net_gate[1] = ZERO;
config.net_gate[2] = ZERO;
config.net_gate[3] = ZERO;
config.net_dns[0] = ZERO;
config.net_dns[1] = ZERO;
config.net_dns[2] = ZERO;
config.net_dns[3] = ZERO;
config.sys_boot = ZERO;
// end Temp
// TODO: Temp fix cast~~ so clear config zering all in one go.
//for (uint16_t i=ZERO;i<sizeof(xnode_base_config_t);i++) {
//((void *)config)+i = ZERO;
//}
// Copy some defaults into config
for (byte i = ZERO; i < NET_BYTE_MAC_SIZE; ++i) {
config.net_mac[i] = CONFIG_DEFAULT_NET_MAC[i];
}
// Add default mask
config.net_mask[0] = 255;
config.net_mask[1] = 255;
config.net_mask[2] = 255;
config.net_mask[3] = ZERO;
config.rf_next_node_id = ONE + ONE;
for (byte i = ZERO; i < RF_KEY_SIZE; ++i) {
config.rf_key[i] = ZERO;
}
for (byte i = ZERO; i < NET_BYTE_NET_KEY_SIZE; ++i) {
config.net_key.u8[i] = ZERO;
}
for (byte i = ZERO; i < NET_BYTE_NET_ID_SIZE; ++i) {
config.net_id[i] = ZERO;
}
}
unsigned long XnodeBaseHardware::getSystemHardwareRebootCount() {
return config.sys_boot;
}
const char* XnodeBaseHardware::getSystemHardwareTypeP() {
return pmSystemHardwareType;
}
const char* XnodeBaseHardware::getSystemHardwareVersionP() {
return pmSystemHardwareVersion;
}
void XnodeBaseHardware::writeLed(uint8_t led, uint8_t status) {
int out = LOW;
if (status != SYS_LED_STATUS_OFF) {
out = HIGH;
}
if (led == SYS_LED_ERROR) {
digitalWrite(HW_PIN_ERROR_LED, out);
} else if (led == SYS_LED_RADIO) {
digitalWrite(HW_PIN_RADIO_LED, out);
} else {
digitalWrite(HW_PIN_NETWORK_LED, out);
}
}
void XnodeBaseHardware::changeLed(uint8_t led, uint8_t status) {
#ifdef DEBUG_SYSTEM
Serial.print(F("#D XBHardware.changeLed led="));
Serial.print(led);
Serial.print(F(",status="));
Serial.print(status);
Serial.println();
#endif
if (status == SYS_LED_STATUS_BLINK) {
blink_led = led; // start blinking this led
}
if (blink_led == led && status != SYS_LED_STATUS_BLINK) {
blink_led = SYS_LED_NONE; // clear blinking
}
writeLed(led, status);
}
// blink led, note only single led can blink at once.
void XnodeBaseHardware::loopBlink() {
if (blink_led == SYS_LED_NONE) {
return; // no led to blink
}
unsigned long time = millis();
if (time < blink_timer) {
return; // wait until time is passed
}
blink_timer = time + SYS_LED_BLINK_TIME;
if (blink_status == ZERO) {
blink_status = ONE;
} else {
blink_status = ZERO;
}
writeLed(blink_led, blink_status);
}

View file

@ -0,0 +1,56 @@
#ifndef XnodeBaseHardware_h
#define XnodeBaseHardware_h
#include <XnodeConstants.h>
#include <XnodeSystemHardware.h>
#include <XnodeBaseConfig.h>
#include <XnodeSerial.h>
#include <avr/wdt.h>
#include <Arduino.h>
#include <avr/io.h>
#include <avr/wdt.h>
#include <util/delay.h>
#include <avr/eeprom.h>
#include <avr/interrupt.h>
#define HW_PIN_ERROR_LED 7
#define HW_PIN_RADIO_LED A2
#define HW_PIN_NETWORK_LED A3
#define HW_PIN_SPI_ETH0_CS 9
#define SYS_LED_TEST_TIME 666 // ms of on time in boot to see if all leds are on.
#define SYS_LED_BLINK_TIME 100 // ms delay of blink freq.
enum ShieldLED {
SYS_LED_NONE, // = 0 is used for blink led idx
SYS_LED_ERROR,
SYS_LED_RADIO,
SYS_LED_NETWORK
};
enum ShieldLEDStatus {
SYS_LED_STATUS_OFF, SYS_LED_STATUS_ON, SYS_LED_STATUS_BLINK
};
class XnodeBaseHardware: public XnodeSystemHardware {
private:
void loopBlink();
void writeLed(uint8_t led, uint8_t status);
public:
void begin();
void loop();
void changeLed(uint8_t led, uint8_t status);
// from XnodeSystemHardware
xnode_base_config_t config;
const char* getSystemHardwareTypeP();
const char* getSystemHardwareVersionP();
unsigned long getSystemHardwareRebootCount();
bool systemHardwareConfigBegin();
void systemHardwareConfigSave();
void systemHardwareConfigReset();
};
extern XnodeBaseHardware XBHardware;
#endif

View file

@ -0,0 +1,625 @@
#include <XnodeBaseNetwork.h>
const char pmNetworkChipBoot[] PROGMEM = "Init eth0";
const char pmNetworkChipError[] PROGMEM = "ERR: No chip";
const char pmNetworkLinkError[] PROGMEM = "ERR: No link";
const char pmNetworkStaticBoot[] PROGMEM = "Static IP";
const char pmNetworkDhcpBoot[] PROGMEM = "Request dhcp";
const char pmNetworkDhcpDone[] PROGMEM = "dhcp done";
const char pmNetworkDhcpError[] PROGMEM = "ERR: dhcp failed";
const char pmNetworkDnsLookup[] PROGMEM = "Lookup dns";
const char pmNetworkResultError[] PROGMEM = "# Invalid result:0x";
const char pmNetworkResultPing[] PROGMEM = "Server replied";
const char pmNetworkUsePrefix[] PROGMEM = "eth0_";
const char pmNetworkConfigInfo[] PROGMEM = "net_info";
const char pmNetworkConfigInfoEth0[] PROGMEM = "net_info_eth0";
const char pmNetworkConfigMac[] PROGMEM = "net_mac";
const char pmNetworkConfigIp[] PROGMEM = "net_ip";
const char pmNetworkConfigMask[] PROGMEM = "net_mask";
const char pmNetworkConfigGate[] PROGMEM = "net_gate";
const char pmNetworkConfigDns[] PROGMEM = "net_dns";
const char pmNetworkConfigKey[] PROGMEM = "net_key";
const char pmNetworkConfigId[] PROGMEM = "net_id";
const char pmNetworkServerHost[] PROGMEM = "net_srv_host";
const char pmNetworkServerIp[] PROGMEM = "net_srv_ip";
const char pmNetworkServerLookup[] PROGMEM = "net_srv_lup";
const char pmNetworkServer[] PROGMEM = NET_XNODE_HOST;
const char pmNetworkUrlPost[] PROGMEM = NET_URL_POST;
const char pmNetworkUrlParaPostType[] PROGMEM = NET_URL_PARA_POST_TYPE;
const char pmNetworkUrlParaReqCnt[] PROGMEM = NET_URL_PARA_REQ_CNT;
const char pmNetworkUrlParaNetId[] PROGMEM = NET_URL_PARA_NET_ID;
const char pmNetworkUrlParaNodeData[] PROGMEM = NET_URL_PARA_NODE_DATA;
const char pmNetworkUrlParaNodeNumber[] PROGMEM = NET_URL_PARA_NODE_NUMBER;
const char pmNetworkInfoTXOpen[] PROGMEM = "net_tx_open";
const char pmNetworkInfoTX[] PROGMEM = "net_tx";
const char pmNetworkInfoRX[] PROGMEM = "net_rx";
const char pmNetworkPing[] PROGMEM = "net_ping";
unsigned long net_ping_boot_timer = ZERO;
unsigned long net_ping_host_timer = ZERO;
unsigned long net_dhcp_retry_timer = ZERO;
unsigned long net_dns_lookup_timer = ZERO;
uint8_t net_status_eth = ZERO;
uint8_t net_status_ip = ZERO;
uint8_t net_status_dns = ZERO;
uint8_t net_status_data = ZERO;
uint8_t net_status_ping = ZERO;
uint8_t net_status_reply = ZERO;
uint8_t net_tx_open = ZERO;
unsigned long net_tx = ZERO;
unsigned long net_rx = ZERO;
char line_buf[128];
#define ETH_BUFFER_SIZE 450
byte Ethernet::buffer[ETH_BUFFER_SIZE];
Stash stash;
byte stashStart;
byte stashSession;
XnodeBaseNetwork XBNetwork;
void XnodeBaseNetwork::begin() {
XBHardware.changeLed(SYS_LED_NETWORK, SYS_LED_STATUS_ON);
setupChip();
setupIp();
XSerial.printCommentLineP(pmNetworkDnsLookup);
XSerial.executeCommandP(pmNetworkServerLookup);
XSerial.executeCommandP(pmNetworkConfigInfo);
XSerial.executeCommandP(pmNetworkConfigInfoEth0);
unsigned long time = millis();
net_dhcp_retry_timer = time + (unsigned long) NET_DHCP_RETRY_TIME;
net_dns_lookup_timer = time + (unsigned long) NET_DNS_LOOKUP_TIME;
net_ping_boot_timer = time + (unsigned long) NET_PING_BOOT_TIME;
net_ping_host_timer = time + (unsigned long) NET_PING_HOST_TIME;
XBHardware.changeLed(SYS_LED_NETWORK, SYS_LED_STATUS_OFF);
}
void XnodeBaseNetwork::loop() {
if (net_status_eth != ZERO) {
return; // no chip
}
loopAutoSetup(); // auto dhcp and dns refresh
if (net_status_ip != ZERO || net_status_dns != ZERO) {
return; // no ip or dns
}
ether.packetLoop(ether.packetReceive());
loopDataRX(); // rx first so that on tx-req packetLoop is runned first before RX code.
loopDataTX();
loopPingBoot();
loopPingHost();
}
// prev version needed about ~32 requests before network is working, now it works direct but we keep ping.
void XnodeBaseNetwork::loopPingBoot() {
if (net_status_ping == ONE) {
return;
}
unsigned long time = millis();
if (time < net_ping_boot_timer) {
return; // wait
}
net_ping_boot_timer = time + (unsigned long) NET_PING_BOOT_TIME;
if (net_status_reply == ZERO) {
line_buf[ZERO] = ZERO;
sendPing(); // keep sending ping requests.
return;
}
if (net_status_ping == ZERO) {
XSerial.print(CHAR_NEWLINE);// new line bacause the dot progress bar
XSerial.printCommentLineP(pmNetworkResultPing); // let user know network is up.
XSerial.printPromt();
net_tx_open = ZERO; // reset open request counter
}
net_status_ping = ONE+ONE; // print once
if (XBHardware.config.net_key.u8[ZERO] == ZERO) {
sendInit(); // send init request.
net_ping_boot_timer += (unsigned long) NET_PING_BOOT_TIME; // add extra wait time
return;
}
net_status_ping = ONE; // never run this function again.
}
void XnodeBaseNetwork::loopPingHost() {
if (millis() < net_ping_host_timer) {
return; // wait
}
sendPing();
net_ping_host_timer = millis() + (unsigned long) NET_PING_HOST_TIME;
}
void XnodeBaseNetwork::xxteaEncrypt(unsigned long v[2]) {
unsigned int i;
unsigned long v0=v[0], v1=v[1], sum=0, delta=0x9E3779B9;
for (i=0; i < XXTEA_NUM_ROUNDS; i++) {
v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + XBHardware.config.net_key.u32[sum & 3]);
sum += delta;
v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + XBHardware.config.net_key.u32[(sum>>11) & 3]);
}
v[0]=v0; v[1]=v1;
}
void XnodeBaseNetwork::xxteaDecrypt(unsigned long v[2]) {
unsigned int i;
uint32_t v0=v[0], v1=v[1], delta=0x9E3779B9, sum=delta*XXTEA_NUM_ROUNDS;
for (i=0; i < XXTEA_NUM_ROUNDS; i++) {
v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + XBHardware.config.net_key.u32[(sum>>11) & 3]);
sum -= delta;
v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + XBHardware.config.net_key.u32[sum & 3]);
}
v[0]=v0; v[1]=v1;
}
int XnodeBaseNetwork::parseReplyData(word off) {
memset(line_buf, NULL, sizeof(line_buf));
if (off != 0) {
uint16_t pos = off;
int line_pos = 0;
// Skip over header until data part is found
while (Ethernet::buffer[pos]) {
if (Ethernet::buffer[pos - 1] == '\n' && Ethernet::buffer[pos] == '\r') {
break;
}
pos++;
}
pos += 2;
while (Ethernet::buffer[pos]) {
if (line_pos < 49) {
line_buf[line_pos] = Ethernet::buffer[pos];
line_pos++;
} else {
break;
}
pos++;
}
line_buf[line_pos] = '\0';
return line_pos;
}
return 0;
}
void XnodeBaseNetwork::sendStart(char postType) {
if (stashSession != ZERO) {
int maxWait = 100;
while (stashSession != ZERO) {
loop();
maxWait--;
if (maxWait == ZERO) {
break;
}
}
#ifdef DEBUG_NETWORK
if (stashSession != ZERO) {
Serial.println(F("#D kill prev tcp req"));
}
#endif
//return; // data is already sending (TODO: move ??)
}
stashStart = stash.create();
stash.print(XUtil.UNPSTR(pmNetworkUrlParaPostType));
stash.print(postType);
stash.print(XUtil.UNPSTR(pmNetworkUrlParaReqCnt));
stash.print(net_tx++);
stash.print(XUtil.UNPSTR(pmNetworkUrlParaNetId));
for (byte i = ZERO; i < NET_BYTE_NET_ID_SIZE; ++i) {
byte data = XBHardware.config.net_id[i];
if (data<0xF) {
stash.print(ZERO); // extra zero to have fixed length hex string
}
stash.print(data, HEX);
}
}
void XnodeBaseNetwork::sendPing() {
sendStart(NET_URL_PT_PING);
net_status_data = NET_URL_PT_PING;
}
void XnodeBaseNetwork::sendInit() {
#ifdef DEBUG_NETWORK
Serial.println(F("#D req init"));
#endif
sendStart(NET_URL_PT_INIT);
net_status_data = NET_URL_PT_INIT;
}
NetKey x_data;
void XnodeBaseNetwork::sendNodeData(byte senderId, char* data) {
sendStart(NET_URL_PT_DATA);
stash.print(XUtil.UNPSTR(pmNetworkUrlParaNodeNumber));
stash.print((int) senderId);
stash.print(XUtil.UNPSTR(pmNetworkUrlParaNodeData));
char* dstring = data;
while (*dstring != 0) {
char value = *dstring;
dstring++;
stash.print(value);
}
/*
byte i = 0;
char* dstring = data;
while (*dstring != 0) {
x_data.u8[i] = *dstring;
dstring++;
i++;
if (i==8) {
i = 0;
xxteaEncrypt(x_data.u32);
for (byte i = 0; i < 8; ++i) {
char value = x_data.u8[i];
if (value == 0) {
stash.print('00');
continue;
}
if (value <= 0xF) {
stash.print('0');
}
stash.print(value,HEX);
}
}
}
while(i < 8) {
x_data.u8[i] = 0;
i++;
}
xxteaEncrypt(x_data.u32);
for (byte i = 0; i < 8; ++i) {
char value = x_data.u8[i];
if (value == 0) {
stash.print('00');
continue;
}
if (value <= 0xF) {
stash.print('0');
}
stash.print(value,HEX);
}
*/
net_status_data = NET_URL_PT_DATA;
}
void XnodeBaseNetwork::loopDataTX() {
if (net_status_data == ZERO) {
return; // no data to send
}
if (!ether.isLinkUp()) {
if (net_status_ip == ZERO) {
XSerial.printCommentLineP(pmNetworkLinkError);
}
net_status_ip = ONE;
return; // link down; request new ip when link gets up.
}
XBHardware.changeLed(SYS_LED_NETWORK, SYS_LED_STATUS_ON);
#ifdef DEBUG_NETWORK
Serial.print(F("#D sendHttp pt="));
Serial.print((char)net_status_data);
Serial.print(F(" size="));
Serial.println((char)stash.size(),DEC);
#endif
stash.save();
Stash::prepare(PSTR("POST http://$F/$F HTTP/1.1" "\r\n"
"Host: $F" "\r\n"
"Cache-Control: no-cache" "\r\n"
"Content-type: application/x-www-form-urlencoded;\r\n"
"Accept: application/xhtml+xml" "\r\n"
"Content-Length: $D" "\r\n"
"\r\n"
"$H"), pmNetworkServer, pmNetworkUrlPost, pmNetworkServer, stash.size(), stashStart);
net_tx_open++;
net_status_data = ZERO;
stashSession = ether.tcpSend(); // releases also stash buf
//stash.release();
}
void XnodeBaseNetwork::loopDataRX() {
if (stashSession == ZERO) {
return; // no reply to search for
}
const char* msgStart = ether.tcpReply(stashSession);
if (msgStart == ZERO) {
#ifdef DEBUG_NETWORK
Serial.println(F("#D wait tcpReply"));
#endif
delay(100);
return; // no reply yet
}
net_rx++;
stashSession = ZERO;
parseReplyData((int) *msgStart);
handleServerResult();
XBHardware.changeLed(SYS_LED_NETWORK, SYS_LED_STATUS_OFF);
}
void XnodeBaseNetwork::handleServerResult() {
if (line_buf[ZERO] != NET_URL_RESULT_OK) {
XSerial.printCharP(pmNetworkResultError);
Serial.print(line_buf[ZERO], HEX); // TODO: check
#ifdef DEBUG_NETWORK
Serial.print(CHAR_SPACE);
for (int i=0;i<10;i++) {
if (line_buf[i] != ZERO) {
Serial.print(line_buf[i]);
}
}
#endif
XSerial.print(CHAR_NEWLINE);
return;
}
net_tx_open--; // valid result
net_status_reply = ONE; // we have reply
if (line_buf[ONE] == ZERO) {
return; // Only X returns so nop.
}
if (line_buf[ONE] == NET_URL_PT_INIT) {
XSystem.executeCommand(line_buf + ONE + ONE,true); // skip Xi chars
loop(); // Else we miss most of the time the second init request.
loop(); // an other extra to be sure we done miss the second request.
sendInit(); // request next init command
return;
}
if (line_buf[ONE] == NET_URL_RESULT_OK) {
XSystem.executeCommand(line_buf + ONE + ONE,true); // skip XX chars
loop();
loop();
sendNodeData(RF_BASE_NODE_ID,XSystem.replyBuffer); // send reply as
}
}
bool XnodeBaseNetwork::isUp() {
return ZERO == (net_status_eth + net_status_ip + net_status_dns);
}
void XnodeBaseNetwork::loopAutoSetup() {
unsigned long time = millis();
// auto dhcp
if (net_status_ip == ONE) {
if (time < net_dhcp_retry_timer) {
return; // wait
}
net_dhcp_retry_timer = millis() + (unsigned long) NET_DHCP_RETRY_TIME;
setupIp();
if (net_status_ip == ONE) {
return; // failed dhcp
}
net_status_dns = ONE; // when link plugs in later
net_dns_lookup_timer = ONE; // then do direct dns lookup
}
// auto dns
if (net_status_dns == ONE) {
if (time < net_dns_lookup_timer) {
return; // wait
}
net_dns_lookup_timer = time + (unsigned long) NET_DNS_LOOKUP_TIME;
XSerial.executeCommandP(pmNetworkServerLookup);
}
// auto dns refresh
if (net_status_dns == ZERO && time > net_dns_lookup_timer) {
net_status_dns = ONE;
}
}
// FIXME: without chip it takes 3 watchdog reboots before no chip result ?? (prev lib version)
void XnodeBaseNetwork::setupChip() {
XSerial.printCommentLineP(pmNetworkChipBoot);
if (ether.begin(sizeof Ethernet::buffer, XBHardware.config.net_mac, HW_PIN_SPI_ETH0_CS) == ZERO) {
XSerial.printCommentLineP(pmNetworkChipError);
XBHardware.changeLed(SYS_LED_ERROR, SYS_LED_STATUS_BLINK);
net_status_eth = ONE;
return;
}
ether.disableBroadcast();
ether.disableMulticast();
delay(100); // needed for link isLinkUp to return valid value.
}
void XnodeBaseNetwork::setupIp() {
net_status_ip = ZERO;
if (net_status_eth != ZERO) {
return; // no chip
}
if (!ether.isLinkUp()) {
net_status_ip = ONE; // do again
XSerial.printCommentLineP(pmNetworkLinkError);
return;
}
// Setup static if ip is given.
if (XBHardware.config.net_ip[ZERO] != ZERO) {
XSerial.printCommentLineP(pmNetworkStaticBoot);
ether.staticSetup(XBHardware.config.net_ip, XBHardware.config.net_gate, XBHardware.config.net_dns);
ether.copyIp(ether.netmask, XBHardware.config.net_mask);
return;
}
XSerial.printCommentLineP(pmNetworkDhcpBoot);
wdt_disable();
if (!ether.dhcpSetup()) {
net_status_ip = ONE; // do again
XSerial.printCommentLineP(pmNetworkDhcpError);
XBHardware.changeLed(SYS_LED_ERROR, SYS_LED_STATUS_ON); // TODO: fix led blinking on timer int.
} else {
XSerial.printCommentLineP(pmNetworkDhcpDone);
XBHardware.changeLed(SYS_LED_ERROR, SYS_LED_STATUS_OFF); // set led off after recovery
}
wdt_enable(WDT_TIMEOUT); // no link or no dhcp will take long timeout ~30s
#ifdef DEBUG_NET_GATE
if (net_status_ip == ZERO) {
byte debug_gate[NET_BYTE_IP_SIZE] = DEBUG_NET_GATE; // transparent http proxy network config.
ether.copyIp(ether.gwip, debug_gate);
Serial.println(F("#D Used debug gate ip"));
}
#endif
}
void XnodeBaseNetwork::cmdDnsLookup() {
net_status_dns = ZERO;
if (net_status_eth != ZERO) {
return; // no chip
}
if (net_status_ip != ZERO) {
net_status_dns = ONE;
return; // no ip
}
#ifdef DEBUG_NET_HISIP
byte debug_hisip[NET_BYTE_IP_SIZE] = DEBUG_NET_HISIP;
ether.copyIp(ether.hisip, debug_hisip);
Serial.println(F("#D Used debug dns ip"));
return;
#endif
wdt_disable();
if (!ether.dnsLookup(pmNetworkServer)) {
net_status_dns = ONE;
XSystem.buildReplyPValue(pmNetworkServerLookup,ZERO);
XBHardware.changeLed(SYS_LED_ERROR, SYS_LED_STATUS_BLINK);
} else {
XSystem.buildReplyPValue(pmNetworkServerLookup,ONE);
XBHardware.changeLed(SYS_LED_ERROR, SYS_LED_STATUS_OFF);
}
wdt_enable (WDT_TIMEOUT);
}
void XnodeBaseNetwork::cmdInfoEth0() {
// Print currently used config
XSystem.buildReplyCharP(pmNetworkUsePrefix);
XSystem.buildReplyPValueIP(pmNetworkConfigIp, ether.myip);
XSystem.buildReplyCharP(pmNetworkUsePrefix);
XSystem.buildReplyPValueIP(pmNetworkConfigMask, ether.netmask);
XSystem.buildReplyCharP(pmNetworkUsePrefix);
XSystem.buildReplyPValueIP(pmNetworkConfigGate, ether.gwip);
XSystem.buildReplyCharP(pmNetworkUsePrefix);
XSystem.buildReplyPValueIP(pmNetworkConfigDns, ether.dnsip);
XSystem.buildReplyCharP(pmNetworkUsePrefix);
XSystem.buildReplyPValueByteA(pmNetworkConfigMac,ether.mymac, NET_BYTE_MAC_SIZE);
}
void XnodeBaseNetwork::cmdInfo() {
// Print server value and lookup
XSystem.buildReplyPValueP(pmNetworkServerHost,pmNetworkServer);
XSystem.buildReplyPValueIP(pmNetworkServerIp, ether.hisip);
// Print config
XSystem.buildReplyPValueByteA(pmNetworkConfigMac,XBHardware.config.net_mac, NET_BYTE_MAC_SIZE);
XSystem.buildReplyPValueIP(pmNetworkConfigIp, XBHardware.config.net_ip);
XSystem.buildReplyPValueIP(pmNetworkConfigMask, XBHardware.config.net_mask);
XSystem.buildReplyPValueIP(pmNetworkConfigGate, XBHardware.config.net_gate);
XSystem.buildReplyPValueIP(pmNetworkConfigDns, XBHardware.config.net_dns);
XSystem.buildReplyPValueByteA(pmNetworkConfigKey,XBHardware.config.net_key.u8, NET_BYTE_NET_KEY_SIZE);
XSystem.buildReplyPValueByteA(pmNetworkConfigId,XBHardware.config.net_id, NET_BYTE_NET_ID_SIZE);
XSystem.buildReplyPValue(pmNetworkInfoTXOpen, (int) net_tx_open);
XSystem.buildReplyPValue(pmNetworkInfoTX, net_tx);
XSystem.buildReplyPValue(pmNetworkInfoRX, net_rx);
}
void XnodeBaseNetwork::parseCommandIp(const char* pmName, char** args, byte* ip_conf) {
ether.parseIp(ip_conf, args[ZERO]);
XSystem.buildReplyPValueIP(pmName, ip_conf);
XSystem.configSave(); // auto save on change
}
void XnodeBaseNetwork::systemModuleCommandList() {
XSystem.buildReplyCommandListP(pmNetworkConfigInfo);
XSystem.buildReplyCommandListP(pmNetworkConfigInfoEth0);
XSystem.buildReplyCommandListP(pmNetworkConfigMac);
XSystem.buildReplyCommandListP(pmNetworkConfigIp);
XSystem.buildReplyCommandListP(pmNetworkConfigMask);
XSystem.buildReplyCommandListP(pmNetworkConfigGate);
XSystem.buildReplyCommandListP(pmNetworkConfigDns);
XSystem.buildReplyCommandListP(pmNetworkPing);
XSystem.buildReplyCommandListP(pmNetworkServerLookup);
#ifdef DEBUG_NETWORK
XSystem.buildReplyCommandListP(pmNetworkConfigId); // keep secret that these cmd work
XSystem.buildReplyCommandListP(pmNetworkConfigKey); // for init over network with this.
#endif
}
bool XnodeBaseNetwork::systemModuleCommandExecute(char* cmd, char** args) {
if (XUtil.strcmpP(cmd, pmNetworkConfigInfo) == ZERO) {
cmdInfo();
return true;
} else if (XUtil.strcmpP(cmd, pmNetworkConfigInfoEth0) == ZERO) {
cmdInfoEth0();
return true;
} else if (XUtil.strcmpP(cmd, pmNetworkConfigId) == ZERO) {
if (XSystem.buildReplyCommandArgumentError(cmd, args)) {
return true;
}
XUtil.charsToByteA(args[ZERO],XBHardware.config.net_id, NET_BYTE_NET_ID_SIZE);
XSystem.buildReplyPValueByteA(pmNetworkConfigId,XBHardware.config.net_id,NET_BYTE_NET_ID_SIZE);
XSystem.configSave();
return true;
} else if (XUtil.strcmpP(cmd, pmNetworkConfigKey) == ZERO) {
if (XSystem.buildReplyCommandArgumentError(cmd, args)) {
return true;
}
XUtil.charsToByteA(args[ZERO],XBHardware.config.net_key.u8, NET_BYTE_NET_KEY_SIZE);
XSystem.buildReplyPValueByteA(pmNetworkConfigKey,XBHardware.config.net_key.u8,NET_BYTE_NET_KEY_SIZE);
XSystem.configSave();
return true;
} else if (XUtil.strcmpP(cmd, pmNetworkConfigMac) == ZERO) {
if (XSystem.buildReplyCommandArgumentError(cmd, args)) {
return true;
}
XUtil.charsToByteA(args[ZERO],XBHardware.config.net_mac, NET_BYTE_MAC_SIZE);
XSystem.buildReplyPValueByteA(pmNetworkConfigMac,XBHardware.config.net_mac, NET_BYTE_MAC_SIZE);
XSystem.configSave();
return true;
} else if (XUtil.strcmpP(cmd, pmNetworkConfigIp) == ZERO) {
if (XSystem.buildReplyCommandArgumentError(cmd, args)) {
return true;
}
parseCommandIp(pmNetworkConfigIp, args, XBHardware.config.net_ip);
return true;
} else if (XUtil.strcmpP(cmd, pmNetworkConfigMask) == ZERO) {
if (XSystem.buildReplyCommandArgumentError(cmd, args)) {
return true;
}
parseCommandIp(pmNetworkConfigMask, args, XBHardware.config.net_mask);
return true;
} else if (XUtil.strcmpP(cmd, pmNetworkConfigGate) == ZERO) {
if (XSystem.buildReplyCommandArgumentError(cmd, args)) {
return true;
}
parseCommandIp(pmNetworkConfigGate, args, XBHardware.config.net_gate);
return true;
} else if (XUtil.strcmpP(cmd, pmNetworkConfigDns) == ZERO) {
if (XSystem.buildReplyCommandArgumentError(cmd, args)) {
return true;
}
parseCommandIp(pmNetworkConfigDns, args, XBHardware.config.net_dns);
return true;
} else if (XUtil.strcmpP(cmd, pmNetworkPing) == ZERO) {
sendPing();
return true;
} else if (XUtil.strcmpP(cmd, pmNetworkServerLookup) == ZERO) {
cmdDnsLookup();
return true;
}
return false;
}

View file

@ -0,0 +1,53 @@
#ifndef XnodeBaseNetwork_h
#define XnodeBaseNetwork_h
#include <XnodeBaseHardware.h>
#include <XnodeSerial.h>
#include <XnodeSystem.h>
#include <XnodeSystemModule.h>
#include <XnodeBaseConfig.h>
#include <XnodeUtil.h>
#include <avr/wdt.h>
#include <Arduino.h>
#include <EtherCard.h>
#define XXTEA_NUM_ROUNDS 32
class XnodeBaseNetwork: public XnodeSystemModule {
private:
int parseReplyData(word off);
void sendStart(char postType);
void loopAutoSetup();
void loopDataTX();
void loopDataRX();
void loopPingBoot();
void loopPingHost();
void handleServerResult();
void setupChip();
void setupIp();
void sendPing();
void sendInit();
void parseCommandIp(const char* pmName, char** args, byte* ip_conf);
void xxteaEncrypt(unsigned long v[2]);
void xxteaDecrypt(unsigned long v[2]);
void cmdDnsLookup();
void cmdInfo();
void cmdInfoEth0();
public:
void begin();
void loop();
bool isUp();
void sendNodeData(byte senderId, char* data);
// from XnodeSystemModule
bool systemModuleCommandExecute(char* cmd, char** args);
void systemModuleCommandList();
};
extern XnodeBaseNetwork XBNetwork;
#endif

View file

@ -0,0 +1,243 @@
#include <XnodeBaseRadio.h>
const char pmRadioChipInit[] PROGMEM = MSG_RF_CHIP_INIT;
const char pmRadioChipError[] PROGMEM = MSG_RF_CHIP_ERROR;
const char pmRadioInfoFreq[] PROGMEM = MSG_RF_INFO_FREQ;
const char pmRadioInfoNetwork[] PROGMEM = MSG_RF_INFO_NETWORK;
const char pmRadioInfoNode[] PROGMEM = MSG_RF_INFO_NODE;
const char pmRadioInfoKey[] PROGMEM = MSG_RF_INFO_KEY;
const char pmRadioInfoTx[] PROGMEM = MSG_RF_INFO_TX;
const char pmRadioInfoRx[] PROGMEM = MSG_RF_INFO_RX;
const char pmRadioInfoTxe[] PROGMEM = MSG_RF_INFO_TXE;
const char pmRadioInfoNextNode[] PROGMEM = "rf_next_node";
//const char pmRadioInfoNextNode[] PROGMEM = "rf_sat";
const char pmRadioEncrypt[] PROGMEM = MSG_RF_ENCRYPT;
const char pmRadioInfo[] PROGMEM = MSG_RF_INFO;
const char pmRadioSendFailRf[] PROGMEM = "rf_fail=1";
volatile unsigned long key_encrypt_timer = ZERO;
volatile unsigned long rf_no_data_timer = ZERO;
unsigned long rf_tx = ZERO;
unsigned long rf_rx = ZERO;
unsigned long rf_txe = ZERO;
uint16_t rf_ping_count = ZERO;
uint8_t rf_status_data = ZERO;
uint8_t rf_status_ping = ZERO;
uint8_t rf_status_chip = ZERO;
byte rf_data_sender_id;
xp_super_t rf_data;
xp_msg_init_t* rf_msg_init;
xp_msg_flash_t* rf_msg_flash;
xp_msg_char_t* rf_msg_char;
RFM69 radio;
XnodeBaseRadio XBRadio;
void XnodeBaseRadio::begin() {
XBHardware.changeLed(SYS_LED_RADIO, SYS_LED_STATUS_ON);
// Init radio chip
XSerial.printCommentLineP(pmRadioChipInit);
if (!radio.initialize(RF_FREQUENCY, RF_BASE_NODE_ID, RF_NETWORK_ID)) {
XSerial.printCommentLineP(pmRadioChipError);
XBHardware.changeLed(SYS_LED_ERROR, SYS_LED_STATUS_BLINK);
rf_status_chip = ONE;
return;
}
radio.promiscuous(true);
XSerial.executeCommandP(pmRadioInfo);
// init of time window until rf is encrypted and idle timeout
key_encrypt_timer = millis() + (unsigned long) RF_KEY_ENCRYPT_TIME;
rf_no_data_timer = millis() + (unsigned long) RF_KEY_ENCRYPT_TIME + (unsigned long) RF_NO_DATA_FAIL_TIME;
}
void XnodeBaseRadio::loop() {
if (rf_status_chip != ZERO) {
return; // no chip
}
if (key_encrypt_timer != ZERO && millis() > key_encrypt_timer) {
cmdClose();
}
checkNoDataTimeout();
if (!radio.receiveDone()) {
return;
}
XBHardware.changeLed(SYS_LED_RADIO, SYS_LED_STATUS_ON);
rf_no_data_timer = millis() + (unsigned long) RF_NO_DATA_FAIL_TIME;
rf_rx++;
rf_status_data = ZERO;
rf_data = *(xp_super_t*) radio.DATA;
rf_data_sender_id = radio.SENDERID;
#ifdef DEBUG_RADIO
Serial.print(F("#D rf_rx "));
Serial.print(rf_data_sender_id,DEC);
Serial.print(CHAR_SPACE);
Serial.print(rf_data.msg_type,DEC);
Serial.print(CHAR_NEWLINE);
#endif
// always reply ack if requested, see sendWithRetry impl (before reply with sendWithRetry)
if (radio.ACK_REQUESTED) {
#ifdef DEBUG_RADIO
Serial.println(F("#D rf_sendACK"));
#endif
radio.sendACK();
}
if (rf_data.msg_type == RF_MSG_NODE_DATA) {
rf_msg_char = (xp_msg_char_t*) rf_data.msg_data;
XBNetwork.sendNodeData(rf_data_sender_id, rf_msg_char->char_data);
} else if (rf_data.msg_type == RF_MSG_NODE_INIT) {
rf_msg_init = (xp_msg_init_t*) rf_data.msg_data;
handleCmdInit();
} else if (rf_data.msg_type == RF_MSG_NODE_FLASH) {
rf_msg_flash = (xp_msg_flash_t*) rf_data.msg_data;
handleCmdFlash();
}
checkPing(); // sets radio.SENDERID to zero because it sends data
XBHardware.changeLed(SYS_LED_RADIO, SYS_LED_STATUS_OFF);
}
void XnodeBaseRadio::checkPing() {
rf_ping_count++;
if (rf_ping_count % 10 != ZERO) {
return;
}
rf_status_ping = ZERO; // reset good
#ifdef DEBUG_RADIO
Serial.print(F("#D rf_ping "));
Serial.print(radio.SENDERID);
Serial.print(CHAR_NEWLINE);
#endif
delay(3); //need this when sending right after reception .. ?
rf_tx++;
if (!radio.sendWithRetry(radio.SENDERID, "ACK TEST", 8, 0)) { // 0 = only 1 attempt, no retries
rf_txe++;
rf_status_ping = ONE; // bad
}
}
void XnodeBaseRadio::checkNoDataTimeout() {
if (millis() < rf_no_data_timer) {
return; // wait until time is passed
}
rf_no_data_timer = millis() + (unsigned long) RF_NO_DATA_FAIL_TIME;
rf_msg_char = (xp_msg_char_t*) rf_data.msg_data; // hacky reuse sensor buffer to send data
strcpy(rf_msg_char->char_data, XUtil.UNPSTR(pmRadioSendFailRf)); // because send also used unpstr buffer
XBNetwork.sendNodeData(RF_BASE_NODE_ID, rf_msg_char->char_data);
}
void XnodeBaseRadio::sendReply() {
rf_tx++;
if (!radio.sendWithRetry(rf_data_sender_id, (const void*) &rf_data, sizeof(xp_super_t))) {
rf_txe++;
}
}
void XnodeBaseRadio::handleCmdFlash() {
sendReply();
}
void XnodeBaseRadio::handleCmdInit() {
for (byte i = ZERO; i < RF_KEY_SIZE; ++i) {
rf_msg_init->rf_key[i] = XBHardware.config.rf_key[i];
}
#ifdef DEBUG_RADIO
Serial.print(F("#D rf_init "));
Serial.print(rf_msg_init->node_id);
Serial.print(CHAR_NEWLINE);
#endif
if (rf_msg_init->node_id == ZERO) {
rf_msg_init->node_id = XBHardware.config.rf_next_node_id;
// Save next node id
XBHardware.config.rf_next_node_id++;
XSystem.configSave();
}
sendReply();
// update open encrypt timer so user can init multiple devices one at the time
key_encrypt_timer = millis() + (unsigned long) RF_KEY_ENCRYPT_TIME;
}
void XnodeBaseRadio::cmdClose() {
XSystem.buildReplyPValue(pmRadioEncrypt, ONE);
radio.encrypt((const char*) XBHardware.config.rf_key);
key_encrypt_timer = ZERO; // flag encrypted
XBHardware.changeLed(SYS_LED_RADIO, SYS_LED_STATUS_OFF);
}
void XnodeBaseRadio::cmdOpen() {
if (key_encrypt_timer != ZERO) {
return; // only open if it is encrypted already
}
radio.encrypt(ZERO);
key_encrypt_timer = millis() + (unsigned long) RF_KEY_ENCRYPT_TIME;
XSystem.buildReplyPValue(pmRadioEncrypt, ZERO);
XBHardware.changeLed(SYS_LED_RADIO, SYS_LED_STATUS_BLINK); // rf blink = open for key requests
}
void XnodeBaseRadio::cmdInfo() {
// Print key
XSystem.buildReplyPValueByteA(pmRadioInfoKey,XBHardware.config.rf_key, RF_KEY_SIZE);
// Print radio config
XSystem.buildReplyPValue(pmRadioInfoFreq,(int) RF_FREQUENCY == RF69_433MHZ ? 433 : RF_FREQUENCY == RF69_868MHZ ? 868 : 915);
XSystem.buildReplyPValue(pmRadioInfoNetwork,(int) RF_NETWORK_ID);
XSystem.buildReplyPValue(pmRadioInfoNode,(int) RF_BASE_NODE_ID);
XSystem.buildReplyPValue(pmRadioEncrypt,(int) key_encrypt_timer == ZERO ? ONE : ZERO);// if timer==0 then encrypted=1
// Print base radio stuff
XSystem.buildReplyPValue(pmRadioInfoRx, rf_rx);
XSystem.buildReplyPValue(pmRadioInfoTx, rf_tx);
XSystem.buildReplyPValue(pmRadioInfoTxe, rf_txe);
XSystem.buildReplyPValue(pmRadioInfoNextNode, (int) XBHardware.config.rf_next_node_id);
}
void XnodeBaseRadio::systemModuleCommandList() {
XSystem.buildReplyCommandListP(pmRadioInfoKey);
XSystem.buildReplyCommandListP(pmRadioInfo);
XSystem.buildReplyCommandListP(pmRadioEncrypt);
}
bool XnodeBaseRadio::systemModuleCommandExecute(char* cmd, char** args) {
if (XUtil.strcmpP(cmd, pmRadioInfo) == ZERO) {
cmdInfo();
return true;
} else if (XUtil.strcmpP(cmd, pmRadioInfoKey) == ZERO) {
if (XSystem.buildReplyCommandArgumentError(cmd, args)) {
return true;
}
XUtil.charsToByteA(args[ZERO],XBHardware.config.rf_key, RF_KEY_SIZE);
XSystem.buildReplyPValueByteA(pmRadioInfoKey,XBHardware.config.rf_key, RF_KEY_SIZE);
XSystem.configSave();
return true;
} else if (XUtil.strcmpP(cmd, pmRadioEncrypt) == ZERO) {
if (key_encrypt_timer == ZERO) {
cmdOpen();
} else {
cmdClose();
}
return true;
}
return false;
}

View file

@ -0,0 +1,33 @@
#ifndef XnodeBaseRadio_h
#define XnodeBaseRadio_h
#include <XnodeBaseHardware.h>
#include <XnodeSerial.h>
#include <XnodeSystem.h>
#include <XnodeSystemModule.h>
#include <XnodeBaseNetwork.h>
#include <XnodeProtocol.h>
#include <RFM69.h>
class XnodeBaseRadio: public XnodeSystemModule {
private:
void checkNoDataTimeout();
void checkPing();
void cmdOpen();
void cmdClose();
void cmdInfo();
void handleCmdInit();
void handleCmdFlash();
void sendReply();
public:
void begin();
void loop();
// from XnodeSystemModule
bool systemModuleCommandExecute(char* cmd, char** args);
void systemModuleCommandList();
};
extern XnodeBaseRadio XBRadio;
#endif