Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Revision 0:575ebe257bd6, committed 2016-12-15
- Comitter:
- mikehogan62
- Date:
- Thu Dec 15 04:47:55 2016 +0000
- Commit message:
- Initial publication of code for Hamamatsu C12666MA sensor and Pure Engineering carrier module for ST Micro mbed modules. Licensed as Creative Commons, non-commercial, shara alike .. see https://creativecommons.org/licenses/by-nc-sa/2.0/ for details
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C12666.cpp Thu Dec 15 04:47:55 2016 +0000 @@ -0,0 +1,199 @@ +#include "mbed.h" +#include "C12666.h" + +// These values need to be accessed by the are real-time control values +// so they are not contained within the class +volatile int specClockSig; +volatile int specReady; +volatile SpecState specState; +volatile int specDataIndex; +volatile unsigned int specBufferFilling; +unsigned int lastspecBufferFilling; +int video_bias = DEFAULT_VIDEO_BIAS; // approximate bias of video signal +int video_max[] = {DEFAULT_VIDEO_AD_MAX_HI_GAIN, DEFAULT_VIDEO_AD_MAX_LO_GAIN}; // aproximate saturation value of video signal. First value is for high gain, second is for low gain + +uint16_t rawSpectralSamples[2][SPEC_CHANNELS]; +double darkScan[SPEC_CHANNELS]; +double avgScan[SPEC_CHANNELS]; + +extern DigitalOut specGain; +extern DigitalOut specST; +extern DigitalOut specClock; +extern AnalogIn specVideo; +extern DigitalOut whiteLED; +extern DigitalOut laserLED; +extern DigitalOut clippingLED; +extern Ticker clockTicker; + +extern int debugLevel; + + + +C12666_spectrometer::C12666_spectrometer() +{ + + pszSerialNum[0] = 0; + A_0 = 0.0; + B_1 = 0.0; + B_2 = 0.0; + B_3 = 0.0; + B_4 = 0.0; + B_5 = 0.0; + + specGain = 0; // default to high gain + specST = 1; // set ST signal high + specClock = 0; // arbitrarily start clock on low ... + whiteLED = 0; // set while LED off + laserLED = 0; // set laser LED off + + specClockSig = 0; + specReady = 0; + specState = spec_idle; + specDataIndex = 0; + specBufferFilling = 0; + lastspecBufferFilling = 0; + clippingLED = 0; + clockPulse_us = DEFAULT_CLOCK_PULSE_US; + // clear out the spectral data array and + // Start the timer that drives the routine + memset(rawSpectralSamples, 0, sizeof(rawSpectralSamples)); + clockTicker.attach_us(&specClockFn, clockPulse_us); +} + +// specClock is the event handler for the timer interrupt +// it handes the spectrometer clock and also manages the clock +void specClockFn() +{ + + // bail if we are not running + if (specState != spec_idle) { + + // toggle the spec clock bit ... + specClock = !specClock; + + // if spec clock is on a rising edge, update the state machine + if( specClock ) { + switch(specState) { + case spec_start : + specST = 0; + specDataIndex = 0; + specState = spec_sample_clock0; + break; + + case spec_sample_clock0 : + specST = 1; + specState = spec_sample_clock1; + break; + + case spec_sample_clock1 : + + // advance to the next state + specState = spec_sample_clock2; + + break; + + case spec_sample_clock2 : + // sample data into the appropriate buffer + rawSpectralSamples[specBufferFilling][specDataIndex] = specVideo.read_u16(); + clippingLED = rawSpectralSamples[specBufferFilling][specDataIndex] > video_max[specGain.read()]; // flash LED if we are clipping + + // advance the buffer + specDataIndex++; + + specState = spec_sample_clock3; + break; + + case spec_sample_clock3 : + + // if we have sampled all of the channels + // then go the spec_start state + if(specDataIndex == SPEC_CHANNELS) { + specState = spec_start; + + // if we have set the ready flag and we are not mid-print, + // then move to the other buffer + if(specReady) { + specBufferFilling = (specBufferFilling == 0) ? 1 : 0; + } + + // set specReady flag, + // since arriving here means that we have read out at least one full sample + // and that integration times will be consistent going forward + specReady = 1; + } else { + specState = spec_sample_clock0; + } + break; + } + } + } +} + +void C12666_spectrometer::Start(){ + specClockSig = 0; + specReady = 0; + specState = spec_start; + specDataIndex = 0; + specBufferFilling = 0; + lastspecBufferFilling = 0; + clippingLED = 0; + clockTicker.attach_us(&specClockFn, clockPulse_us); +} + +void C12666_spectrometer::Stop(){ + specClockSig = 0; + specReady = 0; + specState = spec_idle; + specDataIndex = 0; + specBufferFilling = 0; + lastspecBufferFilling = 0; + clippingLED = 0; + clockTicker.detach(); +} + +void C12666_spectrometer::setWhiteLED(int onOrOff){ + whiteLED = (onOrOff != 0); +} + +void C12666_spectrometer::setUVLED(int onOrOff){ + laserLED = (onOrOff != 0); +} + + +void C12666_spectrometer::setClockPeriod(long clockPeriod_usec){ + clockPulse_us = clockPeriod_usec; + clockTicker.detach(); + specClockSig = 0; + specReady = 0; + specState = (specState == spec_idle) ? spec_idle : spec_start; + specDataIndex = 0; + specBufferFilling = 0; + lastspecBufferFilling = 0; + clippingLED = 0; + clockTicker.attach_us(&specClockFn, clockPulse_us); +} + + +void C12666_spectrometer::getVolatileParams(int& specClockSig_, int& specReady_, SpecState& specState_, int& specDataIndex_, unsigned int& specBufferFilling_){ + specClockSig_ = specClockSig; + specReady_ = specReady; + specState_ = specState; + specDataIndex_ = specDataIndex; + specBufferFilling_ = specBufferFilling; +} + +// make a quick coppy of the last complete spectrum +// into the buffer passd in the rawSpectrumBuffer argument +void C12666_spectrometer::getRawSpectrum(uint16_t* rawSpectrumBuffer){ + + // determine if thelast full raw spectrum is in + // rawSpectralSamples[0] or rawSpectralSamples[1] + // if specBufferFilling = 0 then the last buffer = 1 + // elseif specBufferFilling = 0 then the last buffer = 0 + int lastBufferIndex = -specBufferFilling + 1; + + // copy the data to the buffer + memcpy(rawSpectrumBuffer, rawSpectralSamples[lastBufferIndex], sizeof(uint16_t) * SPEC_CHANNELS); +} + +C12666_spectrometer::~C12666_spectrometer(){} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C12666.h Thu Dec 15 04:47:55 2016 +0000 @@ -0,0 +1,93 @@ +#ifndef C1266_H +#define C1266_H +#include "mbed.h" + + +enum SpecState {spec_idle, spec_start, spec_sample_clock0, spec_sample_clock1, spec_sample_clock2, spec_sample_clock3}; + +// number of different spectral bins +#define SPEC_CHANNELS 256 + +// default clock pulse width +#define DEFAULT_CLOCK_PULSE_US 100 + +// shortest clock pulse width we will permit +#define MIN_CLOCK_PULSE_US 5 + +// default gain limits for hi gain and low gain +// If we exceed these levels, +#define DEFAULT_VIDEO_AD_MAX_HI_GAIN 40000 +#define DEFAULT_VIDEO_AD_MAX_LO_GAIN 32000 + +#define DEFAULT_VIDEO_BIAS 0 +#define SPEC_SERIAL_NUM_LEN 32 + + + +void specClockFn(); + +class C12666_spectrometer +{ + public: + C12666_spectrometer(); + ~C12666_spectrometer(); + + + // set the serial number and calibration coefficients for that serial number + void setCalibration(const char* psz_newSerialNum, double newA_0, double newB_1, double newB_2, double newB_3, double newB_4, double newB_5){ + strncpy(pszSerialNum, psz_newSerialNum, SPEC_SERIAL_NUM_LEN-1); + A_0 = newA_0; + B_1 = newB_1; + B_2 = newB_2; + B_3 = newB_3; + B_4 = newB_4; + B_5 = newB_5; + + } + + // retrieve all of the calibration coefficients + void getCalibration(char* psz_getSerialNum, double& getA_0, double& getB_1, double& getB_2, double& getB_3, double& getB_4, double& getB_5){ + strncpy(psz_getSerialNum, pszSerialNum, SPEC_SERIAL_NUM_LEN-1); + getA_0 = A_0; + getB_1 = B_1; + getB_2 = B_2; + getB_3 = B_3; + getB_4 = B_4; + getB_5 = B_5; + } + + void getVolatileParams(int& specClockSig_, int& specReady_, SpecState& specState_, int& specDataIndex_, unsigned int& specBufferFilling_); + void getRawSpectrum(uint16_t* rawSpectrumBuffer); + + void Start(); + void Stop(); + void setNumDarks(); + + void setWhiteLED(int onOrOff); + void setUVLED(int onOrOff); + void setClockPeriod(long clockPeriod_usec); + inline long getClockPeriod(){ return clockPulse_us; } + + + protected: + + + timestamp_t clockPulse_us; + + + + + + + char pszSerialNum[SPEC_SERIAL_NUM_LEN]; + double A_0; + double B_1; + double B_2; + double B_3; + double B_4; + double B_5; + + +}; + +#endif \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CommandDispatch.cpp Thu Dec 15 04:47:55 2016 +0000 @@ -0,0 +1,542 @@ +#include "mbed.h" +#include "CommandDispatch.h" +#include "C12666.h" + +extern C12666_spectrometer spec; +extern DigitalOut specGain; +extern DigitalOut specST; +extern DigitalOut specClock; +extern AnalogIn specVideo; +extern DigitalOut whiteLED; +extern DigitalOut laserLED; +extern DigitalOut clippingLED; // +extern Ticker clockTicker; + + +// external references to object that we need in this scope +// the "serial" object and the debugLevel are mainly used helpful printf statements +// that are used to debug etc. +extern Serial serial; +extern int debugLevel; + +/* +volatile int specClockSig; +volatile int specReady; +volatile SpecState specState; +volatile int specDataIndex; +volatile unsigned int specBufferFilling; +*/ +extern int lastspecBufferFillng; + +// Set default integration time to 100us +extern timestamp_t clockPulse_us; + +extern C12666_spectrometer *pSpec; + +extern int video_bias; +extern int video_max[]; + + +/* + + These commands are used to interact with an MBED enabled STMF303 Nucleo-32 board + + + BEGIN_DISPATCH_DECLS + + :DEBUG(int debugLevel) + # sets a debug level flag for turning on various levels of logging + + :COEFFICIENTS(string SerialNum, double A0, double B1, double B2, double B3, double B4, double B5) + # Sets the coefficients that map the pixels in the spec to individual wavenumbers + + :WAVELENGTHS() + # Tells the system to emit the 256 wavelength values given by the coefficients that are set in the coefficients routine + + :SAVE() + # SAVE saves coefficient information to the SD storage for the system + + :DARKS(int num_darks) + # Sets number of dark scans to take + + :SCANS(int num_scans) + # Sets number of (non-dark) scans to take + + :WHITE(int onOrOff) + # Tells system to turn on Whie LED as a light source + + :UVLASER(int onOrOff) + # Tells system to turn on UV LED Laser as light source + + :RAW_SCAN() + # Tells system to take a single, raw scan_is + + :FILE() + # Tells system to write results to SD storage. File names describe conditions for the scan etc. + + :SERIAL() + # Tells system to emit results over serial port + + :START() + # Start scanning +- + :STOP() + # Stop scanning + + :CLOCK_PERIOD(long clockPeriod_usec) + # set integration time in microseconds + + :GAIN(int high_or_low) + # set gain .. 0 sets low gain, non-0 sets high gain + + :VIDMAX(int lowGainMaxAD, int highGainMaxAD) + # sets max AD counts for low and high gain. + + :SHOW() + # returns a JSON string that tells the current configuration of the system + + + + END_DISPATCH_DECLS +*/ + + + + +// It is unwise to alter code in the following UNWISE TO ALTER block +// ********************************************************* +// ************** BEGIN UNWISE TO ALTER BLOCK ************** +// ********************************************************* + char *getNextField(char *pszString, char fieldDelimiter){ + pszString = strchr(pszString, fieldDelimiter); + if(pszString) pszString += 1; + return pszString; + } + + int get_longArg(char *&pszCommandArgs, long defaultVal){ + + long retval = defaultVal; // initialize the retval to a default ... + + // if the command string pointer is not null, + // then attempt to convert it to a long + // and advance pszCommand to the next ' ' char + // in the arguments string. + if(pszCommandArgs){ + // point to start of string + retval = atol(pszCommandArgs); + + // get pointer to next field + pszCommandArgs = getNextField(pszCommandArgs, ' '); + + } + return retval; + } + + U8 get_byteArg(char *&pszCommandArgs, U8 defaultVal){ + return ((U8) get_longArg(pszCommandArgs, (long) defaultVal)); + } + + int get_intArg(char *&pszCommandArgs, int defaultVal){ + return ((int) get_longArg(pszCommandArgs, (long) defaultVal)); + } + + float get_floatArg(char *&pszCommandArgs, float defaultVal){ + + float retval = defaultVal; // initialize the retval to a default ... + + // if the command string pointer is not null, + // then attempt to convert it to a float + // and advance pszCommand to the next ' ' char + // in the arguments string. + if(pszCommandArgs){ + retval = atof(pszCommandArgs); + pszCommandArgs = getNextField(pszCommandArgs, ' '); + } + return retval; + } + + double get_doubleArg(char *&pszCommandArgs, double defaultVal){ + + double retval = defaultVal; // initialize the retval to a default ... + + // if the command string pointer is not null, + // then attempt to convert it to a float + // and advance pszCommand to the next ' ' char + // in the arguments string. + if(pszCommandArgs){ + retval = atof(pszCommandArgs); + pszCommandArgs = getNextField(pszCommandArgs, ' '); + } + return retval; + } + + char* get_stringArg(char *&pszCommandArgs){ + + char *retval = 0; + + // if the command string pointer is not null, + // then attempt to convert it to a float + // and advance pszCommand to the next ' ' char + // in the arguments string. + if(pszCommandArgs){ + + // get pointer to current field in command string + retval = pszCommandArgs; + + // get pointer ot next field (if any) + pszCommandArgs = getNextField(pszCommandArgs, ' '); + + // insert a null character after the current field + // to form a null terminanated string + if (pszCommandArgs) *(pszCommandArgs-1) = 0; + } + return retval; + } + +// ********************************************************* +// ************** END UNWISE TO ALTER BLOCK ************** +// ********************************************************* + +/* + sets a debug level flag for turning on various levels of logging +*/ +void handle_DEBUG(char *&pszCommandArgs){ + debugLevel = get_intArg(pszCommandArgs); +} + +/* + Sets the coefficients that map the pixels in the spec to individual wavenumbers +*/ +void handle_COEFFICIENTS(char *&pszCommandArgs){ + char *psz_newSerialNum = get_stringArg(pszCommandArgs); + double A_0 = get_doubleArg(pszCommandArgs); + double B_1 = get_doubleArg(pszCommandArgs); + double B_2 = get_doubleArg(pszCommandArgs); + double B_3 = get_doubleArg(pszCommandArgs); + double B_4 = get_doubleArg(pszCommandArgs); + double B_5 = get_doubleArg(pszCommandArgs); + + if (debugLevel) serial.printf("handle_COEFFICIENTS(A0:=%f, B1:=%f, B2:=%f, B3:=%f, B4:=%f, B5:=%f)\n",A_0, B_1, B_2, B_3, B_4, B_5); + + spec.setCalibration(psz_newSerialNum, A_0, B_1, B_2, B_3, B_4, B_5); +} + +/* + Tells the system to emit the 256 wavelength values given by the coefficients that are set in the coefficients routine +*/ +void handle_WAVELENGTHS(char *&pszCommandArgs){ + + + if (debugLevel) serial.printf("handle_WAVELENGTHS()\n"); + + char pszSerialNum[SPEC_SERIAL_NUM_LEN]; + double A_0; + double B_1; + double B_2; + double B_3; + double B_4; + double B_5; + + + spec.getCalibration(pszSerialNum, A_0, B_1, B_2, B_3, B_4, B_5); + + // print the start of a json statement that describes the WAVELENGTHS structure + // include the 5th order polynomial coefficients + serial.printf("WAVELENGTHS={A0:%f,B1:%f,B2:%f,B3:%f,B4:%f,B5:%f",A_0, B_1, B_2, B_3, B_4, B_5); + + // print the wavelengths themselves as an array + serial.printf(",NM:["); + double i; + double coeff; + for(i = 0; i < SPEC_CHANNELS; i ++){ + coeff = A_0 + B_1 * i + B_2 * pow(i,2) + B_3 * pow(i,3) + B_4 * pow(i,4) + B_5 * pow(i,5); + if (i) serial.printf(","); + serial.printf("%f", coeff); + } + // end the wavelength array + // end the json statement + // and do a newline + serial.printf("]}\n"); +} + +/* + SAVE saves coefficient information to the SD storage for the system +*/ +void handle_SAVE(char *&pszCommandArgs){ + + if (debugLevel) serial.printf("handle_SAVE()\n"); + // TODO: Fill in code for SAVE +} + +/* + Sets number of dark scans to take +*/ +void handle_DARKS(char *&pszCommandArgs){ + int num_darks = get_intArg(pszCommandArgs); + if (debugLevel) serial.printf("handle_SCANS(num_darks:=%d)\n", num_darks); + // TODO: Fill in code for DARKS +} + +/* + Sets number of (non-dark) scans to take +*/ +void handle_SCANS(char *&pszCommandArgs){ + int num_scans = get_intArg(pszCommandArgs); + if (debugLevel) serial.printf("handle_SCANS(num_scans:=%d)\n", num_scans); + + // TODO: Fill in code for SCANS +} + +/* + Tells system to turn on Whie LED as a light source +*/ +void handle_WHITE(char *&pszCommandArgs){ + + int onOrOff = get_intArg(pszCommandArgs); + if (debugLevel) serial.printf("handle_WHITE(onOrOff:=%d)\n", onOrOff); + spec.setWhiteLED(onOrOff); + +} + +/* + Tells system to turn on UV LED Laser as light source +*/ +void handle_UVLASER(char *&pszCommandArgs){ + int onOrOff = get_intArg(pszCommandArgs); + if (debugLevel) serial.printf("handle_UVLASER(onOrOff:=%d)\n", onOrOff); + spec.setUVLED(onOrOff); +} + +/* + Tells system to take a single, raw scan_is +*/ +void handle_RAW_SCAN(char *&pszCommandArgs){ + + if (debugLevel) serial.printf("handle_RAW_SCAN()\n"); + + // TO_DO --- make this a little more clever so that the number of spec channels is not hard-wired + uint16_t rawSpectrumBuffer[SPEC_CHANNELS]; + + spec.getRawSpectrum(rawSpectrumBuffer); + + serial.printf("RAW_SCAN=["); + for (int i = 0; i < SPEC_CHANNELS; i++){ + if(i) serial.printf(","); + serial.printf("%d",rawSpectrumBuffer[i]); + } + serial.printf("]\n"); +} + +/* + Tells system to write results to SD storage. File names describe conditions for the scan etc. +*/ +void handle_FILE(char *&pszCommandArgs){ + + // TODO: Fill in code for FILE + if (debugLevel) serial.printf("handle_FILE()\n"); +} + +/* + Tells system to emit results over serial port +*/ +void handle_SERIAL(char *&pszCommandArgs){ + + // TODO: Fill in code for SERIAL + if (debugLevel) serial.printf("handle_FILE()\n"); +} + +/* + Start scanning +*/ +void handle_START(char *&pszCommandArgs){ + if (debugLevel) serial.printf("handle_START()\n"); + spec.Start(); +} + +/* + Stop scanning +*/ +void handle_STOP(char *&pszCommandArgs){ + if (debugLevel) serial.printf("handle_STOP()\n"); + spec.Stop(); +} + + +/* + set integration time in microseconds +*/ +void handle_CLOCK_PERIOD(char *&pszCommandArgs){ + long clockPeriod_usec = get_longArg(pszCommandArgs); + if (debugLevel) serial.printf("handle_CLOCK_PERIOD(clockPeriod_usec=%d)\n", clockPeriod_usec); + spec.setClockPeriod(clockPeriod_usec); +} + + +/* + set gain .. 0 sets low gain, non-0 sets high gain +*/ +void handle_GAIN(char *&pszCommandArgs){ + int high_or_low = get_intArg(pszCommandArgs); + if (debugLevel) serial.printf("handle_GAIN(high_or_low:=%d)\n", high_or_low); + specGain = (high_or_low == 0); // if argument is 0 (low gain), then assert the specFain pin (pin high means low gain) +} + +/* + sets max AD counts for low and high gain. +*/ +void handle_VIDMAX(char *&pszCommandArgs){ + int lowGainMaxAD = get_intArg(pszCommandArgs); + int highGainMaxAD = get_intArg(pszCommandArgs); + + // Put high gain in 0th element of video_max + // and low gain in 1th element of video_max + // that way, we can use the state of the gain pin to index the appropriate A/D limit + video_max[1] = lowGainMaxAD; + video_max[0] = highGainMaxAD; +} + +/* + returns a JSON string that tells the current configuration of the system +*/ +void handle_SHOW(char *&pszCommandArgs){ + // make special call to stash copy of volatile values + int specClockSig; + int specReady; + SpecState specState; + int specDataIndex; + unsigned int specBufferFilling; + + spec.getVolatileParams(specClockSig, specReady, specState, specDataIndex, specBufferFilling); + + + // grab various digital signals and indexed values (like video_max) + // before emitting them as json + int specGain_ = specGain.read(); + int video_max_ = video_max[specGain_]; + int whiteLED_ = whiteLED.read(); + int laserLED_ = laserLED.read(); + + // get values related to integration time + long clockPeriod = spec.getClockPeriod(); + long integrationTime = 8*256*clockPeriod; + + // get local copy of calibration data + char pszSerialNum[SPEC_SERIAL_NUM_LEN]; + double A_0; + double B_1; + double B_2; + double B_3; + double B_4; + double B_5; + spec.getCalibration(pszSerialNum, A_0, B_1, B_2, B_3, B_4, B_5); + + serial.printf("SHOW={specState:%d, specReady:%d, specDataIndex:%d, specBufferFilling:%d, video_bias:%d, video_max:%d, ",specState, specReady, specDataIndex, specBufferFilling, video_bias, video_max_); + serial.printf("specGain:%d, whiteLED:%d, laserLED:%d, ",specGain_, whiteLED_, laserLED_); + serial.printf("clockPeriod:%d, integrationTime:%d, ",clockPeriod, integrationTime); + serial.printf("serialNum:\"%s\", A0:%f, B1:%f, B2:%f, B3:%f, B4:%f, B5:%f",pszSerialNum, A_0, B_1, B_2, B_3, B_4, B_5); + serial.printf("}\n"); +} + +namespace CommandDispatch { + #define COEFFICIENTS_COMMAND 0 + #define WAVELENGTHS_COMMAND 1 + #define SAVE_COMMAND 2 + #define DARKS_COMMAND 3 + #define SCANS_COMMAND 4 + #define WHITE_COMMAND 5 + #define UVLASER_COMMAND 6 + #define RAW_SCAN_COMMAND 7 + #define FILE_COMMAND 8 + #define SERIAL_COMMAND 9 + #define START_COMMAND 10 + #define STOP_COMMAND 11 + #define CLOCK_PERIOD_COMMAND 12 + #define GAIN_COMMAND 13 + #define VIDMAX_COMMAND 14 + #define SHOW_COMMAND 15 + + + union command_data_lookup_t { + char string[4 * NUM_COMMANDS]; + unsigned long command_val[NUM_COMMANDS]; + }; + + command_data_lookup_t commands = { + 'D','E','B','U', + 'C','O','E','F', + 'W','A','V','E', + 'S','A','V','E', + 'D','A','R','K', + 'S','C','A','N', + 'W','H','I','T', + 'U','V','L','A', + 'R','A','W','_', + 'F','I','L','E', + 'S','E','R','I', + 'S','T','A','R', + 'S','T','O','P', + 'C','L','O','C', + 'G','A','I','N', + 'V','I','D','M', + 'S','H','O','W'}; + + + handler_fn_t command_handlers[NUM_COMMANDS] = { + handle_DEBUG, + handle_COEFFICIENTS, + handle_WAVELENGTHS, + handle_SAVE, + handle_DARKS, + handle_SCANS, + handle_WHITE, + handle_UVLASER, + handle_RAW_SCAN, + handle_FILE, + handle_SERIAL, + handle_START, + handle_STOP, + handle_CLOCK_PERIOD, + handle_GAIN, + handle_VIDMAX, + handle_SHOW}; +}; + + +int dispatchCommand(command_data_t& command_data){ + int i; + for (i = 0; i < NUM_COMMANDS; i++){ + + // Attempt to match the first four characters of command_data.command_val + // against a value in the commands table. + // This logic effectively encodes the four-character field as a long, + // which makes the matching a little easier. + if (CommandDispatch::commands.command_val[i] == command_data.command_val){ + + // Echo what we are processing. + if (debugLevel){ + serial.printf("Processing: %s\n",command_data.string); + } + + // We have a match, so use the value of "i" + // to retrieve the pointer for the command handler + handler_fn_t command_handler = CommandDispatch::command_handlers[i]; + + // get pointer to tail of string (beyond command portion) + // and pass to the handler function + char *pszCommandArgs = getNextField(command_data.string, ' '); + if (debugLevel){ + serial.printf("%s\n",pszCommandArgs); + } + + command_handler(pszCommandArgs); + + // exit the loop + break; + } + } + + // if we iterate over all commands without a metch + // thrn return a 0 to indicate failure + // otherwise return a 1 + return( i != NUM_COMMANDS); +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CommandDispatch.h Thu Dec 15 04:47:55 2016 +0000 @@ -0,0 +1,195 @@ +#ifndef COMMAND_DISPATCH_H +#define COMMAND_DISPATCH_H + + +/* + + These commands are used to interact with an MBED enabled STMF303 Nucleo-32 board + + + BEGIN_DISPATCH_DECLS + + :DEBUG(int debugLevel) + # sets a debug level flag for turning on various levels of logging + + + :COEFFICIENTS(string SerialNum, double A0, double B1, double B2, double B3, double B4, double B5) + # Sets the coefficients that map the pixels in the spec to individual wavenumbers + + :WAVELENGTHS() + # Tells the system to emit the 256 wavelength values given by the coefficients that are set in the coefficients routine + + :SAVE() + # SAVE saves coefficient information to the SD storage for the system + + :DARKS(int num_darks) + # Sets number of dark scans to take + + :SCANS(int num_scans) + # Sets number of (non-dark) scans to take + + :WHITE(int onOrOff) + # Tells system to turn on Whie LED as a light source + + :UVLASER(int onOrOff) + # Tells system to turn on UV LED Laser as light source + + :RAW_SCAN() + # Tells system to take a single, raw scan_is + + :FILE() + # Tells system to write results to SD storage. File names describe conditions for the scan etc. + + :SERIAL() + # Tells system to emit results over serial port + + :START() + # Start scanning + + :STOP() + # Stop scanning + + :CLOCK_PERIOD(long clockPeriod_usec) + # set integration time in microseconds + + :GAIN(int high_or_low) + # set gain .. 0 sets low gain, non-0 sets high gain + + :VIDMAX(int lowGainMaxAD, int highGainMaxAD) + # sets max AD counts for low and high gain. + + :SHOW() + # returns a JSON string that tells the current configuration of the system + + + + END_DISPATCH_DECLS +*/ + + #define NUM_COMMANDS 17 + + +// It is unwise to alter code in the following UNWISE TO ALTER block +// ********************************************************* +// ************** BEGIN UNWISE TO ALTER BLOCK ************** +// ********************************************************* + typedef void (*handler_fn_t)(char *&pszCommandArgs); + + char *getNextField(char *pszString, char fieldDelimiter); + + int get_longArg(char *&pszCommandArgs, long defaultVal=0); + +#ifndef U8 + #define U8 unsigned char +#endif + U8 get_byteArg(char *&pszCommandArgs, U8 defaultVal=0); + + int get_intArg(char *&pszCommandArgs, int defaultVal=0); + + float get_floatArg(char *&pszCommandArgs, float defaultVal=0.0); + + double get_doubleArg(char *&pszCommandArgs, double defaultVal=0.0); + + char* get_stringArg(char *&pszCommandArgs); + +// Create a buffer of 127 characters to hold command strings (plus one extra byte for null termination) +// Also wrap the command data in a union, so that we can easily convert the first four characters of the +// command into a long in order to speed up the logic that maps commands to function handlers. +#define COMMAND_STRING_LENGTH 127 +union command_data_t { + char string[COMMAND_STRING_LENGTH]; + unsigned long command_val; +}; + + int dispatchCommand(command_data_t& command_data); + +// ********************************************************* +// ************** END UNWISE TO ALTER BLOCK ************** +// ********************************************************* + +/* + sets a debug level flag for turning on various levels of logging +*/ +void handle_DEBUG(char *&pszCommandArgs); + +/* + Sets the coefficients that map the pixels in the spec to individual wavenumbers +*/ +void handle_COEFFICIENTS(char *&pszCommandArgs); + +/* + Tells the system to emit the 256 wavelength values given by the coefficients that are set in the coefficients routine +*/ +void handle_WAVELENGTHS(char *&pszCommandArgs); + +/* + SAVE saves coefficient information to the SD storage for the system +*/ +void handle_SAVE(char *&pszCommandArgs); + +/* + Sets number of dark scans to take +*/ +void handle_DARKS(char *&pszCommandArgs); + +/* + Sets number of (non-dark) scans to take +*/ +void handle_SCANS(char *&pszCommandArgs); + +/* + Tells system to turn on Whie LED as a light source +*/ +void handle_WHITE(char *&pszCommandArgs); + +/* + Tells system to turn on UV LED Laser as light source +*/ +void handle_UVLASER(char *&pszCommandArgs); + +/* + Tells system to take a single, raw scan +*/ +void handle_RAW_SCAN(char *&pszCommandArgs); + +/* + Tells system to write results to SD storage. File names describe conditions for the scan etc. +*/ +void handle_FILE(char *&pszCommandArgs); + +/* + Tells system to emit results over serial port +*/ +void handle_SERIAL(char *&pszCommandArgs); + +/* + Start scanning +*/ +void handle_START(char *&pszCommandArgs); + +/* + Stop scanning +*/ +void handle_STOP(char *&pszCommandArgs); + +/* + set integration time in microseconds +*/ +void handle_CLOCK_PERIOD(char *&pszCommandArgs); + +/* + set gain .. 0 sets low gain, non-0 sets high gain +*/ +void handle_GAIN(char *&pszCommandArgs); + +/* + sets max AD counts for low and high gain. +*/ +void handle_VIDMAX(char *&pszCommandArgs); + +/* + returns a JSON string that tells the current configuration of the system +*/ +void handle_SHOW(char *&pszCommandArgs); + +#endif // #ifndef COMMAND_DISPATCH_H \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CommandProcessor.cpp Thu Dec 15 04:47:55 2016 +0000 @@ -0,0 +1,88 @@ +#include "mbed.h" +#include "CommandProcessor.h" +#include "CommandDispatch.h" + +// external references to object that we need in this scope +// the "serial" object and the debugLevel are mainly used helpful printf statements +// that are used to debug etc. +extern Serial serial; +extern int debugLevel; + +command_data_t command_data; + +// Define an index variable that is used to load serial characters into the command_data buffer +int command_str_index; + +// define states for the command processor as an enum +Command_State_t command_state; + +// initializeCommandProcessor just initializes +// variabls for the command processing so that it will work properly after a re-start +void initializeCommandProcessor(){ + // initialize the command string, + // the command string index, + // and the command_state + command_data.string[0]=(char)0; + command_str_index = 0; + command_state = WAITING; +} + +// processSerialCommands is used to read the serial port +// and convert the character stream to plausible commands +// It essentially looks for an input string that starts with a +// ":" character and that ends with a ";" character. +// Then it grabs the first field from that string and matches +// the first four characters against candidates strings in the +// commands array. This is done by casting the entries to long values +// so that the match cane +void processSerialCommands(){ + // If there are serial characters present, + // then process them + while (serial.readable()){ + + // Retrieve the next character + char inChar = serial.getc();; + + // If we see the start of a command string, + // Then + // Set the command processing state machine to READING, + // and reset the character buffer for the command string + if (inChar == COMMAND_START){ + command_str_index = 0; + command_state = READING; + command_data.string[0]=(char)0; + } + // If we are reading a command and see the COMMAND_END character + // Then + // switch to the PROCESSING command state, + // and break out of the loop + else if ((command_state == READING) && (inChar == COMMAND_END)){ + command_state = PROCESSING; + break; + } + // If there is any other character, + // and we still have room in the buffer, + // Then + // add the character to the buffer and advance the command string index + // also, fill in the character following the last char with a null termination. + else if ((command_state == READING) && (command_str_index < COMMAND_STRING_LENGTH)){ + command_data.string[command_str_index] = inChar; + command_str_index++; + command_data.string[command_str_index]=(char)0; + } + } + + // If we are in the PROCESSING state, + // then attempt to dispatch to the appropriate function + if (command_state == PROCESSING){ + + // if we were unable to dispatch to a command, then complain + if ( (dispatchCommand(command_data) == 0) && debugLevel) { + serial.printf("No Match for: %s\n", command_data.string); + } + + // reset the command state to WAITING and exit the for loop + command_state = WAITING; + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CommandProcessor.h Thu Dec 15 04:47:55 2016 +0000 @@ -0,0 +1,33 @@ +#ifndef COMMAND_PROCESSOR_H +#define COMMAND_PROCESSOR_H + + + + +// declare variables used for managing command processing states +// and reading command strings. +// Define command start character (':') and command end character ";" +#define COMMAND_START ':' +#define COMMAND_END ';' + + + +// define states for the command processor as an enum +enum Command_State_t {WAITING, READING, PROCESSING}; + +// initializeCommandProcessor just initializes +// variabls for the command processing so that it will work properly after a re-start +void initializeCommandProcessor(); + +// processSerialCommands is used to read the serial port +// and convert the character stream to plausible commands +// It essentially looks for an input string that starts with a +// ":" character and that ends with a ";" character. +// Then it grabs the first field from that string and matches +// the first four characters against candidates strings in the +// commands array. This is done by casting the entries to long values +// so that the match cane +void processSerialCommands(); + + +#endif // #ifndef COMMAND_PROCESSOR_H++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/TO_DO.txt Thu Dec 15 04:47:55 2016 +0000 @@ -0,0 +1,3 @@ +Ensure START/STOP work as intended +Get NUM_DARK etc working +Integrate with R
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Thu Dec 15 04:47:55 2016 +0000 @@ -0,0 +1,55 @@ +#include "mbed.h" +#include "CommandProcessor.h" +#include "C12666.h" + + +Serial serial(USBTX, USBRX); +DigitalOut specGain(A0); +DigitalOut specST(A1); +DigitalOut specClock(A2); +AnalogIn specVideo(A3); +DigitalOut whiteLED(A4); +DigitalOut laserLED(A5); +DigitalOut clippingLED(LED3); +Ticker clockTicker; +C12666_spectrometer spec; + +// use debug level to turn on/off various printf logging stuff +int debugLevel = 0; + +// Use setup and loop style logic a la Arduino +// so that we can use the dispatch code from the various other modules in the suste +void setup(); +void loop(); + +int main() +{ + setup(); + while(1) { + loop(); + } +} + +void setup(void) +{ + // Set up the serial port ... + serial.baud(115200); + serial.printf("Biorealize: PureEngineering C12666MA UV-VIS Spectrometer 03MAY2016\n"); + + + + + // initialize the variables that handle command dispatching + initializeCommandProcessor(); + + + +} + +void loop(void) +{ + processSerialCommands(); +} + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed.bld Thu Dec 15 04:47:55 2016 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/mbed_official/code/mbed/builds/c0f6e94411f5 \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/text_snippets.txt Thu Dec 15 04:47:55 2016 +0000 @@ -0,0 +1,3 @@ +:COEFFICIENTS 16a00055 318.7948193 2.386405907 -0.0007973593848 -0.000005712853029 0.000000003262240438 1.24E-11; +:COEFFICIENTS 16a00055 318.7948193 2.386405907 -0.0007973593848 -0.000005712853029 0.000000003262240438 1.24E-11; +:COEFFICIENTS 16a00049 312.7990824 2.38900469 -0.0007590948411 -0.000006698919144 0.00000001087748788 0.00000001087748788;