From 69ac4b759aa03a101ab27adaef5eb44d84c21444 Mon Sep 17 00:00:00 2001 From: Marcelo Aquino Date: Mon, 10 Oct 2016 22:35:13 -0300 Subject: [PATCH] RPi: Add support for I2C Add MyHw file for RaspberryPi. Fix compilation warnings in x86. Fix some error messages. --- MySensors.h | 18 +-- core/MyHw.h | 27 +++- core/MyHwATMega328.cpp | 4 +- core/MyHwESP8266.cpp | 2 + core/MyHwLinuxGeneric.cpp | 105 ++++----------- core/MyHwLinuxGeneric.h | 22 ++-- core/MyHwRPi.cpp | 151 ++++++++++++++++++++++ core/MyHwRPi.h | 34 +++++ core/MyHwSAMD.cpp | 2 + core/MyMainLinux.cpp | 19 ++- drivers/Linux/Arduino.h | 12 ++ drivers/Linux/EthernetServer.cpp | 6 +- drivers/Linux/Print.h | 9 ++ drivers/Linux/SerialPort.cpp | 22 +++- drivers/Linux/SoftEeprom.cpp | 129 +++++++++++++++++++ drivers/Linux/SoftEeprom.h | 81 ++++++++++++ drivers/RPi/SPI.cpp | 11 +- drivers/RPi/SPI.h | 11 +- drivers/RPi/Wire.cpp | 213 +++++++++++++++++++++++++++++++ drivers/RPi/Wire.h | 80 ++++++++++++ drivers/RPi/rpi_util.cpp | 6 +- 21 files changed, 817 insertions(+), 147 deletions(-) create mode 100644 core/MyHwRPi.cpp create mode 100644 core/MyHwRPi.h create mode 100644 drivers/Linux/SoftEeprom.cpp create mode 100644 drivers/Linux/SoftEeprom.h create mode 100644 drivers/RPi/Wire.cpp create mode 100644 drivers/RPi/Wire.h diff --git a/MySensors.h b/MySensors.h index 508bb31..cbeee90 100644 --- a/MySensors.h +++ b/MySensors.h @@ -71,22 +71,10 @@ #elif defined(ARDUINO_ARCH_SAMD) #include "core/MyHwSAMD.cpp" #elif defined(__linux__) - // Remove PSTR macros from debug prints - #undef PSTR - #define PSTR(x) (x) - //#undef F - //#define F(x) (x) - #define PROGMEM - #define vsnprintf_P(...) vsnprintf( __VA_ARGS__ ) - #define snprintf_P(...) snprintf( __VA_ARGS__ ) - #define memcpy_P memcpy - #define pgm_read_dword(x) (*x) - #define pgm_read_byte_near(x) (*x) - #include "core/MyHwLinuxGeneric.cpp" - // Ugly hack #ifdef LINUX_ARCH_RASPBERRYPI - #undef hwDigitalWrite - #define hwDigitalWrite(__pin, __value) (digitalWrite(__pin, __value)) + #include "core/MyHwRPi.cpp" + #else + #include "core/MyHwLinuxGeneric.cpp" #endif #endif diff --git a/core/MyHw.h b/core/MyHw.h index f2b29c3..99bfcc6 100644 --- a/core/MyHw.h +++ b/core/MyHw.h @@ -36,12 +36,15 @@ // Implement these as functions or macros /* -#define hwDigitalWrite(__pin, __value) #define hwInit() MY_SERIALDEVICE.begin(BAUD_RATE) #define hwWatchdogReset() wdt_reset() #define hwReboot() wdt_enable(WDTO_15MS); while (1) #define hwMillis() millis() +#define hwDigitalWrite(__pin, __value) +#define hwDigitalRead(__pin) +#define hwPinMode(__pin, __value) + void hwReadConfigBlock(void* buf, void* adr, size_t length); void hwWriteConfigBlock(void* buf, void* adr, size_t length); void hwWriteConfig(int adr, uint8_t value); @@ -75,8 +78,28 @@ int8_t hwSleep(uint8_t interrupt, uint8_t mode, unsigned long ms); */ int8_t hwSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms); +#if defined(MY_DEBUG) || defined(MY_SPECIAL_DEBUG) +/** + * CPU voltage + * @return CPU voltage in mV + */ +uint16_t hwCPUVoltage(); + +/** + * CPU frequency + * @return CPU frequency in 1/10Mhz + */ +uint16_t hwCPUFrequency(); + +/** + * Free memory + * @return free memory in bytes + */ +uint16_t hwFreeMem(); +#endif + #ifdef MY_DEBUG - void hwDebugPrint(const char *fmt, ... ); +void hwDebugPrint(const char *fmt, ... ); #endif /** diff --git a/core/MyHwATMega328.cpp b/core/MyHwATMega328.cpp index b0f9ead..5705590 100644 --- a/core/MyHwATMega328.cpp +++ b/core/MyHwATMega328.cpp @@ -161,6 +161,7 @@ int8_t hwSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mo return ret; } +#if defined(MY_DEBUG) || defined(MY_SPECIAL_DEBUG) uint16_t hwCPUVoltage() { // Measure Vcc against 1.1V Vref #if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) @@ -214,8 +215,7 @@ uint16_t hwFreeMem() { int v; return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); } - - +#endif #ifdef MY_DEBUG void hwDebugPrint(const char *fmt, ... ) { diff --git a/core/MyHwESP8266.cpp b/core/MyHwESP8266.cpp index 1760b68..5c48a0b 100644 --- a/core/MyHwESP8266.cpp +++ b/core/MyHwESP8266.cpp @@ -114,6 +114,7 @@ int8_t hwSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mo return MY_SLEEP_NOT_POSSIBLE; } +#if defined(MY_DEBUG) || defined(MY_SPECIAL_DEBUG) ADC_MODE(ADC_VCC); uint16_t hwCPUVoltage() { @@ -129,6 +130,7 @@ uint16_t hwCPUFrequency() { uint16_t hwFreeMem() { return ESP.getFreeHeap(); } +#endif #ifdef MY_DEBUG void hwDebugPrint(const char *fmt, ... ) { diff --git a/core/MyHwLinuxGeneric.cpp b/core/MyHwLinuxGeneric.cpp index 76e202f..d632d3f 100644 --- a/core/MyHwLinuxGeneric.cpp +++ b/core/MyHwLinuxGeneric.cpp @@ -20,109 +20,43 @@ #include "MyHwLinuxGeneric.h" #include -#include -#include -#include -#include #include +#include "SoftEeprom.h" #include "log.h" -static const char* CONFIG_FILE = MY_LINUX_CONFIG_FILE; -static const size_t _length = 1024; // ATMega328 has 1024 bytes -static uint8_t _config[_length]; - -bool CheckConfigFile() -{ - struct stat fileInfo; - - if (stat(CONFIG_FILE, &fileInfo) != 0) { - //File does not exist. Create it. - debug("Config file %s does not exist, creating new config file.\n", CONFIG_FILE); - std::ofstream myFile(CONFIG_FILE, std::ios::out | std::ios::binary); - if (!myFile) { - debug("Unable to create config file %s.\n", CONFIG_FILE); - return false; - } - myFile.write((const char*)_config, _length); - myFile.close(); - } else if (fileInfo.st_size < 0 || (size_t)fileInfo.st_size != _length) { - debug("Config file %s is not the correct size of %i. Please remove the file and a new one will be created.\n", CONFIG_FILE, _length); - return false; - } else { - //Read config into local memory. - std::ifstream myFile(CONFIG_FILE, std::ios::in | std::ios::binary); - if (!myFile) { - debug("Unable to open config to file %s for reading.\n", CONFIG_FILE); - return false; - } - myFile.read((char*)_config, _length); - myFile.close(); - } - - return true; -} +static SoftEeprom eeprom = SoftEeprom(MY_LINUX_CONFIG_FILE, 1024); // ATMega328 has 1024 bytes void hwInit() { - for (size_t i = 0; i < _length; i++) { - _config[i] = 0xFF; - } - - if (!CheckConfigFile()) { - exit(1); - } - - #ifdef MY_GATEWAY_SERIAL - MY_SERIALDEVICE.begin(MY_BAUD_RATE); - #ifdef MY_LINUX_SERIAL_GROUPNAME - if (!MY_SERIALDEVICE.setGroupPerm(MY_LINUX_SERIAL_GROUPNAME)) { - debug("Unable to change permission for serial port device.\n"); - exit(1); - } - #endif +#ifdef MY_GATEWAY_SERIAL + MY_SERIALDEVICE.begin(MY_BAUD_RATE); + #ifdef MY_LINUX_SERIAL_GROUPNAME + if (!MY_SERIALDEVICE.setGroupPerm(MY_LINUX_SERIAL_GROUPNAME)) { + mys_log(LOG_ERR, "Unable to change permission for serial port device.\n"); + exit(1); + } #endif +#endif } void hwReadConfigBlock(void* buf, void* addr, size_t length) { - unsigned long int offs = reinterpret_cast(addr); - - if (length && offs + length <= _length) { - memcpy(buf, _config+offs, length); - } + eeprom.readBlock(buf, addr, length); } void hwWriteConfigBlock(void* buf, void* addr, size_t length) { - unsigned long int offs = reinterpret_cast(addr); - - if (length && offs + length <= _length) { - memcpy(_config+offs, buf, length); - - std::ofstream myFile(CONFIG_FILE, std::ios::out | std::ios::in | std::ios::binary); - if (!myFile) { - debug("Unable to write config to file %s.\n", CONFIG_FILE); - return; - } - myFile.seekp(offs); - myFile.write((const char*)buf, length); - myFile.close(); - } + eeprom.writeBlock(buf, addr, length); } -uint8_t hwReadConfig(int adr) +uint8_t hwReadConfig(int addr) { - uint8_t value = 0xFF; - hwReadConfigBlock(&value, reinterpret_cast(adr), 1); - return value; + return eeprom.readByte(addr); } -void hwWriteConfig(int adr, uint8_t value) +void hwWriteConfig(int addr, uint8_t value) { - uint8_t curr = hwReadConfig(adr); - if (curr != value) { - hwWriteConfigBlock(&value, reinterpret_cast(adr), 1); - } + eeprom.writeByte(addr, value); } void hwRandomNumberInit() @@ -130,6 +64,11 @@ void hwRandomNumberInit() randomSeed(time(NULL)); } +unsigned long hwMillis() +{ + return millis(); +} + // Not supported! int8_t hwSleep(unsigned long ms) { @@ -160,6 +99,7 @@ int8_t hwSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mo return MY_SLEEP_NOT_POSSIBLE; } +#if defined(MY_DEBUG) || defined(MY_SPECIAL_DEBUG) uint16_t hwCPUVoltage() { // TODO: Not supported! @@ -177,6 +117,7 @@ uint16_t hwFreeMem() // TODO: Not supported! return 0; } +#endif #ifdef MY_DEBUG void hwDebugPrint(const char *fmt, ...) diff --git a/core/MyHwLinuxGeneric.h b/core/MyHwLinuxGeneric.h index 3d39203..ff8b008 100644 --- a/core/MyHwLinuxGeneric.h +++ b/core/MyHwLinuxGeneric.h @@ -33,21 +33,21 @@ #define MY_SERIALDEVICE Serial -// Define these as macros -#define hwDigitalWrite(__pin, __value) digitalWrite(__pin, __value) -#define hwDigitalRead(__pin) digitalRead(__pin) -#define hwPinMode(__pin, __value) pinMode(__pin, __value) - +// Define these as macros (do nothing) #define hwWatchdogReset() #define hwReboot() -#define hwMillis() millis() + +#define hwDigitalWrite(__pin, __value) _Pragma("GCC error \"Not supported on linux-generic\"") +#define hwDigitalRead(__pin) _Pragma("GCC error \"Not supported on linux-generic\"") +#define hwPinMode(__pin, __value) _Pragma("GCC error \"Not supported on linux-generic\"") void hwInit(); -void hwReadConfigBlock(void* buf, void* adr, size_t length); -void hwWriteConfigBlock(void* buf, void* adr, size_t length); -uint8_t hwReadConfig(int adr); -void hwWriteConfig(int adr, uint8_t value); -void hwRandomNumberInit(); +inline void hwReadConfigBlock(void* buf, void* addr, size_t length); +inline void hwWriteConfigBlock(void* buf, void* addr, size_t length); +inline uint8_t hwReadConfig(int addr); +inline void hwWriteConfig(int addr, uint8_t value); +inline void hwRandomNumberInit(); +inline unsigned long hwMillis(); #ifdef MY_RF24_IRQ_PIN static pthread_mutex_t hw_mutex = PTHREAD_MUTEX_INITIALIZER; diff --git a/core/MyHwRPi.cpp b/core/MyHwRPi.cpp new file mode 100644 index 0000000..de10021 --- /dev/null +++ b/core/MyHwRPi.cpp @@ -0,0 +1,151 @@ +/** + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Marcelo Aquino + * Copyright (C) 2016 Marcelo Aquino + * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors + * + * Documentation: http://www.mysensors.org + * Support Forum: http://forum.mysensors.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ + +#include "MyHwRPi.h" + +#include +#include +#include "SoftEeprom.h" +#include "log.h" + +static SoftEeprom eeprom = SoftEeprom(MY_LINUX_CONFIG_FILE, 1024); // ATMega328 has 1024 bytes + +void hwInit() +{ + if (!bcm2835_init()) { + mys_log(LOG_ERR, "Failed to initialized bcm2835.\n"); + exit(1); + } + +#ifdef MY_GATEWAY_SERIAL + MY_SERIALDEVICE.begin(MY_BAUD_RATE); + #ifdef MY_LINUX_SERIAL_GROUPNAME + if (!MY_SERIALDEVICE.setGroupPerm(MY_LINUX_SERIAL_GROUPNAME)) { + mys_log(LOG_ERR, "Unable to change permission for serial port device.\n"); + exit(1); + } + #endif +#endif +} + +void hwReadConfigBlock(void* buf, void* adr, size_t length) +{ + eeprom.readBlock(buf, adr, length); +} + +void hwWriteConfigBlock(void* buf, void* adr, size_t length) +{ + eeprom.writeBlock(buf, adr, length); +} + +uint8_t hwReadConfig(int adr) +{ + return eeprom.readByte(adr); +} + +void hwWriteConfig(int adr, uint8_t value) +{ + eeprom.writeByte(adr, value); +} + +void hwRandomNumberInit() +{ + randomSeed(time(NULL)); +} + +unsigned long hwMillis() +{ + return millis(); +} + +// Not supported! +int8_t hwSleep(unsigned long ms) +{ + (void)ms; + + return MY_SLEEP_NOT_POSSIBLE; +} + +// Not supported! +int8_t hwSleep(uint8_t interrupt, uint8_t mode, unsigned long ms) +{ + (void)interrupt; + (void)mode; + (void)ms; + + return MY_SLEEP_NOT_POSSIBLE; +} + +// Not supported! +int8_t hwSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms) +{ + (void)interrupt1; + (void)mode1; + (void)interrupt2; + (void)mode2; + (void)ms; + + return MY_SLEEP_NOT_POSSIBLE; +} + +#if defined(MY_DEBUG) || defined(MY_SPECIAL_DEBUG) + uint16_t hwCPUVoltage() + { + // TODO: Not supported! + return 0; + } + + uint16_t hwCPUFrequency() + { + // TODO: Not supported! + return 0; + } + + uint16_t hwFreeMem() + { + // TODO: Not supported! + return 0; + } +#endif + +#ifdef MY_DEBUG + void hwDebugPrint(const char *fmt, ...) + { + va_list args; + + va_start(args, fmt); + mys_log_v(LOG_DEBUG, fmt, args); + va_end(args); + } +#endif + +void hwDigitalWrite(uint8_t pin, uint8_t value) +{ + digitalWrite(pin, value); +} + +int hwDigitalRead(uint8_t pin) +{ + return digitalRead(pin); +} + +void hwPinMode(uint8_t pin, uint8_t mode) +{ + pinMode(pin, mode); +} diff --git a/core/MyHwRPi.h b/core/MyHwRPi.h new file mode 100644 index 0000000..f6bbb8e --- /dev/null +++ b/core/MyHwRPi.h @@ -0,0 +1,34 @@ +/** + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Marcelo Aquino + * Copyright (C) 2016 Marcelo Aquino + * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors + * + * Documentation: http://www.mysensors.org + * Support Forum: http://forum.mysensors.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ + +#ifndef MyHwRPi_h +#define MyHwRPi_h + +#include "MyHwLinuxGeneric.h" + +#undef hwDigitalWrite +inline void hwDigitalWrite(uint8_t, uint8_t); + +#undef hwDigitalRead +inline int hwDigitalRead(uint8_t); + +#undef hwPinMode +inline void hwPinMode(uint8_t, uint8_t); + +#endif diff --git a/core/MyHwSAMD.cpp b/core/MyHwSAMD.cpp index 9e11e5d..3d79a48 100644 --- a/core/MyHwSAMD.cpp +++ b/core/MyHwSAMD.cpp @@ -140,6 +140,7 @@ int8_t hwSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mo return MY_SLEEP_NOT_POSSIBLE; } +#if defined(MY_DEBUG) || defined(MY_SPECIAL_DEBUG) uint16_t hwCPUVoltage() { // TODO: Not supported! return 0; @@ -154,6 +155,7 @@ uint16_t hwFreeMem() { // TODO: Not supported! return 0; } +#endif #ifdef MY_DEBUG void hwDebugPrint(const char *fmt, ... ) { diff --git a/core/MyMainLinux.cpp b/core/MyMainLinux.cpp index 5d70e47..12687e8 100644 --- a/core/MyMainLinux.cpp +++ b/core/MyMainLinux.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -56,7 +57,7 @@ static int daemonize(void) /* Fork off the parent process */ pid = fork(); if (pid < 0) { - mys_log(LOG_ERR, "fork: %s", strerror(errno)); + mys_log(LOG_ERR, "fork: %s\n", strerror(errno)); return -1; } /* If we got a good PID, then we can exit the parent process. */ @@ -72,20 +73,26 @@ static int daemonize(void) /* Create a new SID for the child process */ sid = setsid(); if (sid < 0) { - mys_log(LOG_ERR, "setsid: %s", strerror(errno)); + mys_log(LOG_ERR, "setsid: %s\n", strerror(errno)); return -1; } /* Change the current working directory. This prevents the current directory from being locked; hence not being able to remove it. */ if ((chdir("/")) < 0) { - mys_log(LOG_ERR, "chdir(\"/\"): %s", strerror(errno)); + mys_log(LOG_ERR, "chdir(\"/\"): %s\n", strerror(errno)); return -1; } - freopen( "/dev/null", "r", stdin); - freopen( "/dev/null", "r", stdout); - freopen( "/dev/null", "r", stderr); + if (freopen( "/dev/null", "r", stdin) == NULL) { + mys_log(LOG_ERR, "freopen: %s\n", strerror(errno)); + } + if (freopen( "/dev/null", "r", stdout) == NULL) { + mys_log(LOG_ERR, "freopen: %s\n", strerror(errno)); + } + if (freopen( "/dev/null", "r", stderr) == NULL) { + mys_log(LOG_ERR, "freopen: %s\n", strerror(errno)); + } return 0; } diff --git a/drivers/Linux/Arduino.h b/drivers/Linux/Arduino.h index 41081d5..cb0cb72 100644 --- a/drivers/Linux/Arduino.h +++ b/drivers/Linux/Arduino.h @@ -18,6 +18,18 @@ using namespace rpi_util; #endif +#undef PSTR +#define PSTR(x) (x) +#undef F +#define F(x) (x) +#define PROGMEM __attribute__(( section(".progmem.data") )) +#define vsnprintf_P(...) vsnprintf( __VA_ARGS__ ) +#define snprintf_P(...) snprintf( __VA_ARGS__ ) +#define memcpy_P memcpy +#define pgm_read_byte(p) (*(p)) +#define pgm_read_dword(p) (*(p)) +#define pgm_read_byte_near(p) (*(p)) + #define PI 3.1415926535897932384626433832795 #define HALF_PI 1.5707963267948966192313216916398 #define TWO_PI 6.283185307179586476925286766559 diff --git a/drivers/Linux/EthernetServer.cpp b/drivers/Linux/EthernetServer.cpp index 9d0012c..7598bd1 100644 --- a/drivers/Linux/EthernetServer.cpp +++ b/drivers/Linux/EthernetServer.cpp @@ -84,7 +84,7 @@ void EthernetServer::begin(IPAddress address) } if (p == NULL) { - mys_log(LOG_ERR, "failed to bind\n"); + mys_log(LOG_ERR, "Failed to bind!\n"); freeaddrinfo(servinfo); return; } @@ -142,7 +142,7 @@ size_t EthernetServer::write(const uint8_t *buffer, size_t size) client.stop(); clients[i] = clients.back(); clients.pop_back(); - mys_log(LOG_DEBUG, "Client disconnected."); + mys_log(LOG_DEBUG, "Client disconnected.\n"); } } @@ -194,7 +194,7 @@ void EthernetServer::_accept() } } if (no_free_slots) { - mys_log(LOG_DEBUG, "Max number of ethernet clients reached."); + mys_log(LOG_DEBUG, "Max number of ethernet clients reached.\n"); return; } } diff --git a/drivers/Linux/Print.h b/drivers/Linux/Print.h index b63e7a1..e59900e 100644 --- a/drivers/Linux/Print.h +++ b/drivers/Linux/Print.h @@ -34,10 +34,19 @@ class Print { private: + int write_error; size_t printNumber(unsigned long n, uint8_t base); size_t printFloat(double number, uint8_t digits); + protected: + void setWriteError(int err = 1) { write_error = err; } + public: + Print() : write_error(0) {} + + int getWriteError() { return write_error; } + void clearWriteError() { setWriteError(0); } + virtual size_t write(uint8_t) = 0; size_t write(const char *str) { if (str == NULL) diff --git a/drivers/Linux/SerialPort.cpp b/drivers/Linux/SerialPort.cpp index 25082a1..bd16e10 100644 --- a/drivers/Linux/SerialPort.cpp +++ b/drivers/Linux/SerialPort.cpp @@ -194,18 +194,32 @@ int SerialPort::read() { unsigned char c; - ::read(sd, &c, 1); - return c; + int ret = ::read(sd, &c, 1); + if (ret < 0) { + mys_log(LOG_ERR, "Serial - read failed: %s\n", strerror(errno)); + } else if (ret == 1) { + return c; + } + + return -1; } size_t SerialPort::write(uint8_t b) { - return ::write(sd, &b, 1); + int ret = ::write(sd, &b, 1); + if (ret < 0) { + mys_log(LOG_ERR, "Serial - write failed: %s\n", strerror(errno)); + } + return ret; } size_t SerialPort::write(const uint8_t *buffer, size_t size) { - return ::write(sd, buffer, size); + int ret = ::write(sd, buffer, size); + if (ret < 0) { + mys_log(LOG_ERR, "Serial - write failed: %s\n", strerror(errno)); + } + return ret; } int SerialPort::peek() diff --git a/drivers/Linux/SoftEeprom.cpp b/drivers/Linux/SoftEeprom.cpp new file mode 100644 index 0000000..4e22d77 --- /dev/null +++ b/drivers/Linux/SoftEeprom.cpp @@ -0,0 +1,129 @@ +/** + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Marcelo Aquino + * Copyright (C) 2016 Marcelo Aquino + * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors + * + * Documentation: http://www.mysensors.org + * Support Forum: http://forum.mysensors.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include "log.h" +#include "SoftEeprom.h" + +SoftEeprom::SoftEeprom(const char *fileName, size_t length) +{ + struct stat fileInfo; + + _fileName = strdup(fileName); + if (_fileName == NULL) { + perror("Error: "); + exit(1); + } + + _length = length; + _values = new uint8_t[_length]; + for (size_t i = 0; i < _length; ++i) { + _values[i] = 0xFF; + } + + if (stat(_fileName, &fileInfo) != 0) { + //File does not exist. Create it. + mys_log(LOG_INFO, "Config file %s does not exist, creating new config file.\n", _fileName); + std::ofstream myFile(_fileName, std::ios::out | std::ios::binary); + if (!myFile) { + mys_log(LOG_ERR, "Unable to create config file %s.\n", _fileName); + exit(1); + } + myFile.write((const char*)_values, _length); + myFile.close(); + } else if (fileInfo.st_size < 0 || (size_t)fileInfo.st_size != _length) { + mys_log(LOG_ERR, "Config file %s is not the correct size of %zu. Please remove the file and a new one will be created.\n", _fileName, _length); + exit(1); + } else { + //Read config into local memory. + std::ifstream myFile(_fileName, std::ios::in | std::ios::binary); + if (!myFile) { + mys_log(LOG_ERR, "Unable to open config to file %s for reading.\n", _fileName); + exit(1); + } + myFile.read((char*)_values, _length); + myFile.close(); + } +} + +SoftEeprom::~SoftEeprom() +{ + delete[] _values; + free(_fileName); +} + +void SoftEeprom::readBlock(void* buf, void* addr, size_t length) +{ + static bool config_to_mem = false; + unsigned long int offs = reinterpret_cast(addr); + + if (!config_to_mem) { + //Read config into local memory. + std::ifstream myFile(_fileName, std::ios::in | std::ios::binary); + if (!myFile) { + mys_log(LOG_ERR, "Unable to open config to file %s for reading.\n", _fileName); + exit(1); + } + myFile.read((char*)_values, _length); + myFile.close(); + + config_to_mem = true; + } + + if (length && offs + length <= _length) { + memcpy(buf, _values+offs, length); + } +} + +void SoftEeprom::writeBlock(void* buf, void* addr, size_t length) +{ + unsigned long int offs = reinterpret_cast(addr); + + if (length && offs + length <= _length) { + memcpy(_values+offs, buf, length); + + std::ofstream myFile(_fileName, std::ios::out | std::ios::in | std::ios::binary); + if (!myFile) { + mys_log(LOG_ERR, "Unable to write config to file %s.\n", _fileName); + return; + } + myFile.seekp(offs); + myFile.write((const char*)buf, length); + myFile.close(); + } +} + +uint8_t SoftEeprom::readByte(int addr) +{ + uint8_t value = 0xFF; + readBlock(&value, reinterpret_cast(addr), 1); + return value; +} + +void SoftEeprom::writeByte(int addr, uint8_t value) +{ + uint8_t curr = readByte(addr); + if (curr != value) { + writeBlock(&value, reinterpret_cast(addr), 1); + } +} diff --git a/drivers/Linux/SoftEeprom.h b/drivers/Linux/SoftEeprom.h new file mode 100644 index 0000000..e623773 --- /dev/null +++ b/drivers/Linux/SoftEeprom.h @@ -0,0 +1,81 @@ +/** + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Marcelo Aquino + * Copyright (C) 2016 Marcelo Aquino + * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors + * + * Documentation: http://www.mysensors.org + * Support Forum: http://forum.mysensors.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ + +/** +* This a software emulation of EEPROM that uses a file for data storage. +* A copy of the eeprom values are also held in memory for faster reading. +*/ + +#ifndef SoftEeprom_h +#define SoftEeprom_h + +#include + +/** + * SoftEeprom class + */ +class SoftEeprom { + +private: + size_t _length; //!< @brief Eeprom max size. + char *_fileName; //!< @brief file where the eeprom values are stored. + uint8_t *_values; //!< @brief copy of the eeprom values held in memory for a faster reading. + +public: + /** + * @brief SoftEeprom constructor. + */ + SoftEeprom(const char *fileName, size_t length); + /** + * @brief SoftEeprom destructor. + */ + ~SoftEeprom(); + /** + * @brief Read a block of bytes from eeprom. + * + * @param buf buffer to copy to. + * @param addr eeprom address to read from. + * @param length number of bytes to read. + */ + void readBlock(void* buf, void* addr, size_t length); + /** + * @brief Write a block of bytes to eeprom. + * + * @param buf buffer to read from. + * @param addr eeprom address to write to. + * @param length number of bytes to write. + */ + void writeBlock(void* buf, void* addr, size_t length); + /** + * @brief Read a byte from eeprom. + * + * @param addr eeprom address to read from. + * @return the read byte. + */ + uint8_t readByte(int addr); + /** + * @brief Write a byte to eeprom. + * + * @param addr eeprom address to write to. + * @param value to write. + */ + void writeByte(int addr, uint8_t value); +}; + +#endif diff --git a/drivers/RPi/SPI.cpp b/drivers/RPi/SPI.cpp index 0b85422..997991d 100644 --- a/drivers/RPi/SPI.cpp +++ b/drivers/RPi/SPI.cpp @@ -31,16 +31,7 @@ SPIClass SPI = SPIClass(); uint8_t SPIClass::initialized = 0; -SPIClass::SPIClass() { - if (!bcm2835_init()) { - mys_log(LOG_ERR, "Failed to initialized bcm2835.\n"); - exit(1); - } -} - -SPIClass::~SPIClass() {} - -uint8_t SPIClass::isInitialized() { +uint8_t SPIClass::is_initialized() { return initialized; } diff --git a/drivers/RPi/SPI.h b/drivers/RPi/SPI.h index 84a4117..d83a540 100644 --- a/drivers/RPi/SPI.h +++ b/drivers/RPi/SPI.h @@ -18,6 +18,7 @@ * * Based on TMRh20 RF24 library, Copyright (c) 2015 Charles-Henri Hallard */ + #ifndef _SPI_H_ #define _SPI_H_ @@ -122,20 +123,12 @@ private: static uint8_t initialized; //!< @brief SPI initialized flag. public: - /** - * @brief SPIClass constructor. - */ - SPIClass(); - /** - * @brief SPIClass destructor. - */ - virtual ~SPIClass(); /** * @brief Checks if SPI was initialized. * * @return 0 if wasn't initialized, else 1 or more. */ - static uint8_t isInitialized(); + static uint8_t is_initialized(); /** * @brief Send and receive a byte. * diff --git a/drivers/RPi/Wire.cpp b/drivers/RPi/Wire.cpp new file mode 100644 index 0000000..bf78da1 --- /dev/null +++ b/drivers/RPi/Wire.cpp @@ -0,0 +1,213 @@ +/* + TwoWire.h - TWI/I2C library for Arduino & Wiring + Copyright (c) 2006 Nicholas Zambetti. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Modified 2012 by Todd Krein (todd@krein.org) to implement repeated starts + Modified December 2014 by Ivan Grokhotkov (ivan@esp8266.com) - esp8266 support + Modified April 2015 by Hrsto Gochkov (ficeto@ficeto.com) - alternative esp8266 support + Modified October 2016 by Marcelo Aquino for Raspberry Pi +*/ + +#include "Wire.h" +#include +#include +#include "bcm2835.h" +#include "log.h" + +static pthread_mutex_t i2cMutex = PTHREAD_MUTEX_INITIALIZER; + +uint8_t TwoWire::rxBuffer[BUFFER_LENGTH]; +uint8_t TwoWire::rxBufferIndex = 0; +uint8_t TwoWire::rxBufferLength = 0; + +uint8_t TwoWire::txAddress = 0; +uint8_t TwoWire::txBuffer[BUFFER_LENGTH]; +uint8_t TwoWire::txBufferIndex = 0; +uint8_t TwoWire::txBufferLength = 0; + +uint8_t TwoWire::transmitting = 0; + +void TwoWire::begin() +{ + if (!bcm2835_i2c_begin()) { + mys_log(LOG_ERR, "You need to be root to use I2C.\n"); + exit(1); + } +} + +void TwoWire::begin(uint8_t address) +{ + begin(); + bcm2835_i2c_setSlaveAddress(address); +} + +void TwoWire::begin(int address) +{ + begin(static_cast(address)); +} + +void TwoWire::end() +{ + bcm2835_i2c_end(); +} + +void TwoWire::setClock(uint32_t clock) +{ + bcm2835_i2c_set_baudrate(clock); +} + +void TwoWire::beginTransmission(uint8_t address) +{ + pthread_mutex_lock(&i2cMutex); + // indicate that we are transmitting + transmitting = 1; + // set address of targeted slave + txAddress = address; + // reset tx buffer iterator vars + txBufferIndex = 0; + txBufferLength = 0; +} + +void TwoWire::beginTransmission(int address) +{ + beginTransmission(static_cast(address)); +} + +uint8_t TwoWire::endTransmission(void) +{ + // transmit buffer + bcm2835_i2c_setSlaveAddress(txAddress); + uint8_t ret = bcm2835_i2c_write(reinterpret_cast(txBuffer), txBufferLength); + + // reset tx buffer iterator vars + txBufferIndex = 0; + txBufferLength = 0; + // indicate that we are done transmitting + transmitting = 0; + + pthread_mutex_unlock(&i2cMutex); + + if (ret == BCM2835_I2C_REASON_OK) { + return 0; // success + } else if (ret == BCM2835_I2C_REASON_ERROR_NACK) { + return 3; // error: data send, nack received + } + return 4; // other error +} + +size_t TwoWire::requestFrom(uint8_t address, size_t quantity) +{ + // clamp to buffer length + if (quantity > BUFFER_LENGTH) { + quantity = BUFFER_LENGTH; + } + + rxBufferIndex = 0; + rxBufferLength = 0; + + bcm2835_i2c_setSlaveAddress(address); + uint8_t ret = bcm2835_i2c_read(reinterpret_cast(rxBuffer), quantity); + if (ret == BCM2835_I2C_REASON_OK) { + rxBufferLength = quantity; + } + + return rxBufferLength; +} + +uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity) +{ + return requestFrom(address, static_cast(quantity)); +} + +uint8_t TwoWire::requestFrom(int address, int quantity) +{ + return requestFrom(static_cast(address), static_cast(quantity)); +} + +size_t TwoWire::write(uint8_t data) +{ + if (transmitting) { + // in master transmitter mode + // don't bother if buffer is full + if (txBufferLength >= BUFFER_LENGTH) { + setWriteError(); + return 0; + } + // put byte in tx buffer + txBuffer[txBufferIndex] = data; + ++txBufferIndex; + // update amount in buffer + txBufferLength = txBufferIndex; + + return 1; + } else { + return write(&data, 1); + } +} + +size_t TwoWire::write(const uint8_t *data, size_t quantity) +{ + if (transmitting) { + // in master transmitter mode + for (size_t i = 0; i < quantity; ++i) { + write(data[i]); + } + } else { + uint8_t rc = bcm2835_i2c_write(reinterpret_cast(data), quantity); + if (rc != BCM2835_I2C_REASON_OK) { + return 0; + } + } + + return quantity; +} + +int TwoWire::available() +{ + return rxBufferLength - rxBufferIndex; +} + +int TwoWire::read() +{ + int value = -1; + + if (rxBufferIndex < rxBufferLength) { + value = rxBuffer[rxBufferIndex]; + ++rxBufferIndex; + } + + return value; +} + +int TwoWire::peek() +{ + if (rxBufferIndex < rxBufferLength) { + return rxBuffer[rxBufferIndex]; + } + + return -1; +} + +void TwoWire::flush() +{ + rxBufferIndex = 0; + rxBufferLength = 0; + txBufferIndex = 0; + txBufferLength = 0; +} + +TwoWire Wire = TwoWire(); diff --git a/drivers/RPi/Wire.h b/drivers/RPi/Wire.h new file mode 100644 index 0000000..a82625b --- /dev/null +++ b/drivers/RPi/Wire.h @@ -0,0 +1,80 @@ +/* + TwoWire.h - TWI/I2C library for Arduino & Wiring + Copyright (c) 2006 Nicholas Zambetti. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Modified 2012 by Todd Krein (todd@krein.org) to implement repeated starts + Modified December 2014 by Ivan Grokhotkov (ivan@esp8266.com) - esp8266 support + Modified April 2015 by Hrsto Gochkov (ficeto@ficeto.com) - alternative esp8266 support + Modified October 2016 by Marcelo Aquino for Raspberry Pi +*/ + +#ifndef Wire_h +#define Wire_h + +#if !DOXYGEN +#include +#include "Stream.h" + +#define BUFFER_LENGTH 32 + +class TwoWire : public Stream { + +private: + static uint8_t rxBuffer[]; + static uint8_t rxBufferIndex; + static uint8_t rxBufferLength; + + static uint8_t txAddress; + static uint8_t txBuffer[]; + static uint8_t txBufferIndex; + static uint8_t txBufferLength; + + static uint8_t transmitting; + +public: + void begin(); + void begin(uint8_t address); + void begin(int address); + void end(); + void setClock(uint32_t clock); + + void beginTransmission(uint8_t address); + void beginTransmission(int address); + uint8_t endTransmission(void); + + size_t requestFrom(uint8_t address, size_t size); + uint8_t requestFrom(uint8_t address, uint8_t quantity); + uint8_t requestFrom(int address, int quantity); + + size_t write(uint8_t data); + size_t write(const uint8_t *data, size_t quantity); + int available(); + int read(); + int peek(); + void flush(); + + inline size_t write(unsigned long n) { return write((uint8_t)n); } + inline size_t write(long n) { return write((uint8_t)n); } + inline size_t write(unsigned int n) { return write((uint8_t)n); } + inline size_t write(int n) { return write((uint8_t)n); } + using Print::write; +}; + +extern TwoWire Wire; + +#endif +#endif diff --git a/drivers/RPi/rpi_util.cpp b/drivers/RPi/rpi_util.cpp index 5954fcc..47f65bb 100644 --- a/drivers/RPi/rpi_util.cpp +++ b/drivers/RPi/rpi_util.cpp @@ -167,7 +167,7 @@ void rpi_util::pinMode(uint8_t physPin, uint8_t mode) { return; } // Check if SPI is in use and target pin is related to SPI - if (SPIClass::isInitialized() && gpioPin >= RPI_GPIO_P1_26 && gpioPin <= RPI_GPIO_P1_23) { + if (SPIClass::is_initialized() && gpioPin >= RPI_GPIO_P1_26 && gpioPin <= RPI_GPIO_P1_23) { return; } else { bcm2835_gpio_fsel(gpioPin, mode); @@ -181,7 +181,7 @@ void rpi_util::digitalWrite(uint8_t physPin, uint8_t value) { return; } // Check if SPI is in use and target pin is related to SPI - if (SPIClass::isInitialized() && gpioPin >= RPI_GPIO_P1_26 && gpioPin <= RPI_GPIO_P1_23) { + if (SPIClass::is_initialized() && gpioPin >= RPI_GPIO_P1_26 && gpioPin <= RPI_GPIO_P1_23) { if (value == LOW && (gpioPin == RPI_GPIO_P1_24 || gpioPin == RPI_GPIO_P1_26)) { SPI.chipSelect(gpioPin); } @@ -199,7 +199,7 @@ uint8_t rpi_util::digitalRead(uint8_t physPin) { return 0; } // Check if SPI is in use and target pin is related to SPI - if (SPIClass::isInitialized() && gpioPin >= RPI_GPIO_P1_26 && gpioPin <= RPI_GPIO_P1_23) { + if (SPIClass::is_initialized() && gpioPin >= RPI_GPIO_P1_26 && gpioPin <= RPI_GPIO_P1_23) { return 0; } else { return bcm2835_gpio_lev(gpioPin);