pull/3/head
Jean-François Milants 2022-01-01 15:43:07 +01:00
commit a1ccddba7a
24 zmienionych plików z 795 dodań i 0 usunięć

3
.gitmodules vendored 100644
Wyświetl plik

@ -0,0 +1,3 @@
[submodule "libs/sx126x_driver"]
path = libs/sx126x_driver
url = git@github.com:YukiWorkshop/sx126x_driver.git

12
CMakeLists.txt 100644
Wyświetl plik

@ -0,0 +1,12 @@
project(pinedio-lora-driver-project)
cmake_minimum_required(VERSION 3.21)
set(CMAKE_CXX_STANDARD 20)
option(BUILD_FOR_PINEPHONE "Build PinePhone support" FALSE)
option(BUILD_FOR_USB "Build support for the USB adapter" FALSE)
add_subdirectory(src)
add_subdirectory(libs)
add_subdirectory(apps)

98
README.md 100644
Wyświetl plik

@ -0,0 +1,98 @@
# PineDio LoRa drivers
PineDio is the new LoRa-based product range at [Pine64](https://pine64.org). This project implements a C++ driver for the [LoRa USB adapter](https://wiki.pine64.org/wiki/Pinedio#USB_adapter) and the [Pinephone backplate](https://wiki.pine64.org/wiki/Pinedio#Pinephone_backplate).
## Build
Build this project like any CMake project:
- Clone the project from Git
- Initialize/update the Git submodules
- Create a build directory and invoke CMake with command line parameters to choose the target hardware
- Build
- Enjoy
```bash
git clone xxx
cd pinedio-lora-driver
git submodule update --init
mkdir build && cd build
cmake -DBUILD_FOR_PINEPHONE=0 -DBUILD_FOR_USB=1 ..
make -j
```
2 CMake options are available :
- `BUILD_FOR_PINEPHONE` : build the driver for the pinephone backplate and the `pinephone-communicator` test app.
- `BUILD_FOR_USB` : build the driver for the USB adapter and the `usb-communicator` test app.
## Dependencies
You need to install and load [this driver](https://github.com/rogerjames99/spi-ch341-usb) to be able to use the USB adapter. This spi-ch341-usb is a driver that configures the CH341 chip (USB <-> serial converter) mounted on the USB adapter and exposes it as a `spidev` (userland SPI API) device.
## Run the test applications
The test applications (one for the Pinephone, one for the USB adapter) provide a very basic chat application : it prints all the data received on the LoRa radio and allows sending messages to other LoRa devices.
The *communicator* test app are available in the directory `apps/` :
```bash
./apps/pinephone-communicator/pinephone-communicator
```
or
```bash
./apps/usb-communicator/usb-communicator
```
## Demo
![PineDio communbicator demo](doc/pinedio-communicator-demo2.jpg)
[Here's a video showcasing this project](https://video.codingfield.com/videos/watch/5a68be9e-01a2-43aa-af60-595366619553).
## Overview of the project structure
The goal of this implementation is to provide a common interface for both devices (Pinephone add-on and USB adapter) as they are both based on the same LoRa module (Semtech SX1262).
The implementation specific for the Pinephone is located in the class `PineDio::LoRa::PinephoneBackplate`. It initializes the I²C port to communicate with the LoRa backplate. The LoRa backplate PCB is based on the SX1262 and uses a simple ATtiny84 MCU to convert the I²C bus from the Pinephone into the SPI bus for the SX1262 (it run [this firmware](https://github.com/zschroeder6212/tiny-i2c-spi)).
The implementation specific for the USB adapter is located in the class `PineDio::LoRa::UsbAdapter`. It uses the `spidev` API to access the SPI bus from the Linux userland.
Both classes derive from `SX126x`, the generic driver from [this repo](https://github.com/YukiWorkshop/sx126x_driver).
The generic abstraction is provided by the class `PineDio::LoRa::PinedioLoraRadio`, which takes a reference to a `SX126x`-derived object (which can be either `PinephoneBackplate` or `UsbAdapter`).
At the application level, all you have to do is instantiate the driver for the targeted device and configure it if necessary, pass it to an instance of PineDioLoraRadio and then use this instance to send and receive data:
```c++
PineDio::LoRa::PinephoneBackplate pinephoneBackplate("/dev/i2c-2");
pinephoneBackplate.Initialize();
PineDio::LoRa::PinedioLoraRadio radio(pinephoneBackplate);
radio.Send({...});
auto data = radio.Receive();
```
or
```c++
PineDio::LoRa::UsbAdapter usbAdapter;
PineDio::LoRa::PinedioLoraRadio radio(usbAdapter);
radio.Send({...});
auto data = radio.Receive();
```
Linking with this driver in an external application should be as easy as linking (`target_link_libraries`) with `pinedio-lora-driver` and `pinedio-lora-driver-usb` or `pinedio-lora-driver-pinephone`. See *communicator* demo applications in the folder `apps`.
## Current state of this project
This project is at its very beginning. There are still a lot of unnecessary `sleep()`, and most of the configuration is hard-coded.
The test application (usb-communicator and pinephone-communicator) are also in a very early stage of development. They are already able to print all data received on the LoRa radio and to send messages from the standard input.
### TODO
- [ ] Remove unnecessary `sleep()`
- [ ] Remove hard-coded configuration in the driver, add API to configure the driver
- [ ] Communicator test app: configure the driver parameters (frequency, LoRa settings,..) via command line options and/or configuration file
- [ ] Improve the implementation to provide synchronous and asynchronous API
- [ ] Probably many other things
## License
This project is released under the terms of the **LGPLv3 license**.
## Acknowledgements
This project is based on [this C++ SX126x driver](https://github.com/YukiWorkshop/sx126x_driver) from [YukiWorkshop](https://github.com/YukiWorkshop).
The implementation of the driver for the USB adapter is possible thanks to [rogerjames99's](https://github.com/rogerjames99) [fork](https://github.com/rogerjames99/spi-ch341-usb) of the [spi-ch341-usb module](https://github.com/gschorcht/spi-ch341-usb) from [Gunar Schorcht](https://github.com/gschorcht).
The LoRa add-on board works thanks to a ATtiny MCU running [this firmware](https://github.com/zschroeder6212/tiny-i2c-spi) from [Zachary Schroeder](https://github.com/zschroeder6212).
This project wouldn't be possible without the good work from [Pine64](https://pine64.org), which works hands in hands with the open source community to design and build nice and open devices!

Wyświetl plik

@ -0,0 +1,3 @@
add_subdirectory(communicator)
add_subdirectory(pinephone-communicator)
add_subdirectory(usb-communicator)

Wyświetl plik

@ -0,0 +1,15 @@
project(communicator)
cmake_minimum_required(VERSION 3.21)
add_library(communicator
src/Communicator.cpp
include/PineDio/LoRa/Communicator.h)
target_link_libraries(communicator
pinedio-lora-driver
pthread
)
target_include_directories(communicator PUBLIC
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/apps/communicator/include>
)

Wyświetl plik

@ -0,0 +1,20 @@
#pragma once
#include "PineDio/LoRa/PinedioLoraRadio.h"
namespace PineDio::LoRa {
class PinedioLoraRadio;
class Communicator {
public:
explicit Communicator(PineDio::LoRa::PinedioLoraRadio &radio);
void Run();
private:
PineDio::LoRa::PinedioLoraRadio &radio;
std::atomic<bool> running{false};
std::unique_ptr<std::thread> receiveTask;
void Receive();
};
}

Wyświetl plik

@ -0,0 +1,57 @@
#include <PineDio/LoRa/Communicator.h>
#include <iostream>
#include <future>
#include <algorithm>
using namespace PineDio::LoRa;
namespace {
std::string GetString() {
std::string input;
std::getline(std::cin, input);
return input;
}
}
Communicator::Communicator(PineDio::LoRa::PinedioLoraRadio &radio) : radio{radio} {
radio.Initialize();
receiveTask.reset(new std::thread([this](){Receive();}));
}
void Communicator::Run() {
running = true;
std::future<std::string> futureString = std::async(std::launch::async, GetString);
while(running) {
if(futureString.wait_for(std::chrono::milliseconds {0}) == std::future_status::ready) {
auto msgStr = futureString.get();
std::vector<uint8_t> msg;
for(auto c : msgStr)
msg.push_back(c);
msg.push_back('\0');
radio.Send(msg);
futureString = std::async(std::launch::async, GetString);
}
std::this_thread::sleep_for(std::chrono::milliseconds{100});
}
}
void Communicator::Receive() {
while(true){
auto data = radio.Receive();
std::cout << "Data received on LoRa radio : " << std::endl;
std::cout << "\tHEX: ";
for(auto d : data) {
int dd = d;
std::cout << std::hex << "0x" << dd << " ";
}
std::cout << std::endl << "\tSTR: ";
for(auto d : data) {
std::cout << d;
}
std::cout << std::endl;
}
}

Wyświetl plik

@ -0,0 +1,10 @@
project(pinephone-communicator)
cmake_minimum_required(VERSION 3.21)
add_executable(pinephone-communicator
main.cpp)
target_link_libraries(pinephone-communicator
communicator
pinedio-lora-driver-pinephone
)

Wyświetl plik

@ -0,0 +1,19 @@
#include <PineDio/LoRa/PinedioLoraRadio.h>
#include <PineDio/LoRa/PinephoneBackplate.h>
#include <PineDio/LoRa/Communicator.h>
#include <PineDio/LoRa/Exceptions.h>
#include <iostream>
int main() {
try {
PineDio::LoRa::PinephoneBackplate pinephoneBackplate("/dev/i2c-2");
pinephoneBackplate.Initialize();
PineDio::LoRa::PinedioLoraRadio radio(pinephoneBackplate);
PineDio::LoRa::Communicator communicator(radio);
communicator.Run();
} catch(const PineDio::LoRa::InitializationException& ex) {
std::cerr << "Initialization error : " << ex.what() << std::endl;
}
return 0;
}

Wyświetl plik

@ -0,0 +1,10 @@
project(usb-communicator)
cmake_minimum_required(VERSION 3.21)
add_executable(usb-communicator
main.cpp)
target_link_libraries(usb-communicator
communicator
pinedio-lora-driver-usb
)

Wyświetl plik

@ -0,0 +1,17 @@
#include <PineDio/LoRa/Communicator.h>
#include <iostream>
#include "PineDio/LoRa/UsbAdapter.h"
#include "PineDio/LoRa/Exceptions.h"
int main() {
try {
PineDio::LoRa::UsbAdapter usbAdapter;
PineDio::LoRa::PinedioLoraRadio radio(usbAdapter);
PineDio::LoRa::Communicator communicator(radio);
communicator.Run();
} catch(const PineDio::LoRa::InitializationException& ex) {
std::cerr << "Initialization error : " << ex.what() << std::endl;
}
return 0;
}

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 148 KiB

Wyświetl plik

@ -0,0 +1,9 @@
#pragma once
namespace PineDio::LoRa {
class InitializationException : public std::runtime_error {
public:
explicit InitializationException(const std::string& message) : runtime_error(message) {}
};
}

Wyświetl plik

@ -0,0 +1,27 @@
#pragma once
#include <string>
#include <memory>
#include <sx126x_driver/SX126x.hpp>
namespace PineDio {
namespace LoRa {
class PinedioLoraRadio {
public:
explicit PinedioLoraRadio(SX126x& radio);
virtual void Initialize();
virtual void Send(std::vector<uint8_t> data);
virtual std::vector<uint8_t> Receive();
private:
SX126x& radio;
bool dataReceived {false};
std::vector<uint8_t> receivedBuffer;
bool dataToSend {false};
std::vector<uint8_t> transmitBuffer;
void OnDataReceived();
};
}
}

Wyświetl plik

@ -0,0 +1,22 @@
#pragma once
#include <PineDio/LoRa/PinedioLoraRadio.h>
#include <memory>
namespace PineDio::LoRa {
class PinephoneBackplate : public SX126x {
public:
explicit PinephoneBackplate(const std::string& i2cFilename);
void Initialize();
private:
uint8_t HalGpioRead(GpioPinFunction_t func) override;
void HalGpioWrite(GpioPinFunction_t func, uint8_t value) override;
void HalSpiTransfer(uint8_t *buffer_in, const uint8_t *buffer_out, uint16_t size) override;
int handle;
void RxDone();
void SyncI2CBuffer();
};
}

Wyświetl plik

@ -0,0 +1,21 @@
#pragma once
#include <PineDio/LoRa/PinedioLoraRadio.h>
#include <memory>
namespace PineDio::LoRa {
class UsbAdapter : public SX126x {
public:
UsbAdapter();
private:
uint8_t HalGpioRead(GpioPinFunction_t func) override;
void HalGpioWrite(GpioPinFunction_t func, uint8_t value) override;
void HalSpiTransfer(uint8_t *buffer_in, const uint8_t *buffer_out, uint16_t size) override;
int handle;
void RxDone();
};
}

Wyświetl plik

@ -0,0 +1,10 @@
cmake_minimum_required(VERSION 3.17)
project(sx126x_driver)
set(CMAKE_CXX_STANDARD 14)
add_library(sx126x_driver sx126x_driver/SX126x.cpp sx126x_driver/SX126x.hpp)
target_include_directories(sx126x_driver PUBLIC
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/libs>
)

@ -0,0 +1 @@
Subproject commit 42a75dfbeb6a4df8bb4e77d10d135fba878b88d4

31
src/CMakeLists.txt 100644
Wyświetl plik

@ -0,0 +1,31 @@
project(pinedio-lora-driver CXX)
add_library(pinedio-lora-driver
../include/PineDio/LoRa/PinedioLoraRadio.h
../include/PineDio/LoRa/Exceptions.h
PinedioLoraRadio.cpp
)
target_link_libraries(pinedio-lora-driver PUBLIC
sx126x_driver
)
target_include_directories(pinedio-lora-driver PUBLIC
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/include/>
)
set(AT_LEAST_ONE_BUILD_ENABLED FALSE)
if(${BUILD_FOR_PINEPHONE})
add_subdirectory(pinephone)
set(AT_LEAST_ONE_BUILD_ENABLED TRUE)
endif()
if(${BUILD_FOR_USB})
add_subdirectory(usb-adapter)
set(AT_LEAST_ONE_BUILD_ENABLED TRUE)
endif()
if(NOT ${AT_LEAST_ONE_BUILD_ENABLED})
message(FATAL_ERROR "Please select at least one build (-DBUILD_FOR_PINEPHONE or -DBUILD_FOR_USB)")
endif()

Wyświetl plik

@ -0,0 +1,109 @@
#include <PineDio/LoRa/PinedioLoraRadio.h>
#include <sx126x_driver/SX126x.hpp>
#include <iostream>
#include <unistd.h>
using namespace PineDio::LoRa;
PinedioLoraRadio::PinedioLoraRadio(SX126x &radio) : radio{radio} {}
void PinedioLoraRadio::Initialize() {
std::cout << "[PinedioLoraRadio] Initialize()" << std::endl;
radio.callbacks.rxDone = [this](){OnDataReceived();};
radio.callbacks.txDone = [this](){
std::cout << "TX DONE" << std::endl;
usleep(10000);
radio.SetRx(0xffffffff);
usleep(10000);
};
radio.SetDeviceType(SX126x::SX1262);
radio.Init();
radio.SetDio2AsRfSwitchCtrl(true);
radio.SetStandby(SX126x::RadioStandbyModes_t::STDBY_RC);
radio.SetRegulatorMode(SX126x::RadioRegulatorMode_t::USE_DCDC);
radio.SetBufferBaseAddresses(0,127);
radio.SetTxParams(22, SX126x::RadioRampTimes_t::RADIO_RAMP_3400_US);
radio.SetDioIrqParams(0xffff, 0x0001, 0x0000, 0x0000);
radio.SetRfFrequency(868000000);
radio.SetPacketType(SX126x::RadioPacketTypes_t::PACKET_TYPE_LORA);
radio.SetStopRxTimerOnPreambleDetect(false);
SX126x::ModulationParams_t modulationParams;
modulationParams.PacketType = SX126x::RadioPacketTypes_t::PACKET_TYPE_LORA;
modulationParams.Params.LoRa.LowDatarateOptimize = 0;
modulationParams.Params.LoRa.Bandwidth = SX126x::RadioLoRaBandwidths_t::LORA_BW_500;
modulationParams.Params.LoRa.CodingRate = SX126x::RadioLoRaCodingRates_t::LORA_CR_4_5;
modulationParams.Params.LoRa.SpreadingFactor = SX126x::RadioLoRaSpreadingFactors_t::LORA_SF12;
radio.SetModulationParams(modulationParams);
static char* message = "Hello, I'm a Pinephone!";
auto s = strlen(message);
SX126x::PacketParams_t packetParams;
packetParams.PacketType = SX126x::RadioPacketTypes_t::PACKET_TYPE_LORA;;
packetParams.Params.LoRa.HeaderType = SX126x::RadioLoRaPacketLengthsMode_t::LORA_PACKET_VARIABLE_LENGTH;
packetParams.Params.LoRa.InvertIQ = SX126x::RadioLoRaIQModes_t::LORA_IQ_INVERTED;
packetParams.Params.LoRa.CrcMode = SX126x::RadioLoRaCrcModes_t::LORA_CRC_ON;
packetParams.Params.LoRa.PayloadLength = s;
packetParams.Params.LoRa.PreambleLength = 8;
radio.SetPacketParams(packetParams);
radio.ClearIrqStatus(0xffff);
radio.SetRx(0xffffffff);
}
void PinedioLoraRadio::Send(const std::vector<uint8_t> data) {
std::cout << "[PinedioLoraRadio] Send()" << std::endl;
dataToSend = true;
transmitBuffer = data; //copy
}
std::vector<uint8_t> PinedioLoraRadio::Receive() {
std::cout << "[PinedioLoraRadio] Receive()" << std::endl;
bool running = true;
while(running) {
radio.ProcessIrqs();
if(dataToSend) {
std::cout << "SEND " << std::to_string(transmitBuffer.size()) << std::endl;
SX126x::PacketParams_t packetParams;
packetParams.PacketType = SX126x::RadioPacketTypes_t::PACKET_TYPE_LORA;;
packetParams.Params.LoRa.HeaderType = SX126x::RadioLoRaPacketLengthsMode_t::LORA_PACKET_VARIABLE_LENGTH;
packetParams.Params.LoRa.InvertIQ = SX126x::RadioLoRaIQModes_t::LORA_IQ_INVERTED;
packetParams.Params.LoRa.CrcMode = SX126x::RadioLoRaCrcModes_t::LORA_CRC_ON;
packetParams.Params.LoRa.PayloadLength = transmitBuffer.size();
packetParams.Params.LoRa.PreambleLength = 8;
radio.SetPacketParams(packetParams);
radio.SendPayload(transmitBuffer.data(), transmitBuffer.size(), 0xffffffff);
usleep(3000000);
dataToSend = false;
}
if(dataReceived) {
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
dataReceived = false;
return receivedBuffer; //copy
}
void PinedioLoraRadio::OnDataReceived() {
uint8_t size = 0;
uint8_t offset = 0;
radio.GetRxBufferStatus(&size, &offset);
receivedBuffer.clear();
receivedBuffer.resize(size);
radio.GetPayload(receivedBuffer.data(), &size, size);
dataReceived = true;
}

Wyświetl plik

@ -0,0 +1,23 @@
project(pinedio-lora-driver-pinephone CXX)
set(PINEDIO_LORA_DRIVER_SOURCE_FILES
PinephoneBackplate.cpp
)
set(PINEDIO_LORA_DRIVER_PUBLIC_HEADERS
../../include/PineDio/LoRa/PinedioLoraRadio.h
../../include/PineDio/LoRa/PinephoneBackplate.h
)
add_library(pinedio-lora-driver-pinephone
${PINEDIO_LORA_DRIVER_PUBLIC_HEADERS}
${PINEDIO_LORA_DRIVER_SOURCE_FILES}
)
target_link_libraries(pinedio-lora-driver-pinephone PUBLIC
pinedio-lora-driver
)
target_include_directories(pinedio-lora-driver-pinephone PUBLIC
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/include/>
)

Wyświetl plik

@ -0,0 +1,142 @@
#include <PineDio/LoRa/PinephoneBackplate.h>
#include <PineDio/LoRa/Exceptions.h>
#include <iostream>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
using namespace PineDio::LoRa;
PinephoneBackplate::PinephoneBackplate(const std::string &i2cFilename) {
std::cout << "[PinephoneBackplate] ctor()" << std::endl;
if ((handle = open(i2cFilename.c_str(), O_RDWR)) < 0) {
/* ERROR HANDLING: you can check errno to see what went wrong */
perror("Failed to open the i2c bus");
exit(1);
}
int addr = 0x28; // The I2C address of the ADC
if (ioctl(handle, I2C_SLAVE, addr) < 0) {
printf("Failed to acquire bus access and/or talk to slave.\n");
/* ERROR HANDLING; you can check errno to see what went wrong */
exit(1);
}
}
void PinephoneBackplate::Initialize() {
SyncI2CBuffer();
/*
uint8_t bufferWrite = 4;
if (write(handle, &bufferWrite,1) != 1) {
printf("Failed to write to the i2c bus.\n");
printf("\n\n");
exit(1);
}
*/
}
uint8_t PinephoneBackplate::HalGpioRead(SX126x::GpioPinFunction_t func) {
usleep(10000);
return 0;
}
void PinephoneBackplate::HalGpioWrite(SX126x::GpioPinFunction_t func, uint8_t value) {
usleep(10000);
}
void PinephoneBackplate::HalSpiTransfer(uint8_t *buffer_in,
const uint8_t *buffer_out,
uint16_t size) {
//std::cout << "Start transfer " << size +1<< " bytes\n\t";
uint8_t bufferWrite[size+1];
uint8_t bufferRead[size+1];
bufferWrite[0] = 1; // CMD TRANSMIT
std::memcpy(bufferWrite+1, buffer_out, size);
//for(int i = 0; i< size+1; i++) {
// printf("%x ", bufferWrite[i]);
// }
// std::cout << std::endl;
if (write(handle, bufferWrite,size+1) != size+1) {
/* ERROR HANDLING: i2c transaction failed */
printf("Failed to write to the i2c bus.\n");
printf("\n\n");
return;
}
//std::cout << "\t Write OK\n";
//std::cout << "Reading " << size << " bytes from the bus \n";
for(int i = 0; i < size; i++) {
if (read(handle, buffer_in + i, 1) != 1) {
/* ERROR HANDLING: i2c transaction failed */
printf("Failed to read from the i2c bus.\n");
printf("\n\n");
return;
}
}
}
void PinephoneBackplate::SyncI2CBuffer() {
std::cout << "[PinephoneBackplate] Sync buffer..." << std::endl;
std::cout << "\tInit LoRa module..." << std::endl;
// Set radio to idle
uint8_t dd[3] = {0x01, 0x80, 0x00};
write(handle, &dd, 3);
usleep(1000);
// Set RX/TX index to 0
uint8_t d[4] = {0x01, 0x8f, 0x00, 0x00};
write(handle, &d, 4);
usleep(1000);
std::cout << "\tSend verification data..." << std::endl;
uint8_t cmd1[] = {0x01, 0x0E, 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0xAA, 0x55, 0x00, 0xFF};
write(handle, cmd1, 12);
usleep(1000);
uint8_t cmd2[] = {0x01, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
usleep(1000);
write(handle, cmd2, 13);
usleep(1000);
std::cout << "\tSync'ing..." << std::endl;
bool done = false;
bool sequenceStarted = false;
size_t sequenceIndex = 0;
std::array<uint8_t, 9> sequence = {0x10, 0x20, 0x30, 0x40, 0x50, 0xAA, 0x55, 0x00, 0xFF};
int count = 0;
while(!done && count < 256) {
int d = 0;
read(handle, &d, 1);
if(!sequenceStarted) {
for(int i = 0; i < 9; i++) {
if(d == sequence[i]){
sequenceStarted = true;
sequenceIndex = i;
}
}
} else {
if(d == sequence[sequenceIndex+1]) {
sequenceIndex++;
if(sequenceIndex == 8) {
done = true;
std::cout << "\tSync done after " << count << " bytes!" << std::endl;
}
} else {
sequenceStarted = false;
}
}
count ++;
}
if(count >= 256) {
throw InitializationException("Internal buffer synchronization failed");
}
}

Wyświetl plik

@ -0,0 +1,23 @@
project(pinedio-lora-driver-usb CXX)
set(PINEDIO_LORA_DRIVER_SOURCE_FILES
UsbAdapter.cpp
)
set(PINEDIO_LORA_DRIVER_PUBLIC_HEADERS
../../include/PineDio/LoRa/PinedioLoraRadio.h
../../include/PineDio/LoRa/UsbAdapter.h
)
add_library(pinedio-lora-driver-usb
${PINEDIO_LORA_DRIVER_PUBLIC_HEADERS}
${PINEDIO_LORA_DRIVER_SOURCE_FILES}
)
target_link_libraries(pinedio-lora-driver-usb
sx126x_driver
)
target_include_directories(pinedio-lora-driver-usb PUBLIC
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/include/>
)

Wyświetl plik

@ -0,0 +1,113 @@
#include <PineDio/LoRa/UsbAdapter.h>
#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>
using namespace PineDio::LoRa;
UsbAdapter::UsbAdapter() {
std::cout << "[UsbAdapter] ctor()" << std::endl;
// TODO remove code duplication for exporting GPIO
if(access("/sys/class/gpio/ini", F_OK) != 0 ) {
int fd;
if ((fd = open("/sys/class/gpio/export", O_WRONLY)) == -1) {
perror("Error while opening GPIO export file");
// TODO ERROR
}
if (write(fd, "240", 3) == -1) { // TODO how to find the GPIO number programatically?
perror("Error while exporting GPIO \"ini\"");
// TODO ERROR
}
close(fd);
}
if(access("/sys/class/gpio/slct", F_OK) != 0 ) {
int fd;
if ((fd = open("/sys/class/gpio/export", O_WRONLY)) == -1) {
perror("Error while opening GPIO export file");
// TODO ERROR
}
if (write(fd, "244", 3) == -1) { // TODO how to find the GPIO number programatically?
perror("Error while exporting GPIO \"sclt\"");
// TODO ERROR
}
close(fd);
}
handle = open("/dev/spidev0.0", O_RDWR);
if(handle == -1) {
// TODO error
printf("SPI IOCTL error %s\n", strerror(errno));
}
uint8_t mmode = SPI_MODE_0;
uint8_t lsb = 0;
uint8_t bitsperword = 8;
ioctl(handle, SPI_IOC_RD_BITS_PER_WORD, &bitsperword);
ioctl(handle, SPI_IOC_WR_MODE, &mmode);
ioctl(handle, SPI_IOC_WR_LSB_FIRST, &lsb);
}
uint8_t UsbAdapter::HalGpioRead(SX126x::GpioPinFunction_t func) {
if(func != GpioPinFunction_t::GPIO_PIN_BUSY) {
std::cout << "ERROR" << std::endl;
throw;
}
int fd;
if ((fd = open("/sys/class/gpio/slct/value", O_RDWR)) == -1) {
perror("Error while opening GPIO \"busy\"");
return -1;
}
char buf;
if (read(fd, &buf, 1) == -1) {
perror("Error while reading GPIO \"busy\"");
return -1;
}
close(fd);
int value = (buf == '0') ? 0 : 1;
std::this_thread::sleep_for(std::chrono::milliseconds{1}); // Why do I need this sleep()?
return value;
}
void UsbAdapter::HalGpioWrite(SX126x::GpioPinFunction_t func, uint8_t value) {
if(func != GpioPinFunction_t::GPIO_PIN_RESET) {
std::cout << "ERROR" << std::endl;
throw;
}
int fd;
if ((fd = open("/sys/class/gpio/ini/value", O_RDWR)) == -1) {
perror("Error while opening GPIO \"reset\"");
}
if (write(fd, value ? "1" : "0", 1) == -1) {
perror ("Error while writing GPIO \"reset\"");
}
}
void UsbAdapter::HalSpiTransfer(uint8_t *buffer_in, const uint8_t *buffer_out, uint16_t size) {
const uint8_t *mosi = buffer_out; // output data
uint8_t *miso = buffer_in; // input data
struct spi_ioc_transfer spi_trans;
memset(&spi_trans, 0, sizeof(spi_trans));
spi_trans.tx_buf = (unsigned long) mosi;
spi_trans.rx_buf = (unsigned long) miso;
spi_trans.cs_change = true;
spi_trans.len = size;
int status = ioctl (handle, SPI_IOC_MESSAGE(1), &spi_trans);
}