Skip to content

Instantly share code, notes, and snippets.

@TylerOderkirk
Created June 16, 2015 00:59
Show Gist options
  • Save TylerOderkirk/c7d9e4f776d0f5f13647 to your computer and use it in GitHub Desktop.
Save TylerOderkirk/c7d9e4f776d0f5f13647 to your computer and use it in GitHub Desktop.
The Panstamp 'modem' sketch ported to use SoftwareSerial on an Uno
/*
* modem.pde
*
* Copyright (c) 2014 panStamp <contact@panstamp.com>
*
* This file is part of the panStamp project.
*
* panStamp is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* panStamp is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with panStamp; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
* USA
*
* Author: Daniel Berenguer
* Creation date: 15/02/2011
*
* Device:
* Serial gateway or modem
*
* Description:
* This is not a proper SWAP gateway but a simple transparent UART-RF
* interface. This device can be used from a serial console as hyperterminal
* or minicom. Wireless packets are passed to/from the host computer in ASCII
* format whilst configuration is done via AT commands.
*
* Visit our wiki for details about the protocol in case you want to develop
* your own PC library for this device.
*/
#define PANSTAMP_AVR 1
#include "ccpacket.h"
#include "modem.h"
#ifdef PANSTAMP_NRG
#include "timer1a0.h"
#define TIMER timer1a0
#define RESET_TIMER()
#define INIT_TIMER() TIMER.attachInterrupt(isrT1event)
#define START_TIMER() TIMER.start(1000)
#define STOP_TIMER() TIMER.stop()
#elif PANSTAMP_AVR
#include <avr/wdt.h>
#include "TimerOne.h"
byte t1Ticks = 0; // Timer 1 ticks
#define TIMER Timer1
#define RESET_TIMER() t1Ticks = 0
#define INIT_TIMER() TIMER.initialize(TIMER1_TICK_PERIOD_US); TIMER.attachInterrupt(isrT1event)
#define START_TIMER() RESET_TIMER(); TIMER.attachInterrupt(isrT1event)
#define STOP_TIMER() TIMER.detachInterrupt()
#endif
#include <SoftwareSerial.h>
SoftwareSerial softSerial(10, 11); // RX, TX
CCPACKET fakeRxPacket;
/**
* LED pin
*/
#define LEDPIN 13
byte charToHex(byte ch);
/**
* This function is called whenever a wireless packet is received
*/
void rfPacketReceived(CCPACKET *packet)
{
if (packet->length > 5)
{
rxPacket = packet;
packetAvailable = true;
}
}
/**
* isrT1event
*
* Timer1 interrupt routine
*/
void isrT1event(void)
{
#ifdef PANSTAMP_AVR
if (t1Ticks == MAX_SERIAL_SILENCE_TK)
{
#endif
// Detach Timer1 interrupt
STOP_TIMER();
RESET_TIMER();
// Pending "+++" command?
if (!strcmp(strSerial, AT_GOTO_CMDMODE))
{
///panstamp.rxOff(); // Disable wireless reception
Serial.println("OK-Command mode");
serMode = SERMODE_COMMAND;
}
memset(strSerial, 0, sizeof(strSerial));
len = 0;
#ifdef PANSTAMP_AVR
}
else
t1Ticks++;
#endif
}
/**
* handleSerialCmd
*
* Handle serial command received
*
* 'command' AT command received
*/
void handleSerialCmd(char* command)
{
byte i, len;
byte arrV[2];
CCPACKET packet;
ATQUERY atQuery = ATQUERY_REQUEST;
// Data mode?
if (serMode == SERMODE_DATA)
{
// Because data is sent via serial to the modem hex-encoded, it requires half the space. I.e. "41" is sent over the air as "A".
packet.length = strlen(command)/2;
if (packet.length > 0)
{
// Convert ASCII string into array of bytes
for(i=0 ; i<packet.length ; i++)
{
packet.data[i] = charToHex(command[i*2]) << 4;
packet.data[i] |= charToHex(command[i*2 + 1]);
}
// Send packet via RF
//Serial.print("Sending this many bytes: ");
//Serial.println(packet.length, DEC);
//softSerial.write(packet.length);
softSerial.write(packet.data, packet.length);
// Reference: https://github.com/panStamp/panstamp/wiki/panStamp-API#senddata
// First byte is destination addr
///panstamp.radio.sendData(packet);
}
}
// Command mode?
else // serMode = SERMODE_COMMAND
{
len = strlen(command);
if (len < 5)
{
// Basic attention command
if (!strcmp(strSerial, AT_ATTENTION))
Serial.println("OK");
// Reset modem
else if (!strcmp(strSerial, AT_RESET))
{
Serial.println("OK");
///panstamp.reset();
}
// Go to serial data mode
else if (!strcmp(strSerial, AT_GOTO_DATAMODE))
{
serMode = SERMODE_DATA;
Serial.println("OK-Data mode");
///panstamp.rxOn(); // Enable wireless reception
}
}
// Set new value
else
{
if ((strSerial[4] == '=') && (len >= 6))
{
// Get new value
i = (charToHex(strSerial[5]) << 4) & 0xF0;
i |= charToHex(strSerial[6]) & 0x0F;
atQuery = ATQUERY_COMMAND;
}
// Hardware version
if (!strncmp(strSerial, AT_HVERSION, 4))
{
if (atQuery == ATQUERY_REQUEST)
Serial.println(HARDWARE_VERSION, HEX);
}
// Firmware version
else if (!strncmp(strSerial, AT_FVERSION, 4))
{
if (atQuery == ATQUERY_REQUEST)
Serial.println(FIRMWARE_VERSION, HEX);
}
// Frequency channel
else if (!strncmp(strSerial, AT_FREQCHANNEL, 4))
{
if (atQuery == ATQUERY_COMMAND)
{
///panstamp.radio.setChannel(i);
Serial.println("OK");
}
else {
///Serial.println(panstamp.radio.channel, HEX);
Serial.println("88");
}
}
// Synchronization word
else if (!strncmp(strSerial, AT_SYNCWORD, 4))
{
if (atQuery == ATQUERY_COMMAND)
{
if ((len-5) == 4)
{
arrV[0] = charToHex(strSerial[5]) << 4;
arrV[0] |= charToHex(strSerial[6]);
arrV[1] = charToHex(strSerial[7]) << 4;
arrV[1] |= charToHex(strSerial[8]);
///panstamp.radio.setSyncWord(arrV);
Serial.println("OK");
}
else
Serial.println("ERROR");
}
else
{
///Serial.println((unsigned int)panstamp.radio.syncWord[0] << 8 | panstamp.radio.syncWord[1], HEX);
Serial.println("08");
}
}
// Device address
else if (!strncmp(strSerial, AT_DEVADDRESS, 4))
{
if (atQuery == ATQUERY_COMMAND)
{
///panstamp.radio.setDevAddress(i);
Serial.println("OK");
}
else {
///Serial.println(panstamp.radio.devAddress, HEX);
Serial.println("18");
}
}
// Address check
else if (!strncmp(strSerial, AT_ADDRCHECK, 4))
{
if (atQuery == ATQUERY_COMMAND)
{
if (i == 0)
{
///panstamp.radio.disableAddressCheck();
Serial.println("OK");
}
else if (i == 1)
{
///panstamp.radio.enableAddressCheck();
Serial.println("OK");
}
else
Serial.println("ERROR");
}
else
Serial.println("ERROR");
}
else
Serial.println("ERROR");
}
}
}
/**
* setup
*
* Arduino setup function
*/
void setup()
{
pinMode(LEDPIN, OUTPUT);
digitalWrite(LEDPIN, HIGH);
// Reset serial buffer
memset(strSerial, 0, sizeof(strSerial));
Serial.begin(38400);
//Serial.begin(57600);
Serial.flush();
Serial.println("");
softSerial.begin(4800);
// Default mode is COMMAND
Serial.println("Modem ready!");
// Disable address check from the RF IC
///panstamp.radio.disableAddressCheck();
// Declare RF callback function
///panstamp.attachInterrupt(rfPacketReceived);
// Initialize Timer object
INIT_TIMER();
digitalWrite(LEDPIN, LOW);
}
/**
* loop
*
* Arduino main loop
*/
void loop()
{
if( softSerial.peek() != -1 ) {
ch = softSerial.read();
//Serial.print("Got byte: ");
//Serial.println(ch, HEX);
fakeRxPacket.length = 1;
// RSSI and LQI aren't applicable for our serial connection, so we'll use some placeholder values
fakeRxPacket.rssi = 0xBE;
fakeRxPacket.lqi = 0xEF;
fakeRxPacket.data[0] = ch;
rxPacket = &fakeRxPacket;
packetAvailable = true;
}
// Read wireless packet?
if (packetAvailable)
{
digitalWrite(LEDPIN, HIGH);
// Disable wireless reception
///panstamp.rxOff();
byte i;
packetAvailable = false;
if (serMode == SERMODE_DATA)
{
// Print the RSSI and LQI in hex
Serial.print("(");
if (rxPacket->rssi < 0x10)
Serial.print("0");
Serial.print(rxPacket->rssi, HEX);
if (rxPacket->lqi < 0x10)
Serial.print("0");
Serial.print(rxPacket->lqi, HEX);
Serial.print(")");
// Print the packet's payload bytes in hex
for(i=0 ; i<rxPacket->length ; i++)
{
if (rxPacket->data[i] < 0x10)
Serial.print(0, HEX); // Leading zero
Serial.print(rxPacket->data[i], HEX);
}
// Print a linefeed to ensure each packet is printed on its own line.
Serial.println("");
}
// Enable wireless reception
///panstamp.rxOn();
digitalWrite(LEDPIN, LOW);
}
// Read one byte of a serial command (which is an AT command (w/ terminating CR) if in COMMAND mode or hex-encoded data if in DATA mode)
if (Serial.available() > 0)
{
// Disable wireless reception
///panstamp.rxOff();
ch = Serial.read();
// Silently discard data when the buffer size is exceeded.
if (len >= SERIAL_BUF_LEN-1)
{
memset(strSerial, 0, sizeof(strSerial));
len = 0;
}
else if (ch == 0x0D)
{
STOP_TIMER();
strSerial[len] = 0;
handleSerialCmd(strSerial);
memset(strSerial, 0, sizeof(strSerial));
len = 0;
}
else
{
strSerial[len] = ch;
len++;
START_TIMER();
}
// Enable wireless reception
///panstamp.rxOn();
}
}
/**
* charToHex
*
* 'ch' Character to be converted to hexadecimal
*
* Returns:
* Hex value
*/
byte charToHex(byte ch)
{
byte val;
if (ch >= 'A' && ch <= 'F')
val = ch - 55;
else if (ch >= 'a' && ch <= 'f')
val = ch - 87;
else if (ch >= '0' && ch <= '9')
val = ch - 48;
else
val = 0x00;
return val;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment