Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 21 additions & 19 deletions ArduinoCore-Linux/cores/arduino/Arduino.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <stdio.h>

#include <chrono>
#include <cstdint>
#include <cstring>
#include <ctime>
#include <thread>
Expand Down Expand Up @@ -59,6 +60,24 @@ PluggableUSB_::PluggableUSB_() {}

} // namespace arduino

namespace {

using MonotonicClock = std::chrono::steady_clock;

const MonotonicClock::time_point& clockEpoch() {
static const MonotonicClock::time_point epoch = MonotonicClock::now();
return epoch;
}

uint64_t elapsedMicroseconds() {
return static_cast<uint64_t>(
std::chrono::duration_cast<std::chrono::microseconds>(
MonotonicClock::now() - clockEpoch())
.count());
}

} // namespace

/**
* @brief Pause the program for the amount of time (in milliseconds) specified as parameter
* @param ms The number of milliseconds to pause (unsigned long)
Expand Down Expand Up @@ -93,32 +112,15 @@ char* dtostrf(double val, signed char width, unsigned char prec, char* sout) {
* @return Number of milliseconds passed since the program started (unsigned long)
*/
unsigned long millis() {
static uint64_t start = 0;
using namespace std::chrono;
// Get current time with precision of milliseconds
auto now = time_point_cast<milliseconds>(system_clock::now());
// sys_milliseconds is type time_point<system_clock, milliseconds>
using sys_milliseconds = decltype(now);
// Convert time_point to signed integral type
auto result = now.time_since_epoch().count();
if (start == 0) {
start = result;
}
return result - start;
return static_cast<uint32_t>(elapsedMicroseconds() / 1000ULL);
}

/**
* @brief Returns the number of microseconds since the Arduino board began running the current program
* @return Number of microseconds passed since the program started (unsigned long)
*/
unsigned long micros(void) {
using namespace std::chrono;
// Get current time with precision of milliseconds
auto now = time_point_cast<microseconds>(system_clock::now());
// sys_milliseconds is type time_point<system_clock, milliseconds>
using sys_milliseconds = decltype(now);
// Convert time_point to signed integral type
return now.time_since_epoch().count();
return static_cast<uint32_t>(elapsedMicroseconds());
}

/**
Expand Down
57 changes: 52 additions & 5 deletions ArduinoCore-Linux/cores/arduino/StdioDevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@
#pragma once

#include <iostream>
#include <algorithm>
#include <deque>
#if defined(__unix__) || defined(__APPLE__)
#include <sys/ioctl.h>
#include <unistd.h>
#endif
#include <streambuf>
#include "api/Stream.h"
#include "api/Printable.h"
Expand Down Expand Up @@ -127,19 +133,60 @@ class StdioDevice : public Stream {
return 1;
}

int available() override { return std::cin.rdbuf()->in_avail(); };
int available() override {
#if defined(__unix__) || defined(__APPLE__)
fillInputBuffer();
return static_cast<int>(inputBuffer_.size());
#endif
return std::cin.rdbuf()->in_avail();
}

int read() override { return std::cin.get(); }
int read() override {
#if defined(__unix__) || defined(__APPLE__)
if (available() == 0) return -1;
const int value = inputBuffer_.front();
inputBuffer_.pop_front();
return value;
#else
return std::cin.get();
#endif
}

int peek() override { return std::cin.peek(); }
int peek() override {
#if defined(__unix__) || defined(__APPLE__)
return available() > 0 ? inputBuffer_.front() : -1;
#else
return std::cin.peek();
#endif
}

protected:
bool auto_flush = true;

#if defined(__unix__) || defined(__APPLE__)
private:
void fillInputBuffer() {
int byteCount = 0;
if (ioctl(STDIN_FILENO, FIONREAD, &byteCount) != 0 || byteCount <= 0) {
return;
}

char buffer[256];
const size_t bytesToRead = std::min(static_cast<size_t>(byteCount), sizeof(buffer));
const ssize_t bytesRead = ::read(STDIN_FILENO, buffer, bytesToRead);
if (bytesRead <= 0) {
return;
}
inputBuffer_.insert(inputBuffer_.end(), buffer, buffer + bytesRead);
}

std::deque<unsigned char> inputBuffer_;
#endif
};

static StdioDevice Serial;
inline StdioDevice Serial;
#ifndef USE_RPI
static StdioDevice Serial2;
inline StdioDevice Serial2;
#endif

} // namespace arduino
4 changes: 4 additions & 0 deletions ArduinoCore-Linux/cores/arduino/wiring.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#pragma once

// Arduino sketches and libraries often include this legacy header directly.
#include "Arduino.h"
45 changes: 40 additions & 5 deletions ArduinoCore-Linux/libraries/SD.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,11 @@
#define SS -1
#endif

using namespace std;
#ifndef BUILTIN_SDCARD
#define BUILTIN_SDCARD -1
#endif

using std::ios;

/**
* @brief C++ std based emulatoion of SdSpiConfig
Expand Down Expand Up @@ -252,15 +256,46 @@ class SdFat {
int rc = ::rmdir(path);
return rc == 0;
}

/**
* @brief Return whether the host filesystem backing the emulator is usable.
*
* The emulator has no removable media, so a writable current working
* directory is the closest equivalent to an inserted SD card.
*/
bool mediaPresent() {
std::error_code ec;
const std::filesystem::path root = std::filesystem::current_path(ec);
if (ec || !std::filesystem::exists(root, ec) || ec) {
return false;
}

const auto status = std::filesystem::status(root, ec);
if (ec) {
return false;
}

return (status.permissions() & std::filesystem::perms::owner_write) !=
std::filesystem::perms::none;
}

/** @brief Total capacity in bytes of the host filesystem backing SD. */
uint64_t totalSize() { return totalBytes(); }

/** @brief Used bytes of the host filesystem backing SD. */
uint64_t usedSize() { return usedBytes(); }

uint64_t totalBytes() {
std::error_code ec;
const std::filesystem::space_info si = std::filesystem::space("/home", ec);
return si.capacity;
const std::filesystem::space_info si =
std::filesystem::space(std::filesystem::current_path(ec), ec);
return ec ? 0 : si.capacity;
}
uint64_t usedBytes() {
std::error_code ec;
const std::filesystem::space_info si = std::filesystem::space("/home", ec);
return si.capacity - si.available;
const std::filesystem::space_info si =
std::filesystem::space(std::filesystem::current_path(ec), ec);
return ec ? 0 : si.capacity - si.available;
}
};

Expand Down
4 changes: 4 additions & 0 deletions ArduinoEmulator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#pragma once

// PlatformIO discovery entry point for the desktop Arduino runtime.
#include "ArduinoCore-Linux/cores/arduino/Arduino.h"
23 changes: 23 additions & 0 deletions library.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "Arduino-Emulator",
"version": "0.1.0",
"description": "Desktop Arduino compatibility runtime",
"keywords": ["arduino", "emulator", "native"],
"frameworks": "*",
"platforms": "native",
"build": {
"srcDir": ".",
"includeDir": ".",
"flags": [
"-I ArduinoCore-API",
"-I ArduinoCore-API/api",
"-I ArduinoCore-Linux/cores/arduino",
"-I ArduinoCore-Linux/libraries"
],
"srcFilter": [
"+<ArduinoCore-API/api/*.cpp>",
"+<ArduinoCore-Linux/cores/arduino/*.cpp>",
"-<ArduinoCore-Linux/cores/arduino/Main.cpp>"
]
}
}