forked from IanF/OneWireArduinoSlaveATTINY85
Youen Toupin
9 years ago
27 changed files with 95 additions and 1781 deletions
@ -1,434 +0,0 @@
|
||||
#include "Arduino.h" |
||||
#include "LowLevel.h" |
||||
#include "SerialChannel.h" |
||||
|
||||
#define LEDPin 13 |
||||
#define OWPin 2 |
||||
#define InterruptNumber 0 // Must correspond to the OWPin to correctly detect state changes. On Arduino Uno, interrupt 0 is for digital pin 2
|
||||
|
||||
#define ResetMinDuration 480 |
||||
#define ResetMaxDuration 900 |
||||
|
||||
#define PresenceWaitDuration 30 |
||||
#define PresenceDuration 300 |
||||
|
||||
#define ReadBitSamplingTime 13 // the theorical time is about 30us, but given various overhead, this is the empirical delay I've found works best (on Arduino Uno)
|
||||
|
||||
#define SendBitDuration 35 |
||||
|
||||
const byte InvalidBit = (byte)-1; |
||||
const byte IncompleteBit = (byte)-2; |
||||
|
||||
SerialChannel debug("debug"); |
||||
|
||||
Pin owPin(OWPin); |
||||
Pin owOutTestPin(3); |
||||
Pin led(LEDPin); |
||||
|
||||
enum OwStatus |
||||
{ |
||||
OS_WaitReset, |
||||
OS_Presence, |
||||
OS_WaitCommand, |
||||
OS_SearchRom, |
||||
}; |
||||
OwStatus status; |
||||
|
||||
byte owROM[8] = { 0xE2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00 }; |
||||
byte searchROMCurrentByte = 0; |
||||
byte searchROMCurrentBit = 0; |
||||
bool searchROMSendingInverse = false; |
||||
bool searchROMReadingMasterResponseBit = false; |
||||
|
||||
void owPullLow() |
||||
{ |
||||
owPin.outputMode(); |
||||
owPin.writeLow(); |
||||
owOutTestPin.writeLow(); |
||||
} |
||||
|
||||
void owRelease() |
||||
{ |
||||
owPin.inputMode(); |
||||
owOutTestPin.writeHigh(); |
||||
} |
||||
|
||||
void onEnterInterrupt() |
||||
{ |
||||
//owOutTestPin.writeLow();
|
||||
} |
||||
|
||||
void onLeaveInterrupt() |
||||
{ |
||||
//owOutTestPin.writeHigh();
|
||||
} |
||||
|
||||
volatile unsigned long resetStart = (unsigned long)-1; |
||||
unsigned long lastReset = (unsigned long)-1; |
||||
unsigned long bitStart = (unsigned long)-1; |
||||
byte receivingByte = 0; |
||||
byte receivingBitPos = 0; |
||||
bool searchRomNextBit = false; |
||||
bool searchRomNextBitToSend = false; |
||||
|
||||
void setup() |
||||
{ |
||||
owROM[7] = crc8((char*)owROM, 7); |
||||
|
||||
led.outputMode(); |
||||
owPin.inputMode(); |
||||
owOutTestPin.outputMode(); |
||||
owOutTestPin.writeHigh(); |
||||
|
||||
led.writeLow(); |
||||
|
||||
cli(); // disable interrupts
|
||||
attachInterrupt(InterruptNumber, owWaitResetInterrupt, CHANGE); |
||||
|
||||
// set timer0 interrupt at 250KHz (actually depends on compare match register OCR0A)
|
||||
// 4us between each tick
|
||||
TCCR1A = 0; |
||||
TCCR1B = 0; |
||||
TCNT1 = 0; // initialize counter value to 0
|
||||
//TCCR1B |= (1 << WGM12); // turn on CTC mode
|
||||
//TCCR1B |= (1 << CS11) | (1 << CS10); // Set 64 prescaler
|
||||
TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt
|
||||
sei(); // enable interrupts
|
||||
|
||||
Serial.begin(9600); |
||||
} |
||||
|
||||
int count = 0; |
||||
void loop() |
||||
{ |
||||
if ((count++) % 1000 == 0) |
||||
led.write(!led.read()); |
||||
cli();//disable interrupts
|
||||
SerialChannel::swap(); |
||||
sei();//enable interrupts
|
||||
|
||||
SerialChannel::flush(); |
||||
} |
||||
|
||||
void(*timerEvent)() = 0; |
||||
void setTimerEvent(short microSecondsDelay, void(*event)()) |
||||
{ |
||||
microSecondsDelay -= 10; // this seems to be the typical time taken to initialize the timer on Arduino Uno
|
||||
|
||||
short skipTicks = (microSecondsDelay - 3) / 4; // round the micro seconds delay to a number of ticks to skip (4us per tick, so 4us must skip 0 tick, 8us must skip 1 tick, etc.)
|
||||
if (skipTicks < 1) skipTicks = 1; |
||||
//debug.SC_APPEND_STR_INT("setTimerEvent", (long)skipTicks);
|
||||
TCNT1 = 0; |
||||
OCR1A = skipTicks; |
||||
timerEvent = event; |
||||
TCCR1B = (1 << WGM12) | (1 << CS11) | (1 << CS10); // turn on CTC mode with 64 prescaler
|
||||
} |
||||
|
||||
void owError(const char* message) |
||||
{ |
||||
debug.append(message); |
||||
led.writeHigh(); |
||||
status = OS_WaitReset; |
||||
} |
||||
|
||||
void owClearError() |
||||
{ |
||||
led.writeLow(); |
||||
} |
||||
|
||||
void owWaitResetInterrupt() |
||||
{ |
||||
onEnterInterrupt(); |
||||
bool state = owPin.read(); |
||||
unsigned long now = micros(); |
||||
if (state) |
||||
{ |
||||
if (resetStart == (unsigned int)-1) |
||||
{ |
||||
onLeaveInterrupt(); |
||||
return; |
||||
} |
||||
|
||||
unsigned long resetDuration = now - resetStart; |
||||
resetStart = (unsigned int)-1; |
||||
if (resetDuration >= ResetMinDuration) |
||||
{ |
||||
if (resetDuration > ResetMaxDuration) |
||||
{ |
||||
owError("Reset too long"); |
||||
onLeaveInterrupt(); |
||||
return; |
||||
} |
||||
|
||||
owClearError(); |
||||
lastReset = now; |
||||
status = OS_Presence; |
||||
setTimerEvent(PresenceWaitDuration - (micros() - now), &beginPresence); |
||||
} |
||||
} |
||||
else |
||||
{ |
||||
resetStart = now; |
||||
} |
||||
onLeaveInterrupt(); |
||||
} |
||||
|
||||
//bool debugState = false;
|
||||
volatile unsigned long lastInterrupt = 0; |
||||
void owReceiveCommandInterrupt(void) |
||||
{ |
||||
onEnterInterrupt(); |
||||
unsigned long now = micros(); |
||||
if (now < lastInterrupt + 20) |
||||
{ |
||||
onLeaveInterrupt(); |
||||
return; // don't react to our own actions
|
||||
} |
||||
lastInterrupt = now; |
||||
|
||||
//debugState = !debugState;
|
||||
//owOutTestPin.write(debugState);
|
||||
|
||||
//led.write(state);
|
||||
|
||||
bool bit = readBit(); |
||||
/*if (bit)
|
||||
debug.SC_APPEND_STR("received bit 1"); |
||||
else |
||||
debug.SC_APPEND_STR("received bit 0");*/ |
||||
|
||||
receivingByte |= ((bit ? 1 : 0) << receivingBitPos); |
||||
++receivingBitPos; |
||||
|
||||
if (receivingBitPos == 8) |
||||
{ |
||||
byte receivedByte = receivingByte; |
||||
receivingBitPos = 0; |
||||
receivingByte = 0; |
||||
//debug.SC_APPEND_STR_INT("received byte", (long)receivedByte);
|
||||
|
||||
if (status == OS_WaitCommand && receivedByte == 0xF0) |
||||
{ |
||||
status = OS_SearchRom; |
||||
searchROMReadingMasterResponseBit = false; |
||||
searchROMSendingInverse = false; |
||||
searchROMCurrentByte = 0; |
||||
searchROMCurrentBit = 0; |
||||
byte currentByte = owROM[searchROMCurrentByte]; |
||||
searchRomNextBit = bitRead(currentByte, searchROMCurrentBit); |
||||
searchRomNextBitToSend = searchROMSendingInverse ? !searchRomNextBit : searchRomNextBit; |
||||
//attachInterrupt(InterruptNumber, onewireInterruptSearchROM, FALLING);
|
||||
setTimerEvent(10, owSearchSendBit); |
||||
detachInterrupt(InterruptNumber); |
||||
onLeaveInterrupt(); |
||||
return; |
||||
} |
||||
} |
||||
|
||||
onLeaveInterrupt(); |
||||
} |
||||
|
||||
bool ignoreNextFallingEdge = false; |
||||
|
||||
void owSearchSendBit() |
||||
{ |
||||
onEnterInterrupt(); |
||||
|
||||
// wait for a falling edge (active wait is more reliable than interrupts to send the bit fast enough)
|
||||
while (!owPin.read()); |
||||
while (owPin.read()); |
||||
|
||||
//sendBit(searchRomNextBitToSend);
|
||||
if (searchRomNextBitToSend) |
||||
{ |
||||
//delayMicroseconds(SendBitDuration);
|
||||
ignoreNextFallingEdge = false; |
||||
} |
||||
else |
||||
{ |
||||
owPullLow(); |
||||
//delayMicroseconds(SendBitDuration);
|
||||
//owRelease();
|
||||
ignoreNextFallingEdge = true; |
||||
} |
||||
|
||||
unsigned long sendBitStart = micros(); |
||||
|
||||
if (searchROMSendingInverse) |
||||
{ |
||||
searchROMSendingInverse = false; |
||||
searchROMReadingMasterResponseBit = true; |
||||
} |
||||
else |
||||
{ |
||||
searchROMSendingInverse = true; |
||||
} |
||||
|
||||
byte currentByte = owROM[searchROMCurrentByte]; |
||||
searchRomNextBit = bitRead(currentByte, searchROMCurrentBit); |
||||
searchRomNextBitToSend = searchROMSendingInverse ? !searchRomNextBit : searchRomNextBit; |
||||
|
||||
if (searchROMReadingMasterResponseBit) |
||||
{ |
||||
ignoreNextFallingEdge = true; |
||||
attachInterrupt(InterruptNumber, onewireInterruptSearchROM, FALLING); |
||||
} |
||||
else |
||||
{ |
||||
setTimerEvent(10, owSearchSendBit); |
||||
} |
||||
|
||||
delayMicroseconds(SendBitDuration - (micros() - sendBitStart)); |
||||
owRelease(); |
||||
|
||||
onLeaveInterrupt(); |
||||
} |
||||
|
||||
void onewireInterruptSearchROM() |
||||
{ |
||||
onEnterInterrupt(); |
||||
|
||||
if (ignoreNextFallingEdge) |
||||
{ |
||||
ignoreNextFallingEdge = false; |
||||
onLeaveInterrupt(); |
||||
return; |
||||
} |
||||
|
||||
if (searchROMReadingMasterResponseBit) |
||||
{ |
||||
bool bit = readBit(); |
||||
|
||||
if (bit != searchRomNextBit) |
||||
{ |
||||
debug.SC_APPEND_STR("Master didn't send our bit, leaving ROM search"); |
||||
status = OS_WaitReset; |
||||
attachInterrupt(InterruptNumber, owWaitResetInterrupt, CHANGE); |
||||
onLeaveInterrupt(); |
||||
return; |
||||
} |
||||
|
||||
searchROMReadingMasterResponseBit = false; |
||||
++searchROMCurrentBit; |
||||
if (searchROMCurrentBit == 8) |
||||
{ |
||||
++searchROMCurrentByte; |
||||
searchROMCurrentBit = 0; |
||||
//debug.SC_APPEND_STR("sent another ROM byte");
|
||||
} |
||||
|
||||
if (searchROMCurrentByte == 8) |
||||
{ |
||||
searchROMCurrentByte = 0; |
||||
status = OS_WaitReset; |
||||
debug.SC_APPEND_STR("ROM sent entirely"); |
||||
attachInterrupt(InterruptNumber, owWaitResetInterrupt, CHANGE); |
||||
onLeaveInterrupt(); |
||||
return; |
||||
} |
||||
|
||||
byte currentByte = owROM[searchROMCurrentByte]; |
||||
searchRomNextBit = bitRead(currentByte, searchROMCurrentBit); |
||||
searchRomNextBitToSend = searchROMSendingInverse ? !searchRomNextBit : searchRomNextBit; |
||||
|
||||
setTimerEvent(10, owSearchSendBit); |
||||
detachInterrupt(InterruptNumber); |
||||
onLeaveInterrupt(); |
||||
return; |
||||
} |
||||
else |
||||
{ |
||||
sendBit(searchRomNextBitToSend); |
||||
/*if (bitToSend)
|
||||
debug.SC_APPEND_STR("sent ROM search bit : 1"); |
||||
else |
||||
debug.SC_APPEND_STR("sent ROM search bit : 0");*/ |
||||
|
||||
if (searchROMSendingInverse) |
||||
{ |
||||
searchROMSendingInverse = false; |
||||
searchROMReadingMasterResponseBit = true; |
||||
} |
||||
else |
||||
{ |
||||
searchROMSendingInverse = true; |
||||
} |
||||
} |
||||
|
||||
byte currentByte = owROM[searchROMCurrentByte]; |
||||
searchRomNextBit = bitRead(currentByte, searchROMCurrentBit); |
||||
searchRomNextBitToSend = searchROMSendingInverse ? !searchRomNextBit : searchRomNextBit; |
||||
|
||||
onLeaveInterrupt(); |
||||
} |
||||
|
||||
bool readBit() |
||||
{ |
||||
delayMicroseconds(ReadBitSamplingTime); |
||||
bool bit = owPin.read(); |
||||
//owOutTestPin.write(!owOutTestPin.read());
|
||||
//owOutTestPin.write(!owOutTestPin.read());
|
||||
return bit; |
||||
} |
||||
|
||||
void sendBit(bool bit) |
||||
{ |
||||
if (bit) |
||||
{ |
||||
delayMicroseconds(SendBitDuration); |
||||
ignoreNextFallingEdge = false; |
||||
} |
||||
else |
||||
{ |
||||
owPullLow(); |
||||
delayMicroseconds(SendBitDuration); |
||||
owRelease(); |
||||
ignoreNextFallingEdge = true; |
||||
} |
||||
} |
||||
|
||||
void beginPresence() |
||||
{ |
||||
unsigned long now = micros(); |
||||
owPullLow(); |
||||
setTimerEvent(PresenceDuration, &endPresence); |
||||
debug.SC_APPEND_STR_TIME("reset", lastReset); |
||||
debug.SC_APPEND_STR_TIME("beginPresence", now); |
||||
} |
||||
|
||||
void endPresence() |
||||
{ |
||||
unsigned long now = micros(); |
||||
owRelease(); |
||||
debug.SC_APPEND_STR_TIME("endPresence", now); |
||||
|
||||
status = OS_WaitCommand; |
||||
attachInterrupt(InterruptNumber, owReceiveCommandInterrupt, FALLING); |
||||
receivingByte = 0; |
||||
receivingBitPos = 0; |
||||
bitStart = (unsigned long)-1; |
||||
} |
||||
|
||||
ISR(TIMER1_COMPA_vect) // timer1 interrupt
|
||||
{ |
||||
TCCR1B = 0; // disable clock
|
||||
void(*event)() = timerEvent; |
||||
timerEvent = 0; |
||||
if (event != 0) |
||||
event(); |
||||
} |
||||
|
||||
uint8_t crc8(char addr[], uint8_t len) { |
||||
uint8_t crc = 0; |
||||
|
||||
while (len--) { |
||||
uint8_t inbyte = *addr++; |
||||
for (uint8_t i = 8; i; i--) { |
||||
uint8_t mix = (crc ^ inbyte) & 0x01; |
||||
crc >>= 1; |
||||
if (mix) crc ^= 0x8C; |
||||
inbyte >>= 1; |
||||
} |
||||
} |
||||
return crc; |
||||
} |
@ -1,117 +0,0 @@
|
||||
#include "Arduino.h" |
||||
#include "LowLevel.h" |
||||
#include "SerialChannel.h" |
||||
#include "OneWireSlave.h" |
||||
|
||||
#define LEDPin 13 |
||||
#define OWPin 2 |
||||
|
||||
#ifdef ENABLE_SERIAL_CHANNEL |
||||
SerialChannel debug("debug"); |
||||
#endif |
||||
|
||||
Pin led(LEDPin); |
||||
|
||||
byte owROM[7] = { 0xE2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 }; |
||||
|
||||
byte acknowledge = 0x42; |
||||
byte commandCheckError = 0xF1; |
||||
|
||||
int turnOnTimeoutSeconds = 5 * 60; |
||||
|
||||
const byte CMD_TurnOn = 0x01; |
||||
const byte CMD_TurnOff = 0x02; |
||||
const byte CMD_ReadState = 0x03; |
||||
|
||||
const byte ANS_StateIsOn = 0x01; |
||||
const byte ANS_StateIsOff = 0x02; |
||||
|
||||
volatile byte command = 0x0; |
||||
volatile long turnOnStartTime = 0; |
||||
|
||||
void owReceive(OneWireSlave::ReceiveEvent evt, byte data); |
||||
|
||||
void setup() |
||||
{ |
||||
led.outputMode(); |
||||
led.writeLow(); |
||||
|
||||
OneWire.setReceiveCallback(&owReceive); |
||||
OneWire.begin(owROM, OWPin); |
||||
|
||||
Serial.begin(9600); |
||||
} |
||||
|
||||
void loop() |
||||
{ |
||||
delay(1); |
||||
|
||||
long now = millis(); |
||||
if (now > turnOnStartTime + (long)turnOnTimeoutSeconds * 1000 || now < turnOnStartTime) |
||||
{ |
||||
led.writeLow(); |
||||
turnOnStartTime = 0; |
||||
} |
||||
|
||||
cli();//disable interrupts
|
||||
#ifdef ENABLE_SERIAL_CHANNEL |
||||
SerialChannel::swap(); |
||||
#endif |
||||
sei();//enable interrupts
|
||||
|
||||
#ifdef ENABLE_SERIAL_CHANNEL |
||||
SerialChannel::flush(); |
||||
#endif |
||||
} |
||||
|
||||
void owReceive(OneWireSlave::ReceiveEvent evt, byte data) |
||||
{ |
||||
switch (evt) |
||||
{ |
||||
case OneWireSlave::RE_Byte: |
||||
if (command == 0x00) |
||||
{ |
||||
command = data; |
||||
} |
||||
else |
||||
{ |
||||
if (data == (0xFF-command)) // to check there is no communication error, the master must send the command, and then its inverse
|
||||
{ |
||||
byte receivedCommand = command; |
||||
command = 0x0; |
||||
switch (receivedCommand) |
||||
{ |
||||
case CMD_TurnOn: |
||||
turnOnStartTime = millis(); |
||||
led.writeHigh(); |
||||
OneWire.write(&acknowledge, 1, NULL); |
||||
break; |
||||
case CMD_TurnOff: |
||||
led.writeLow(); |
||||
turnOnStartTime = 0; |
||||
OneWire.write(&acknowledge, 1, NULL); |
||||
break; |
||||
case CMD_ReadState: |
||||
bool state = led.read(); |
||||
static byte response[2]; |
||||
response[0] = state ? ANS_StateIsOn : ANS_StateIsOff; |
||||
response[1] = 0xFF - response[0]; |
||||
OneWire.write(response, 2, NULL); |
||||
break; |
||||
} |
||||
} |
||||
else |
||||
{ |
||||
command = 0x0; |
||||
OneWire.write(&commandCheckError, 1, NULL); |
||||
} |
||||
} |
||||
break; |
||||
case OneWireSlave::RE_Reset: |
||||
case OneWireSlave::RE_Error: |
||||
command = 0x0; |
||||
break; |
||||
default: |
||||
; |
||||
} |
||||
} |
@ -1,15 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
<ItemGroup> |
||||
<ClCompile Include="SerialChannel.cpp" /> |
||||
<ClCompile Include="OneWireSlave.cpp" /> |
||||
</ItemGroup> |
||||
<ItemGroup> |
||||
<ClInclude Include="SerialChannel.h" /> |
||||
<ClInclude Include="LowLevel.h" /> |
||||
<ClInclude Include="OneWireSlave.h" /> |
||||
</ItemGroup> |
||||
<ItemGroup> |
||||
<CustomBuild Include="OneWireIO.ino" /> |
||||
<CustomBuild Include="OneWireIO_Demo.ino" /> |
||||
</ItemGroup> |
||||
</Project> |
@ -0,0 +1,75 @@
|
||||
#include "Arduino.h" |
||||
#include "LowLevel.h" |
||||
#include "OneWireSlave.h" |
||||
|
||||
#define LEDPin 13 |
||||
|
||||
// This is the pin that will be used for one-wire data (depending on your arduino model, you are limited to a few choices, because some pins don't have complete interrupt support)
|
||||
// On Arduino Uno, you can use pin 2 or pin 3
|
||||
#define OWPin 2 |
||||
|
||||
Pin led(LEDPin); |
||||
|
||||
// This is the ROM the arduino will respond to, make sure it doesn't conflict with another device
|
||||
byte owROM[7] = { 0xE2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 }; |
||||
|
||||
byte acknowledge = 0x42; |
||||
|
||||
// This sample implements a simple protocol : sending match ROM, then the ROM, then 0x01 will turn the arduino light on. Sending 0x02 will turn it off. In each case, the byte 0x42 is sent as acknowledgement.
|
||||
const byte CMD_TurnOn = 0x01; |
||||
const byte CMD_TurnOff = 0x02; |
||||
|
||||
// This function will be called each time the OneWire library has an event to notify (reset, error, byte received)
|
||||
void owReceive(OneWireSlave::ReceiveEvent evt, byte data); |
||||
|
||||
void setup() |
||||
{ |
||||
led.outputMode(); |
||||
led.writeLow(); |
||||
|
||||
// Setup the OneWire library
|
||||
OneWire.setReceiveCallback(&owReceive); |
||||
OneWire.begin(owROM, OWPin); |
||||
} |
||||
|
||||
void loop() |
||||
{ |
||||
delay(1); |
||||
|
||||
// You can do anything you want here, the OneWire library works entirely in background, using interrupts.
|
||||
|
||||
cli();//disable interrupts
|
||||
// Be sure to not block interrupts for too long, OneWire timing is very tight for some operations. 1 or 2 microseconds (yes, microseconds, not milliseconds) can be too much depending on your master controller, but then it's equally unlikely that you block exactly at the moment where it matters.
|
||||
// This can be mitigated by using error checking and retry in your high-level communication protocol. A good thing to do anyway.
|
||||
sei();//enable interrupts
|
||||
} |
||||
|
||||
void owReceive(OneWireSlave::ReceiveEvent evt, byte data) |
||||
{ |
||||
switch (evt) |
||||
{ |
||||
case OneWireSlave::RE_Byte: |
||||
if (data == CMD_TurnOn) |
||||
{ |
||||
led.writeHigh(); |
||||
} |
||||
else if (data == CMD_TurnOff) |
||||
{ |
||||
led.writeLow(); |
||||
} |
||||
else |
||||
{ |
||||
break; |
||||
} |
||||
|
||||
// in this simple example we just reply with one byte to say we've processed the command
|
||||
// a real application should have a CRC system to ensure messages are not corrupt, for both directions
|
||||
// you can use the static OneWireSlave::crc8 method to add CRC checks in your communication protocol (it conforms to standard one-wire CRC checks, that is used to compute the ROM last byte for example)
|
||||
OneWire.write(&acknowledge, 1, NULL); |
||||
|
||||
break; |
||||
|
||||
default: |
||||
; // we could also react to reset and error notifications, but not in this sample
|
||||
} |
||||
} |
@ -1,170 +0,0 @@
|
||||
#include "Arduino.h" |
||||
#include "SerialChannel.h" |
||||
|
||||
#ifdef ENABLE_SERIAL_CHANNEL |
||||
|
||||
byte SerialChannel::nextId = 1; |
||||
SerialChannel* SerialChannel::first = 0; |
||||
|
||||
SerialChannel::Message SerialChannel::buffer1[SerialChannel::MaxPendingMessages]; |
||||
SerialChannel::Message SerialChannel::buffer2[SerialChannel::MaxPendingMessages]; |
||||
volatile SerialChannel::Message* SerialChannel::backBuffer; |
||||
volatile byte SerialChannel::backBufferPos; |
||||
byte SerialChannel::frontBufferSize; |
||||
|
||||
SerialChannel::Message dummyMessage; |
||||
|
||||
SerialChannel::SerialChannel(const char* name_) |
||||
: next(0) |
||||
, id((byte)-1) |
||||
, name(name_) |
||||
{ |
||||
if(first == 0) |
||||
first = this; |
||||
else |
||||
{ |
||||
SerialChannel* c = first; |
||||
while(c->next != 0) c = c->next; |
||||
c->next = this; |
||||
} |
||||
|
||||
id = nextId++; |
||||
} |
||||
|
||||
void SerialChannel::beginWriteInChannel(byte id, short byteCount, unsigned long time) |
||||
{ |
||||
Serial.write("START"); |
||||
Serial.write(id); |
||||
writeULong(time); |
||||
writeShort(byteCount); |
||||
} |
||||
|
||||
void SerialChannel::write(byte* data, short byteCount, unsigned long time) |
||||
{ |
||||
beginWrite(byteCount, time); |
||||
continueWrite(data, byteCount); |
||||
} |
||||
|
||||
void SerialChannel::beginWrite(short byteCount, unsigned long time) |
||||
{ |
||||
if (time == (unsigned long)-1) |
||||
time = micros(); |
||||
|
||||
handleConnection(); |
||||
|
||||
beginWriteInChannel(id, byteCount, time); |
||||
} |
||||
|
||||
void SerialChannel::continueWrite(byte* data, short byteCount) |
||||
{ |
||||
Serial.write(data, byteCount); |
||||
} |
||||
|
||||
void SerialChannel::write(const char* text, unsigned long time) |
||||
{ |
||||
write((byte*)text, strlen(text), time); |
||||
} |
||||
|
||||
SerialChannel::Message& SerialChannel::append(byte* data, short byteCount, unsigned long time) |
||||
{ |
||||
if (time == (unsigned long)-1) |
||||
time = micros(); |
||||
|
||||
if (backBufferPos >= MaxPendingMessages) |
||||
{ |
||||
Message& msg = ((Message*)backBuffer)[MaxPendingMessages-1]; |
||||
msg.id = id; |
||||
msg.data = (byte*)"OVERFLOW"; |
||||
msg.byteCount = 8; |
||||
msg.time = time; |
||||
msg.longArg0 = 0; |
||||
|
||||
return dummyMessage; |
||||
} |
||||
else |
||||
{ |
||||
Message& msg = ((Message*)backBuffer)[backBufferPos++]; |
||||
msg.id = id; |
||||
msg.data = data; |
||||
msg.byteCount = byteCount; |
||||
msg.time = time; |
||||
msg.longArg0 = 0; |
||||
|
||||
return msg; |
||||
} |
||||
} |
||||
|
||||
void SerialChannel::append(const char* text, unsigned long time) |
||||
{ |
||||
append((byte*)text, strlen(text), time); |
||||
} |
||||
|
||||
void SerialChannel::appendInt(const char* text, short textLength, int arg0, unsigned long time) |
||||
{ |
||||
Message& msg = append((byte*)text, textLength, time); |
||||
msg.longArg0 = arg0 | 0x40000000; |
||||
} |
||||
|
||||
void SerialChannel::swap() |
||||
{ |
||||
backBuffer = backBuffer == buffer1 ? buffer2 : buffer1; |
||||
frontBufferSize = backBufferPos; |
||||
backBufferPos = 0; |
||||
} |
||||
|
||||
void SerialChannel::flush() |
||||
{ |
||||
handleConnection(); |
||||
|
||||
Message* frontBuffer = backBuffer == buffer1 ? buffer2 : buffer1; |
||||
for (Message* msg = frontBuffer; msg < frontBuffer + frontBufferSize; ++msg) |
||||
{ |
||||
char params[32]; |
||||
params[0] = 0; |
||||
|
||||
if ((msg->longArg0 & 0x40000000) != 0) |
||||
sprintf(params, ";arg0=%ld", msg->longArg0 & ~0x40000000); |
||||
|
||||
short paramsSize = strlen(params); |
||||
|
||||
beginWriteInChannel(msg->id, msg->byteCount + paramsSize, msg->time); |
||||
Serial.write(msg->data, msg->byteCount); |
||||
if (paramsSize > 0) |
||||
Serial.write(params, paramsSize); |
||||
|
||||
Serial.flush(); |
||||
} |
||||
} |
||||
|
||||
void SerialChannel::writeShort(short num) |
||||
{ |
||||
Serial.write((byte*)&num, 2); |
||||
} |
||||
|
||||
void SerialChannel::writeULong(unsigned long num) |
||||
{ |
||||
Serial.write((byte*)&num, 4); |
||||
} |
||||
|
||||
void SerialChannel::handleConnection() |
||||
{ |
||||
int b = Serial.read(); |
||||
if(b == (int)'C') |
||||
{ |
||||
Serial.write("CONNECTION"); |
||||
SerialChannel* c = first; |
||||
while(c) |
||||
{ |
||||
Serial.write("START"); |
||||
Serial.write(0); |
||||
Serial.write("ChannelInit"); |
||||
Serial.write(c->id); |
||||
writeShort(strlen(c->name)); |
||||
Serial.write(c->name); |
||||
c = c->next; |
||||
} |
||||
Serial.flush(); |
||||
} |
||||
} |
||||
|
||||
#endif //ENABLE_SERIAL_CHANNEL
|
@ -1,64 +0,0 @@
|
||||
#ifndef _SerialChannel_h_ |
||||
#define _SerialChannel_h_ |
||||
|
||||
//#define ENABLE_SERIAL_CHANNEL
|
||||
|
||||
#ifdef ENABLE_SERIAL_CHANNEL |
||||
#define SC_APPEND_STR(str) append((byte*)str, sizeof(str)-1) |
||||
#define SC_APPEND_STR_INT(str, arg0) appendInt(str, sizeof(str)-1, arg0) |
||||
|
||||
#define SC_APPEND_STR_TIME(str, time) append((byte*)str, sizeof(str)-1, time) |
||||
|
||||
class SerialChannel |
||||
{ |
||||
public: |
||||
struct Message |
||||
{ |
||||
unsigned long time; |
||||
long longArg0; |
||||
byte* data; |
||||
short byteCount; |
||||
byte id; |
||||
}; |
||||
|
||||
private: |
||||
static const byte MaxPendingMessages = 32; |
||||
|
||||
static SerialChannel* first; |
||||
SerialChannel* next; |
||||
|
||||
static byte nextId; |
||||
byte id; |
||||
const char* name; |
||||
|
||||
static Message buffer1[MaxPendingMessages]; |
||||
static Message buffer2[MaxPendingMessages]; |
||||
static volatile Message* backBuffer; |
||||
static volatile byte backBufferPos; |
||||
static byte frontBufferSize; |
||||
|
||||
public: |
||||
SerialChannel(const char* name_); |
||||
|
||||
static void beginWriteInChannel(byte id, short byteCount, unsigned long time); |
||||
void write(byte* data, short byteCount, unsigned long time = (unsigned long)-1); |
||||
void write(const char* text, unsigned long time = (unsigned long)-1); |
||||
|
||||
void beginWrite(short byteCount, unsigned long time = (unsigned long)-1); |
||||
void continueWrite(byte* data, short byteCount); |
||||
|
||||
Message& append(byte* data, short byteCount, unsigned long time = (unsigned long)-1); |
||||
void append(const char* text, unsigned long time = (unsigned long)-1); |
||||
void appendInt(const char* text, short textLength, int arg0, unsigned long time = (unsigned long)-1); |
||||
static void swap(); |
||||
static void flush(); |
||||
|
||||
private: |
||||
static void handleConnection(); |
||||
static void writeShort(short num); |
||||
static void writeULong(unsigned long num); |
||||
}; |
||||
#endif // ENABLE_SERIAL_CHANNEL
|
||||
|
||||
#endif |
||||
|
@ -1,22 +0,0 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00 |
||||
# Visual Studio 2013 |
||||
VisualStudioVersion = 12.0.31101.0 |
||||
MinimumVisualStudioVersion = 10.0.40219.1 |
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SerialMonitor", "SerialMonitor\SerialMonitor.csproj", "{97704F53-6CA1-4155-9E8F-AEBFEEC20A8B}" |
||||
EndProject |
||||
Global |
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution |
||||
Debug|Any CPU = Debug|Any CPU |
||||
Release|Any CPU = Release|Any CPU |
||||
EndGlobalSection |
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution |
||||
{97704F53-6CA1-4155-9E8F-AEBFEEC20A8B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |
||||
{97704F53-6CA1-4155-9E8F-AEBFEEC20A8B}.Debug|Any CPU.Build.0 = Debug|Any CPU |
||||
{97704F53-6CA1-4155-9E8F-AEBFEEC20A8B}.Release|Any CPU.ActiveCfg = Release|Any CPU |
||||
{97704F53-6CA1-4155-9E8F-AEBFEEC20A8B}.Release|Any CPU.Build.0 = Release|Any CPU |
||||
EndGlobalSection |
||||
GlobalSection(SolutionProperties) = preSolution |
||||
HideSolutionNode = FALSE |
||||
EndGlobalSection |
||||
EndGlobal |
@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?> |
||||
<configuration> |
||||
<startup> |
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> |
||||
</startup> |
||||
</configuration> |
@ -1,18 +0,0 @@
|
||||
<Application x:Class="SerialMonitor.App" |
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" |
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
||||
StartupUri="MainWindow.xaml"> |
||||
<Application.Resources> |
||||
|
||||
<Style x:Key="Console" TargetType="{x:Type TextBlock}"> |
||||
<Setter Property="TextWrapping" Value="NoWrap"/> |
||||
<Setter Property="TextTrimming" Value="None"/> |
||||
<Setter Property="Foreground" Value="White"/> |
||||
<Setter Property="Background" Value="Black"/> |
||||
</Style> |
||||
<Style x:Key="Oscilloscope" TargetType="{x:Type Canvas}"> |
||||
<Setter Property="Background" Value="White"/> |
||||
</Style> |
||||
|
||||
</Application.Resources> |
||||
</Application> |
@ -1,94 +0,0 @@
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Configuration; |
||||
using System.Data; |
||||
using System.Diagnostics; |
||||
using System.IO.Ports; |
||||
using System.Linq; |
||||
using System.Text; |
||||
using System.Threading.Tasks; |
||||
using System.Windows; |
||||
using System.Windows.Shapes; |
||||
using System.Windows.Threading; |
||||
|
||||
namespace SerialMonitor |
||||
{ |
||||
/// <summary> |
||||
/// Interaction logic for App.xaml |
||||
/// </summary> |
||||
public partial class App : Application |
||||
{ |
||||
private SerialPort Serial; |
||||
private IDictionary<int, string> Channels = new Dictionary<int, string>(); |
||||
private bool Connected = false; |
||||
|
||||
protected override void OnStartup(StartupEventArgs e) |
||||
{ |
||||
base.OnStartup(e); |
||||
|
||||
Serial = new SerialPort(); |
||||
Serial.PortName = "COM4"; |
||||
Serial.BaudRate = 9600; |
||||
|
||||
Serial.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(OnDataReceived); |
||||
|
||||
Serial.Open(); |
||||
Serial.Write("C"); |
||||
} |
||||
|
||||
private void OnDataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e) |
||||
{ |
||||
if (!Connected) |
||||
{ |
||||
Serial.ReadTo("CONNECTION"); |
||||
Connected = true; |
||||
} |
||||
|
||||
// channel declaration message: byte 0 ; string "ChannelInit" ; byte <channel id> ; short <length> ; byte[length] <channel name> |
||||
// regular message: byte <channel id> ; short <length> ; byte[length] <data> |
||||
|
||||
SerialMessage message = null; |
||||
|
||||
Serial.ReadTo("START"); |
||||
|
||||
int id = Serial.ReadByte(); |
||||
if (id == 0) |
||||
{ |
||||
string check = Encoding.UTF8.GetString(Serial.ReadBytes(11)); |
||||
if (check != "ChannelInit") |
||||
throw new Exception("Incorrect data check for channel initialization"); |
||||
id = Serial.ReadByte(); |
||||
int length = Serial.ReadShort(); |
||||
string name = Encoding.UTF8.GetString(Serial.ReadBytes(length)); |
||||
Channels[id] = name; |
||||
message = new SerialMessage { ChannelName = "debug", StringData = "Channel " + name + " opened", SendTime = 0 }; |
||||
} |
||||
else |
||||
{ |
||||
ulong sendTime = Serial.ReadULong(); |
||||
int length = Serial.ReadShort(); |
||||
byte[] data = Serial.ReadBytes(length); |
||||
message = new SerialMessage { ChannelName = Channels[id], Data = data, SendTime = sendTime }; |
||||
} |
||||
|
||||
if(message != null) |
||||
Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() => OnMessageReceived(message))); |
||||
} |
||||
|
||||
private void OnMessageReceived(SerialMessage message) |
||||
{ |
||||
switch (message.ChannelName) |
||||
{ |
||||
case "debug": |
||||
string text = ((float)message.SendTime / 1000000).ToString("0.000000") + "s " + message.StringData; |
||||
//Debug.WriteLine(text); |
||||
MainWindowContext.Get.WriteLine(text); |
||||
break; |
||||
case "oscilloscope": |
||||
byte frequency = message.Data[0]; |
||||
MainWindowContext.Get.AddSequence(message.SendTime, frequency, message.Data.Skip(1).ToArray()); |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
} |
@ -1,18 +0,0 @@
|
||||
<Window x:Class="SerialMonitor.MainWindow" |
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" |
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
||||
Title="Serial Monitor" Height="490.75" Width="923"> |
||||
<Grid> |
||||
<Grid.RowDefinitions> |
||||
<RowDefinition Height="290" /> |
||||
<RowDefinition Height="5" /> |
||||
<RowDefinition Height="*" /> |
||||
</Grid.RowDefinitions> |
||||
<Canvas Name="Oscilloscope" Style="{DynamicResource Oscilloscope}" Margin="0,0,0,17" /> |
||||
<ScrollBar Grid.Row="0" Orientation="Horizontal" HorizontalAlignment="Stretch" Height="17" VerticalAlignment="Bottom" Minimum="{Binding MinTime}" Maximum="{Binding MaxTime}" ViewportSize="{Binding ViewportTimeWidth}" Value="{Binding ScrollValue}"/> |
||||
<GridSplitter Grid.Row="1" Height="5" HorizontalAlignment="Stretch" /> |
||||
<ScrollViewer Grid.Row="2"> |
||||
<TextBlock TextWrapping="Wrap" Style="{DynamicResource Console}" Text="{Binding ConsoleText}"/> |
||||
</ScrollViewer> |
||||
</Grid> |
||||
</Window> |
@ -1,62 +0,0 @@
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Linq; |
||||
using System.Text; |
||||
using System.Threading.Tasks; |
||||
using System.Windows; |
||||
using System.Windows.Controls; |
||||
using System.Windows.Data; |
||||
using System.Windows.Documents; |
||||
using System.Windows.Input; |
||||
using System.Windows.Media; |
||||
using System.Windows.Media.Imaging; |
||||
using System.Windows.Navigation; |
||||
using System.Windows.Shapes; |
||||
|
||||
namespace SerialMonitor |
||||
{ |
||||
/// <summary> |
||||
/// Interaction logic for MainWindow.xaml |
||||
/// </summary> |
||||
public partial class MainWindow : Window |
||||
{ |
||||
public MainWindow() |
||||
{ |
||||
InitializeComponent(); |
||||
var context = new MainWindowContext(this); |
||||
context.WriteLine("Connecting..."); |
||||
this.DataContext = context; |
||||
|
||||
this.MouseWheel += OnMouseWheel; |
||||
} |
||||
|
||||
private void OnMouseWheel(object sender, MouseWheelEventArgs e) |
||||
{ |
||||
MainWindowContext context = (MainWindowContext)this.DataContext; |
||||
Point cursorPos = e.GetPosition(context.OscilloscopeCanvas); |
||||
if (cursorPos.X < 0 || cursorPos.X > context.OscilloscopeCanvas.ActualWidth || cursorPos.Y < 0 || cursorPos.Y > context.OscilloscopeCanvas.ActualHeight) |
||||
return; |
||||
|
||||
double cursorPosRatio = cursorPos.X / context.OscilloscopeCanvas.ActualWidth; |
||||
double cursorTime = context.ViewportStartTime + cursorPosRatio * context.ViewportTimeWidth; |
||||
|
||||
double newTimeWidth = context.ViewportTimeWidth; |
||||
if (e.Delta > 0) |
||||
newTimeWidth /= e.Delta * 0.01; |
||||
else if (e.Delta < 0) |
||||
newTimeWidth *= -e.Delta * 0.01; |
||||
|
||||
double totalTimeWidth = Math.Max(0.1, context.MaxTime - context.MinTime); |
||||
if (newTimeWidth > totalTimeWidth) |
||||
newTimeWidth = totalTimeWidth; |
||||
|
||||
double newStartTime = cursorTime - cursorPosRatio * newTimeWidth; |
||||
if (newStartTime < context.MinTime) |
||||
newStartTime = context.MinTime; |
||||
if (newStartTime + newTimeWidth > context.MaxTime) |
||||
newStartTime = context.MaxTime - newTimeWidth; |
||||
|
||||
context.SetViewport(newStartTime, newTimeWidth); |
||||
} |
||||
} |
||||
} |
@ -1,222 +0,0 @@
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.ComponentModel; |
||||
using System.Diagnostics; |
||||
using System.Linq; |
||||
using System.Text; |
||||
using System.Threading.Tasks; |
||||
using System.Windows; |
||||
using System.Windows.Controls; |
||||
using System.Windows.Input; |
||||
using System.Windows.Shapes; |
||||
|
||||
namespace SerialMonitor |
||||
{ |
||||
public class MainWindowContext : INotifyPropertyChanged |
||||
{ |
||||
private MainWindow Window; |
||||
|
||||
public MainWindowContext(MainWindow window) |
||||
{ |
||||
Window = window; |
||||
Get = this; |
||||
} |
||||
|
||||
public static MainWindowContext Get { get; private set; } |
||||
|
||||
private List<string> Lines = new List<string>(); |
||||
public string ConsoleText { get { return String.Join("\n", Lines); } } |
||||
|
||||
public void WriteLine(string line) |
||||
{ |
||||
Lines.Add(line); |
||||
if (Lines.Count > 100) |
||||
Lines.RemoveAt(0); |
||||
OnPropertyChanged("ConsoleText"); |
||||
} |
||||
|
||||
public int MaxSequenceSize { get { return 2048; } } |
||||
|
||||
private class Sequence |
||||
{ |
||||
public ulong StartTime { get; set; } // time in microseconds of the first sample |
||||
public byte[] Data { get; set; } |
||||
public double Frequency { get; set; } // sampling rate (in samples per second) |
||||
|
||||
public class Comparer : IComparer<Sequence> |
||||
{ |
||||
public int Compare(Sequence x, Sequence y) |
||||
{ |
||||
return x.StartTime.CompareTo(y.StartTime); |
||||
} |
||||
} |
||||
} |
||||
double ValueToHeight(double time, byte value) |
||||
{ |
||||
//value = (byte)(Math.Sin(time * 100.0) * 127.0 + 128.0); |
||||
return 256.0 - (double)value + 10; |
||||
} |
||||
private SortedSet<Sequence> Sequences< |