Compare commits
15 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
3a29af1a3d | ||
|
88b66b228e | ||
|
dc399fbc93 | ||
|
22f56baf2f | ||
|
6a7f9ac74f | ||
|
29d08921f1 | ||
|
4e295fc66a | ||
|
0460b79db4 | ||
|
07eeac0c9f | ||
|
73ccb41dfb | ||
|
d70797bd92 | ||
|
a0538dc4c3 | ||
|
16a43a5b3d | ||
|
8484546773 | ||
|
0b141290b4 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -1 +1,2 @@
|
|||||||
*build*
|
*build*
|
||||||
|
*venv*
|
||||||
|
43
README.md
43
README.md
@ -1,3 +1,42 @@
|
|||||||
# teng-arduino
|
# arduino-blume
|
||||||
Measure teng output with an arduino
|
**Me**asure analog signal using an Arduino and **Blu**etooth communication.
|
||||||
|
|
||||||
|
This project was written for my bachelor's thesis.
|
||||||
|
|
||||||
|
It mesaures voltage one one of arduino's analog pins and transmits it via Bluetooth LE. It was written for (and only tested with) the **Arduino RP2040 Connect**.
|
||||||
|
|
||||||
|
[m-teng](https://github.com/MatthiasQuintern/m-teng) has a backend that can connect to the Arduino to receive the data.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
See `settings.hpp` for which pin needs to be connected.
|
||||||
|
The program uses 3 leds for displaying device status, however these are optional.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
```
|
||||||
|
git clone https://github.com/MatthiasQuintern/arduino-blume
|
||||||
|
arduino-cli compile --profile nanorp
|
||||||
|
arduino-cli upload --profile nanorp
|
||||||
|
```
|
||||||
|
|
||||||
|
## Communication via Bluetooth
|
||||||
|
The Arduino will advertise a Bluetooth Low Energy service which can be used to interact with the device.
|
||||||
|
|
||||||
|
Service UUID: `00010000-9a74-4b30-9361-4a16ec09930f`
|
||||||
|
|
||||||
|
0. status (`uint8_t`, `rn`): `00010001-9a74-4b30-9361-4a16ec09930f`
|
||||||
|
- `0`: **ERROR**: error occured: invalid command received, can not advertise Bluetooth service, etc.
|
||||||
|
- `1`: **BUSY**
|
||||||
|
- `2`: **WAIT_CONNECT**: waiting for Bluetooth connection
|
||||||
|
- `3`: **CONNECTED**: connected, waiting for command
|
||||||
|
- `4`: **MEASURING**: connected, measurement running
|
||||||
|
1. command (`uint8_t`, `rw`): `00010002-9a74-4b30-9361-4a16ec09930f`
|
||||||
|
send a command to the arduino:
|
||||||
|
- `0` - **STOP**: stop active measurements
|
||||||
|
- `1` - **MEASURE_COUNT**: measure *count* measurements with *interval* *see `count` and `interva` below*
|
||||||
|
- `2` - **MEASURE**: measure with *interval* until interrupted *see `count` below*
|
||||||
|
2. reading (`uint16_t` `rn`): `00010003-9a74-4b30-9361-4a16ec09930f`
|
||||||
|
the reading from the Arduinos analog pin
|
||||||
|
3. count: (`uint16_t`, `rw`): `00010004-9a74-4b30-9361-4a16ec09930f`
|
||||||
|
set the number of measurements to take with `MEASURE_COUNT`
|
||||||
|
4. interval: (`uint16_t`, `rw`): `00010005-9a74-4b30-9361-4a16ec09930f`
|
||||||
|
set the interval between measurements in ms
|
||||||
|
70
host/main.py
Normal file
70
host/main.py
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import bleak as b
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
TARGET_NAME = "ArduinoTENG"
|
||||||
|
|
||||||
|
TENG_SUUID = "00010000-9a74-4b30-9361-4a16ec09930f"
|
||||||
|
TENG_STATUS_CUUID = "00010001-9a74-4b30-9361-4a16ec09930f"
|
||||||
|
TENG_COMMAND_CUUID = "00010002-9a74-4b30-9361-4a16ec09930f"
|
||||||
|
TENG_READING_CUUID = "00010003-9a74-4b30-9361-4a16ec09930f"
|
||||||
|
|
||||||
|
TENG_COMMANDS = {
|
||||||
|
"NOOP": int(0).to_bytes(1),
|
||||||
|
"MEASURE_BASELINE": int(1).to_bytes(1),
|
||||||
|
}
|
||||||
|
TENG_STATUS = ["ERROR", "BUSY", "WAIT_CONNECT", "MEASURING_BASELINE", "READING"]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def teng_status_callback(characteristic, data):
|
||||||
|
value = int.from_bytes(data, byteorder="big", signed=False)
|
||||||
|
if 0 <= value and value < len(TENG_STATUS):
|
||||||
|
print(f"Status change: {TENG_STATUS[value]}")
|
||||||
|
else:
|
||||||
|
print(f"Status change (invalid): status={value}")
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
devices = await b.BleakScanner.discover(return_adv=True)
|
||||||
|
# print(devices)
|
||||||
|
target_device = None
|
||||||
|
for adr, (device, adv_data) in devices.items():
|
||||||
|
if device.name == TARGET_NAME:
|
||||||
|
print(adv_data)
|
||||||
|
target_device = device
|
||||||
|
break
|
||||||
|
if target_device is None:
|
||||||
|
print("ERROR: Could not find target device")
|
||||||
|
return
|
||||||
|
print(f"Found target device: {target_device.name}: {target_device.metadata}, {target_device.details}")
|
||||||
|
print(target_device.name, target_device.details)
|
||||||
|
try:
|
||||||
|
async with b.BleakClient(target_device) as client:
|
||||||
|
for service in client.services:
|
||||||
|
print(f"Service: {service.uuid}: {service.description}")
|
||||||
|
for c in service.characteristics:
|
||||||
|
print(f"\t{c.uuid}: {c.properties}, {c.descriptors}")
|
||||||
|
teng_status = client.services.get_characteristic(TENG_STATUS_CUUID)
|
||||||
|
teng_command = client.services.get_characteristic(TENG_COMMAND_CUUID)
|
||||||
|
teng_reading = client.services.get_characteristic(TENG_READING_CUUID)
|
||||||
|
client.start_notify(teng_status, teng_status_callback)
|
||||||
|
|
||||||
|
await client.write_gatt_char(teng_command, TENG_COMMANDS["NOOP"])
|
||||||
|
await asyncio.sleep(5)
|
||||||
|
await client.write_gatt_char(teng_command, TENG_COMMANDS["MEASURE_BASELINE"])
|
||||||
|
|
||||||
|
while client.is_connected:
|
||||||
|
data = await client.read_gatt_char(teng_reading)
|
||||||
|
|
||||||
|
|
||||||
|
value = int.from_bytes(data, byteorder="little", signed=False)
|
||||||
|
print(f"Reading: {value}")
|
||||||
|
await asyncio.sleep(0.5)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
pass
|
||||||
|
except asyncio.exceptions.CancelledError:
|
||||||
|
pass
|
||||||
|
print("Disconnected")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
asyncio.run(main())
|
104
teng/address.hpp
104
teng/address.hpp
@ -1,104 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
class Counter {
|
|
||||||
public:
|
|
||||||
const unsigned MAX_NUMBER = 4096; // 12-bit
|
|
||||||
const int RES = 18;
|
|
||||||
const int CB = 19;
|
|
||||||
void reset() {
|
|
||||||
digitalWrite(CB, HIGH);
|
|
||||||
digitalWrite(RES, HIGH);
|
|
||||||
delay(1);
|
|
||||||
digitalWrite(RES, LOW);
|
|
||||||
currentValue = 0;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @brief Increment the counter value
|
|
||||||
* @returns true if overflown to zero
|
|
||||||
*/
|
|
||||||
bool increment() {
|
|
||||||
digitalWrite(CB, LOW);
|
|
||||||
delay(1);
|
|
||||||
digitalWrite(CB, HIGH);
|
|
||||||
currentValue++;
|
|
||||||
if (currentValue >= MAX_NUMBER) {
|
|
||||||
currentValue = 0;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
unsigned get() { return currentValue; }
|
|
||||||
private:
|
|
||||||
unsigned currentValue = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class AddressState {
|
|
||||||
public:
|
|
||||||
AddressState(Counter counter_, int addressPinCount_, const int* addressPins_)
|
|
||||||
: counter(counter_), addressPinCount(addressPinCount_), addressPins(addressPins_) {
|
|
||||||
counter.reset();
|
|
||||||
}
|
|
||||||
void set(unsigned targetAddress) {
|
|
||||||
unsigned targetCounterValue = targetAddress % counter.MAX_NUMBER;
|
|
||||||
if (counter.get() > targetCounterValue) {
|
|
||||||
counter.reset();
|
|
||||||
|
|
||||||
}
|
|
||||||
for (unsigned i = 0; i < (targetCounterValue - counter.get()); i++) {
|
|
||||||
counter.increment();
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned pinValues = targetAddress / counter.MAX_NUMBER;
|
|
||||||
addressPinStateMask = 0;
|
|
||||||
for (unsigned i = 0; i < addressPinCount; i++) {
|
|
||||||
unsigned bitSet = pinValues & (1 << i);
|
|
||||||
if (bitSet > 0) {
|
|
||||||
digitalWrite(addressPins[i], HIGH);
|
|
||||||
addressPinStateMask |= (1 << (addressPins[i] - 1));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
digitalWrite(addressPins[i], LOW);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
address = targetAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void increment() {
|
|
||||||
if (counter.increment()) { // if overflow
|
|
||||||
// add 1 to the other pins
|
|
||||||
for (unsigned i = 0; i < addressPinCount; i++) {
|
|
||||||
if ((addressPinStateMask & (addressPins[i] - 1)) == 0) {
|
|
||||||
addressPinStateMask |= (1 << (addressPins[i] - 1));
|
|
||||||
digitalWrite(addressPins[i], HIGH);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
addressPinStateMask ^= (1 << (addressPins[i] - 1));
|
|
||||||
digitalWrite(addressPins[i], LOW);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
address++;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
unsigned getAddress() { return address; }
|
|
||||||
unsigned reset() {
|
|
||||||
counter.reset();
|
|
||||||
for (unsigned i = 0; i < addressPinCount; i++) {
|
|
||||||
digitalWrite(addressPins[i], LOW);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
// if counter is 12 bit and address is 16 bit, this is the value of the top 4 bits
|
|
||||||
// i-th bit represents i-th pin
|
|
||||||
unsigned addressPinStateMask = 0;
|
|
||||||
unsigned address = 0;
|
|
||||||
Counter counter;
|
|
||||||
const int* addressPins;
|
|
||||||
int addressPinCount;
|
|
||||||
};
|
|
@ -1,7 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
|
|
||||||
const char CTRL_WRITE = 1;
|
|
||||||
const char CTRL_READ = 2;
|
|
||||||
const char CTRL_256KB = 11;
|
|
||||||
const char CTRL_2M = 14;
|
|
@ -1,70 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
#include "address.hpp"
|
|
||||||
|
|
||||||
class EEPROM {
|
|
||||||
public:
|
|
||||||
void setup() {
|
|
||||||
for (unsigned i = 0; i < addressPinCount; i++) {
|
|
||||||
pinMode(addressPins[i], OUTPUT);
|
|
||||||
}
|
|
||||||
for (unsigned i = 0; i < 8; i++) {
|
|
||||||
pinMode(dataPins[i], INPUT);
|
|
||||||
}
|
|
||||||
pinMode(OEb, OUTPUT);
|
|
||||||
pinMode(WEb, OUTPUT);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Read the data pins.
|
|
||||||
*/
|
|
||||||
uint8_t readDataPins() {
|
|
||||||
uint8_t b = 0;
|
|
||||||
for (unsigned i = 0; i < 8; i++) {
|
|
||||||
b |= (digitalRead(dataPins[i]) << i);
|
|
||||||
}
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
|
|
||||||
// set start address in via AddressState
|
|
||||||
void read(uint8_t* data, unsigned dataSize, AddressState& address) {
|
|
||||||
unsigned i = 0;
|
|
||||||
for (unsigned i = 0; i < dataSize; i++) {
|
|
||||||
digitalWrite(OEb, LOW);
|
|
||||||
delay(t_OUTPUT_DELAY);
|
|
||||||
data[i] = readDataPins();
|
|
||||||
digitalWrite(OEb, HIGH);
|
|
||||||
delay(t_OUTPUT_FLOAT);
|
|
||||||
address.increment();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* private: */
|
|
||||||
int OEb;
|
|
||||||
int WEb;
|
|
||||||
int addressPins[6];
|
|
||||||
unsigned addressPinCount;
|
|
||||||
int dataPins[8] = { 2, 3, 4, 5, 6, 7, 8, 9 };
|
|
||||||
|
|
||||||
// in ms
|
|
||||||
unsigned t_OUTPUT_DELAY = 1;
|
|
||||||
unsigned t_MIN_WRITE_PULSE_WIDTH = 1;
|
|
||||||
unsigned t_OUTPUT_FLOAT = 1;
|
|
||||||
unsigned t_DATA_HOLD_TIME = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr EEPROM AT28C256 {
|
|
||||||
.OEb = 17,
|
|
||||||
.WEb = 16,
|
|
||||||
.addressPins = {10, 15, 11},
|
|
||||||
.addressPinCount = 3
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr EEPROM SST39SF02A {
|
|
||||||
.OEb = 17,
|
|
||||||
.WEb = 16,
|
|
||||||
.addressPins = {10, 15, 16, 11, 12, 14},
|
|
||||||
.addressPinCount = 6
|
|
||||||
};
|
|
36
teng/measure.hpp
Normal file
36
teng/measure.hpp
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <ArduinoBLE.h>
|
||||||
|
|
||||||
|
#include "services.hpp"
|
||||||
|
#include "status.hpp"
|
||||||
|
|
||||||
|
enum MeasurementTask { STOP_MEASURE, START_MEASURE, START_MEASURE_COUNT };
|
||||||
|
MeasurementTask measurementTask = STOP_MEASURE;
|
||||||
|
|
||||||
|
void measure(BLEDevice central, bool limitToCount=false) {
|
||||||
|
uint16_t interval = tengInterval.value();
|
||||||
|
uint16_t count = tengCount.value();
|
||||||
|
|
||||||
|
Serial.print("Starting measurement with interval=");
|
||||||
|
Serial.print(interval, DEC);
|
||||||
|
if (limitToCount) {
|
||||||
|
Serial.print(", count=");
|
||||||
|
Serial.print(count, DEC);
|
||||||
|
}
|
||||||
|
Serial.println("");
|
||||||
|
|
||||||
|
uint16_t i = 0;
|
||||||
|
setStatus(DeviceStatus::MEASURING);
|
||||||
|
while (central.connected() && (!limitToCount || i < count) && measurementTask != STOP_MEASURE) {
|
||||||
|
uint16_t val = static_cast<uint16_t>(analogRead(PIN_TENG));
|
||||||
|
Serial.println(val, DEC);
|
||||||
|
tengReading.writeValue(val);
|
||||||
|
/* Serial.println(val, DEC); */
|
||||||
|
delay(interval);
|
||||||
|
i++;
|
||||||
|
central.poll();
|
||||||
|
}
|
||||||
|
measurementTask = STOP_MEASURE;
|
||||||
|
setStatus(DeviceStatus::CONNECTED);
|
||||||
|
}
|
@ -1,23 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <vector>
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
enum PacketType : uint8_t {
|
|
||||||
MESSAGE, STATUS, COMMAND
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Packet {
|
|
||||||
public:
|
|
||||||
using size_type = uint16_t;
|
|
||||||
Packet(PacketType type, size_type size)
|
|
||||||
: type(type), size(size) {
|
|
||||||
payload = new uint8_t[size];
|
|
||||||
}
|
|
||||||
~Packet() {
|
|
||||||
delete[] payload;
|
|
||||||
}
|
|
||||||
uint8_t type;
|
|
||||||
size_type size;
|
|
||||||
uint8_t* payload;
|
|
||||||
|
|
||||||
};
|
|
32
teng/services.hpp
Normal file
32
teng/services.hpp
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <ArduinoBLE.h>
|
||||||
|
|
||||||
|
const char* BASE_UUID = "00000000-9a74-4b30-9361-4a16ec09930f";
|
||||||
|
|
||||||
|
// Service 1: TENG
|
||||||
|
const char* TENG_SUUID = "00010000-9a74-4b30-9361-4a16ec09930f"; // Service UUID
|
||||||
|
const char* TENG_STATUS_CUUID = "00010001-9a74-4b30-9361-4a16ec09930f"; // Characteristic UUID
|
||||||
|
const char* TENG_COMMAND_CUUID = "00010002-9a74-4b30-9361-4a16ec09930f";
|
||||||
|
const char* TENG_READING_CUUID = "00010003-9a74-4b30-9361-4a16ec09930f";
|
||||||
|
const char* TENG_COUNT_CUUID = "00010004-9a74-4b30-9361-4a16ec09930f";
|
||||||
|
const char* TENG_INTERVAL_CUUID = "00010005-9a74-4b30-9361-4a16ec09930f";
|
||||||
|
/* const char* TENG_BASELINE_CUUID = "00010004-9a74-4b30-9361-4a16ec09930f"; // Characteristic UUID */
|
||||||
|
BLEService tengService(TENG_SUUID);
|
||||||
|
BLEByteCharacteristic tengStatus(TENG_STATUS_CUUID, BLERead | BLENotify);
|
||||||
|
BLEByteCharacteristic tengCommand(TENG_COMMAND_CUUID, BLEWrite);
|
||||||
|
BLEUnsignedShortCharacteristic tengReading(TENG_READING_CUUID, BLERead | BLENotify);
|
||||||
|
BLEUnsignedShortCharacteristic tengInterval(TENG_INTERVAL_CUUID, BLERead | BLEWrite);
|
||||||
|
BLEUnsignedShortCharacteristic tengCount(TENG_COUNT_CUUID, BLERead | BLEWrite);
|
||||||
|
/* BLEUnsignedShortCharacteristic tengBaseline(TENG_BASELINE_CUUID, BLERead); */
|
||||||
|
|
||||||
|
enum Command : byte { STOP = 0, MEASURE_COUNT, MEASURE, };
|
||||||
|
|
||||||
|
void initServices() {
|
||||||
|
tengService.addCharacteristic(tengStatus);
|
||||||
|
tengService.addCharacteristic(tengCommand);
|
||||||
|
tengService.addCharacteristic(tengReading);
|
||||||
|
tengService.addCharacteristic(tengCount);
|
||||||
|
tengService.addCharacteristic(tengInterval);
|
||||||
|
/* tengService.addCharacteristic(tengBaseline); */
|
||||||
|
}
|
6
teng/settings.hpp
Normal file
6
teng/settings.hpp
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
constexpr int LED_RED = 2;
|
||||||
|
constexpr int LED_YELLOW = 3;
|
||||||
|
constexpr int LED_GREEN = 4;
|
||||||
|
constexpr int PIN_TENG = 0;
|
@ -4,3 +4,4 @@ profiles:
|
|||||||
platforms:
|
platforms:
|
||||||
- platform: arduino:mbed_nano (4.0.2)
|
- platform: arduino:mbed_nano (4.0.2)
|
||||||
libraries:
|
libraries:
|
||||||
|
- ArduinoBLE (1.3.4)
|
||||||
|
43
teng/status.hpp
Normal file
43
teng/status.hpp
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
#include "settings.hpp"
|
||||||
|
#include "services.hpp"
|
||||||
|
|
||||||
|
// needs to be in separate file because sketch preprocessor reorders stuff
|
||||||
|
enum DeviceStatus : byte { ERROR = 0, BUSY, WAIT_CONNECT, CONNECTED, MEASURING };
|
||||||
|
|
||||||
|
void setStatusLED(DeviceStatus s, bool value) {
|
||||||
|
switch (s) {
|
||||||
|
case ERROR:
|
||||||
|
digitalWrite(LED_RED, value);
|
||||||
|
break;
|
||||||
|
case BUSY:
|
||||||
|
digitalWrite(LED_YELLOW, value);
|
||||||
|
digitalWrite(LED_RED, value);
|
||||||
|
break;
|
||||||
|
case WAIT_CONNECT:
|
||||||
|
digitalWrite(LED_YELLOW, value);
|
||||||
|
break;
|
||||||
|
case CONNECTED:
|
||||||
|
digitalWrite(LED_GREEN, value);
|
||||||
|
break;
|
||||||
|
/* case MEASURING_BASELINE: */
|
||||||
|
/* digitalWrite(LED_RED, value); */
|
||||||
|
/* digitalWrite(LED_GREEN, value); */
|
||||||
|
/* break; */
|
||||||
|
case MEASURING:
|
||||||
|
digitalWrite(LED_YELLOW, value);
|
||||||
|
digitalWrite(LED_GREEN, value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceStatus deviceStatus = WAIT_CONNECT;
|
||||||
|
void setStatus(DeviceStatus s) {
|
||||||
|
setStatusLED(deviceStatus, LOW);
|
||||||
|
setStatusLED(s, HIGH);
|
||||||
|
deviceStatus = s;
|
||||||
|
tengStatus.writeValue(deviceStatus);
|
||||||
|
}
|
279
teng/teng.ino
279
teng/teng.ino
@ -1,31 +1,21 @@
|
|||||||
/*
|
/*
|
||||||
* EEPROM PROGRAMMER
|
|
||||||
* 15-18 Address Pins
|
|
||||||
* 8 Data Pins
|
|
||||||
* 2 Control Pins (OEb, WEb)
|
|
||||||
*
|
*
|
||||||
* CEb must tied to ground
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <ArduinoBLE.h>
|
#include <ArduinoBLE.h>
|
||||||
/* #include "eeprom.hpp" */
|
|
||||||
/* #include "address.hpp" */
|
|
||||||
|
|
||||||
/* #include "control_bytes.hpp" */
|
#include "settings.hpp"
|
||||||
|
#include "status.hpp"
|
||||||
|
#include "services.hpp"
|
||||||
|
#include "measure.hpp"
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
/* std::array<uint16_t, 10000> valueBuffer; // 20kB buffer for readings */
|
||||||
|
/* auto valueBufferWriteIter = valueBuffer.begin(); // points past the element that was last written */
|
||||||
|
|
||||||
|
|
||||||
constexpr int LED_RED = 2;
|
|
||||||
constexpr int LED_YELLOW = 3;
|
|
||||||
constexpr int LED_GREEN = 4;
|
|
||||||
constexpr int PIN_TENG = 0;
|
|
||||||
|
|
||||||
/* void maskedWrite(uint32_t mask, uint32_t bits) { */
|
|
||||||
/* for (uint32_t i = 0; i < 32; i++) { */
|
|
||||||
/* if (mask & (1 << i)) { */
|
|
||||||
/* digitalWrite(i, bits & (1 << i)); */
|
|
||||||
/* } */
|
|
||||||
/* } */
|
|
||||||
/* } */
|
|
||||||
void blinkLED(unsigned n=5, unsigned delay_=200) {
|
void blinkLED(unsigned n=5, unsigned delay_=200) {
|
||||||
for (unsigned i = 0; i < n; i++) {
|
for (unsigned i = 0; i < n; i++) {
|
||||||
digitalWrite(LED_BUILTIN, HIGH);
|
digitalWrite(LED_BUILTIN, HIGH);
|
||||||
@ -36,196 +26,113 @@ void blinkLED(unsigned n=5, unsigned delay_=200) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
unsigned MAX_DEVIATION = 0;
|
|
||||||
unsigned BASELINE = 0;
|
|
||||||
void measureBaseline(unsigned nMeas, unsigned interval=50) {
|
|
||||||
uint64_t value = 0;
|
|
||||||
unsigned minVal = 1023;
|
|
||||||
unsigned maxVal = 0;
|
|
||||||
for (unsigned i = 0; i < nMeas; i++) {
|
|
||||||
unsigned reading = analogRead(PIN_TENG);
|
|
||||||
value += reading;
|
|
||||||
delay(interval);
|
|
||||||
if (reading > maxVal) { maxVal = reading; }
|
|
||||||
if (reading < minVal) { minVal = reading; }
|
|
||||||
}
|
|
||||||
BASELINE = value / nMeas;
|
|
||||||
if (BASELINE - minVal > maxVal - BASELINE) {
|
|
||||||
MAX_DEVIATION = BASELINE - minVal;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
MAX_DEVIATION = maxVal - BASELINE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
/* pinMode(LED_BUILTIN, OUTPUT); */
|
setStatus(DeviceStatus::BUSY);
|
||||||
/* digitalWrite(LED_BUILTIN, HIGH); */
|
|
||||||
/* delay(200); */
|
|
||||||
|
|
||||||
Serial.begin(9600);
|
Serial.begin(9600);
|
||||||
/* // wait until available */
|
/* // wait until available */
|
||||||
while (!Serial);
|
/* while (!Serial); */
|
||||||
digitalWrite(LED_BUILTIN, LOW);
|
|
||||||
delay(200); // empty buffer
|
|
||||||
while (Serial.read() != -1);
|
|
||||||
|
|
||||||
/* digitalWrite(LED_BUILTIN, HIGH); */
|
blinkLED(2);
|
||||||
/* delay(5000); */
|
|
||||||
|
|
||||||
/* // reset counter */
|
// TODO: test if works
|
||||||
/* blinkLED(); */
|
analogReadResolution(12); // set analog input to 12 bit resolution (0-4095)
|
||||||
|
|
||||||
/* uint8_t payload[] = { 1, 2, 3, 4, 5, 69 }; */
|
|
||||||
/* uint8_t type = '#'; */
|
|
||||||
/* static_assert(sizeof(payload) == 6); */
|
|
||||||
/* uint16_t size = sizeof(payload); */
|
|
||||||
/* Serial.write(type); */
|
|
||||||
/* Serial.write(lowByte(size)); */
|
|
||||||
/* Serial.write(highByte(size));u */
|
|
||||||
/* Serial.write(payload, size); */
|
|
||||||
|
|
||||||
/* delay(5000); */
|
|
||||||
blinkLED(4);
|
|
||||||
if (!BLE.begin()) {
|
if (!BLE.begin()) {
|
||||||
Serial.println("starting Bluetooth® Low Energy module failed!");
|
Serial.println("starting Bluetooth® Low Energy module failed!");
|
||||||
|
setStatus(DeviceStatus::ERROR);
|
||||||
|
while(true);
|
||||||
}
|
}
|
||||||
BLE.setDeviceName("ArduinoTeng");
|
/* BLE.setDeviceName("ArduinoTENG"); */
|
||||||
|
BLE.setLocalName("ArduinoTENG");
|
||||||
|
|
||||||
|
byte data[10] = "\xFF\xFFMQU@TUM"; // 0xFFFF manufacturer id for testing
|
||||||
|
BLE.setManufacturerData(data, 10);
|
||||||
|
|
||||||
|
initServices();
|
||||||
|
BLE.addService(tengService);
|
||||||
|
BLE.setAdvertisedService(tengService);
|
||||||
|
|
||||||
|
// defaults
|
||||||
|
tengCount.writeValue(100);
|
||||||
|
tengInterval.writeValue(100);
|
||||||
|
|
||||||
|
BLE.setConnectable(true);
|
||||||
|
blinkLED(3);
|
||||||
BLE.advertise();
|
BLE.advertise();
|
||||||
|
|
||||||
measureBaseline(100);
|
setStatus(DeviceStatus::WAIT_CONNECT);
|
||||||
digitalWrite(LED_RED, HIGH);
|
|
||||||
digitalWrite(LED_YELLOW, HIGH);
|
|
||||||
digitalWrite(LED_GREEN, HIGH);
|
|
||||||
delay(500);
|
|
||||||
digitalWrite(LED_RED, LOW);
|
|
||||||
digitalWrite(LED_YELLOW, LOW);
|
|
||||||
digitalWrite(LED_GREEN, LOW);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* constexpr int max_val = 1023 / 5 * 3.5; // 1023 max val for 5V */
|
/* constexpr int max_val = 1023 / 5 * 3.5; // 1023 max val for 5V */
|
||||||
|
// it seems this function must return immediately, otherwise the connection will time out
|
||||||
|
void commandWrittenHandler(BLEDevice central, BLECharacteristic characteristic) {
|
||||||
|
switch (tengCommand.value()) {
|
||||||
|
case Command::STOP:
|
||||||
|
measurementTask = STOP_MEASURE;
|
||||||
|
break;
|
||||||
|
case Command::MEASURE:
|
||||||
|
if (measurementTask == STOP_MEASURE) {
|
||||||
|
measurementTask = START_MEASURE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Serial.println("ERROR: Command 'MEASURE' received while measurement is active");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Command::MEASURE_COUNT:
|
||||||
|
if (measurementTask == STOP_MEASURE) {
|
||||||
|
measurementTask = START_MEASURE_COUNT;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Serial.println("ERROR: Command 'MEASURE_COUNT' received while measurement is active");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
/* case Command::MEASURE_BASELINE: */
|
||||||
|
/* setStatus(DeviceStatus::MEASURING_BASELINE); */
|
||||||
|
/* measureBaseline(100); */
|
||||||
|
/* break; */
|
||||||
|
default:
|
||||||
|
setStatus(DeviceStatus::ERROR);
|
||||||
|
Serial.print("ERROR: Unkown command: ");
|
||||||
|
Serial.println(tengCommand.value(), HEX);
|
||||||
|
delay(1000);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// the loop function runs over and over again forever
|
// the loop function runs over and over again forever
|
||||||
void loop() {
|
void loop() {
|
||||||
int led_red = LOW;
|
blinkLED(3, 300);
|
||||||
int led_yellow = LOW;
|
// listen for Bluetooth® Low Energy peripherals to connect:
|
||||||
int led_green = LOW;
|
BLEDevice central = BLE.central();
|
||||||
|
|
||||||
int val = analogRead(PIN_TENG);
|
// if a central is connected to peripheral:
|
||||||
Serial.print(val, DEC);
|
if (central) {
|
||||||
val -= BASELINE;
|
setStatus(DeviceStatus::CONNECTED);
|
||||||
if (val < 0) { val = -val; }
|
tengCommand.setEventHandler(BLEWritten, commandWrittenHandler);
|
||||||
Serial.print("/");
|
|
||||||
Serial.println(val, DEC);
|
|
||||||
|
|
||||||
if (val >= MAX_DEVIATION * 1) {
|
Serial.print("Connected to central: ");
|
||||||
led_green = HIGH;
|
Serial.println(central.address());
|
||||||
|
|
||||||
|
while (central.connected()) {
|
||||||
|
blinkLED(1, 200);
|
||||||
|
if (measurementTask == START_MEASURE) {
|
||||||
|
measure(central, false);
|
||||||
}
|
}
|
||||||
if (val >= MAX_DEVIATION * 2) {
|
else if (measurementTask == START_MEASURE_COUNT) {
|
||||||
led_yellow = HIGH;
|
measure(central, true);
|
||||||
}
|
}
|
||||||
if (val >= MAX_DEVIATION * 3) {
|
|
||||||
led_red = HIGH;
|
|
||||||
}
|
}
|
||||||
digitalWrite(LED_RED, led_red);
|
|
||||||
digitalWrite(LED_YELLOW, led_yellow);
|
|
||||||
digitalWrite(LED_GREEN, led_green);
|
|
||||||
|
|
||||||
delay(100);
|
setStatus(DeviceStatus::WAIT_CONNECT);
|
||||||
|
// when the central disconnects, notify the user
|
||||||
|
Serial.print("Disconnected from central MAC: ");
|
||||||
|
Serial.println(central.address());
|
||||||
/* // init address */
|
}
|
||||||
/* const int* addressPins = nullptr; */
|
else {
|
||||||
/* unsigned addressPinCount = 0; */
|
BLE.advertise();
|
||||||
/* // TODO pass eeprom via serial */
|
}
|
||||||
/* EEPROM eeprom = AT28C256; */
|
|
||||||
/* if (mem_size = CTRL_2M) { */
|
|
||||||
/* eeprom = SST39SF02A; */
|
|
||||||
/* } */
|
|
||||||
/* AddressState address(Counter(), eeprom.addressPinCount, eeprom.addressPins); */
|
|
||||||
/* const int dataSize = 0x8000; */
|
|
||||||
/* uint8_t data[dataSize]; */
|
|
||||||
|
|
||||||
/* while (true) { */
|
|
||||||
|
|
||||||
/* } */
|
|
||||||
/* if (!Serial.available()) { continue; } */
|
|
||||||
|
|
||||||
/* // read packet */
|
|
||||||
/* uint8_t type = Serial.read(); */
|
|
||||||
/* uint16_t size = Serial.read(); // low byte */
|
|
||||||
/* size |= (Serial.read() << 8); // high byte */
|
|
||||||
/* Packet packet(type, size); */
|
|
||||||
/* Serial.readBytes(packet.payload, size); */
|
|
||||||
|
|
||||||
/* switch(type) { */
|
|
||||||
/* case PacketType::WRITE: { */
|
|
||||||
/* break; */
|
|
||||||
/* } */
|
|
||||||
/* case PacketType::READ: { */
|
|
||||||
/* break; */
|
|
||||||
/* } */
|
|
||||||
/* case PacketType::SET_ADDRESS { */
|
|
||||||
|
|
||||||
/* } */
|
|
||||||
/* default: { */
|
|
||||||
/* break; */
|
|
||||||
/* } */
|
|
||||||
/* } */
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* byte mem_size = 0; */
|
|
||||||
/* byte action = 0; */
|
|
||||||
/* byte x = 0; */
|
|
||||||
/* while (action == 0 || mem_size == 0) { */
|
|
||||||
/* digitalWrite(LED_BUILTIN, HIGH); */
|
|
||||||
/* if (Serial.available() <= 0) { */
|
|
||||||
/* blinkLED(3); */
|
|
||||||
/* } */
|
|
||||||
/* else { */
|
|
||||||
/* /1* while (Serial.available() > 0) { *1/ */
|
|
||||||
/* /1* Serial.write(Serial.read()); *1/ */
|
|
||||||
/* /1* } *1/ */
|
|
||||||
/* /1* Serial.write('\n'); *1/ */
|
|
||||||
|
|
||||||
/* Serial.write(static_cast<uint8_t>(Serial.available())); */
|
|
||||||
/* int cmd = Serial.read(); */
|
|
||||||
/* switch (cmd) { */
|
|
||||||
/* case CTRL_WRITE: */
|
|
||||||
/* case CTRL_READ: */
|
|
||||||
/* action = cmd; */
|
|
||||||
/* Serial.write('\0'); */
|
|
||||||
/* Serial.write('\0'); */
|
|
||||||
/* break; */
|
|
||||||
/* default: */
|
|
||||||
/* mem_size = cmd; */
|
|
||||||
/* Serial.write('\1'); */
|
|
||||||
/* Serial.write('\1'); */
|
|
||||||
/* } */
|
|
||||||
/* Serial.write(static_cast<uint8_t>(cmd)); */
|
|
||||||
/* Serial.write('\2'); */
|
|
||||||
/* Serial.write('\2'); */
|
|
||||||
/* } */
|
|
||||||
/* Serial.write(x); */
|
|
||||||
/* Serial.print('\n'); */
|
|
||||||
/* x++; */
|
|
||||||
/* delay(1000); */
|
|
||||||
/* digitalWrite(LED_BUILTIN, LOW); */
|
|
||||||
/* delay(1000); */
|
|
||||||
/* } */
|
|
||||||
|
|
||||||
/* if (action == CTRL_WRITE) { */
|
|
||||||
/* Serial.println("Write"); */
|
|
||||||
/* } */
|
|
||||||
/* else if (action == CTRL_READ) { */
|
|
||||||
/* Serial.println("Read"); */
|
|
||||||
/* eeprom.read(data, dataSize, address); */
|
|
||||||
/* Serial.write(data, dataSize); */
|
|
||||||
/* blinkLED(10); */
|
|
||||||
/* } */
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user