Skip to content

Commit

Permalink
Merge pull request #76 from CrashOverride85/dev
Browse files Browse the repository at this point in the history
From dev branch for v1.7
  • Loading branch information
CrashOverride85 authored Oct 21, 2023
2 parents 92cd67b + 314f014 commit bc8a0e9
Show file tree
Hide file tree
Showing 71 changed files with 2,007 additions and 408 deletions.
6 changes: 2 additions & 4 deletions docs/Build.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@


# Hardware build notes

*Disclaimer*: These instructions are complete to the best of my knowledge, however I've only built the one so far, and that was over quite some time, so there may be omissions.

There is now an optional audio [input board](./AudioInput-Build.md) too.

## Prerequisites
Expand Down Expand Up @@ -200,6 +196,8 @@ Before showing the confirmation screen, the box will have confirmed the EEPROM I
EEPROM is used to store all settings that can be changed via the menus, but it does not store any uploaded Lua scripts - these are stored in flash, and can be wiped by reuploading the firmware.

## ZC624 output module
If the ZC624 passes its self test, the OK LED should light (which should be ~1-2 seconds after power on). If it fails, this light should flash.

There is also debugging output from the ZC624 board on the serial header. Note that is at 3v3 level, and RS232 levels would damage it.

Connect a 3.3v TTL serial to USB adapter to the pins labelled Tx and GND on the header, connect at 115200 baud, and power it on.
Expand Down
6 changes: 5 additions & 1 deletion docs/LuaNotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,10 @@ Part: can be either "`A`" or "`B`". With a stereo 3.5mm TRS cable inserted, shor
Called once when the pattern is started, before `Loop()`. It can be used to do any initial setup, including setting power level. If this function does _not_ exist, the power level is defaulted to 1000, otherwise it is set to 0 and can be set to something more appropriate here.

### Loop(time_ms)
Called periodically for as long as the pattern is running. `time_ms` is how long in milliseconds the box has been powered on.
Called periodically for as long as the pattern is running. `time_ms` is how long in milliseconds the box has been powered on, and since v1.7 is a floating point number with microsecond precision.

`Loop()` is called as often as possible by default, but how often will depend enormously on how much work is done in the Loop() function - expect an empty Loop() to be called around every 50us, and a complex loop() to be called closer to every 4000us (or more).

If a specific frequency is required (rather than just as often as possible), a `loop_freq_hz = n` option can be added to the `Config = {}` block, where `n` is between 1 and 400. Be aware that for particularly complex scripts, higher values are unlikely to work well - and note that this setting will only reduce how often Loop() is called when compared to the default.

[acc port]: images/lua_acc_port.png "Accessory port"
9 changes: 8 additions & 1 deletion docs/Operation.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,14 @@ Configuration options so far:

* Collar config - Allows 4x shock collars to be configured. Each shock collar needs to be paired to the box, and the bottom option allows for testing the collar with the current settings. The "Chan." option corresponds to the CH button on the original remote, and it probably makes sense to be left as 1 for all collars, as each will have a unique ID anyway. Note that the mode (shock / vibrate / beep) needs to be set here, and won't change outside of this config screen (for the time being, at least)

* LED brightness - Pretty much what it says. If you value your eyesight, single digit values are good.
* Display options - sub-menu, with:
- LED brightness - Pretty much what it says. If you value your eyesight, single digit values are good.

- Power level display - where to show the numeric power level as percentage. Options are:
- Off (default) - Only show power level as a bar graph
- Disappearing - Show in large text on screen for a few moments on change
- In bar graph - Show value, rotated 90 degrees, inside the corresponding bar graph
- Both - Enable Disappearing text display and in bar graph

* Ramp up time - When starting a pattern, how long it takes to ramp up to the power level set for the channel on the front panel

Expand Down
50 changes: 28 additions & 22 deletions docs/Patterns.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,35 @@
# Patterns

**Note**: Any pattern in the list/menu prefixed with `(!)` should not be used above the waist, as channel isolation - where only one channel pulese at the exact same moment - is disabled.
**Note**: Any pattern in the list/menu prefixed with `(!)` should not be used above the waist, as channel isolation - where only one channel pulses at the exact same moment - is disabled.

## Waves
Gradually increasing and decreasing intensity on all channels at different rates, with channel 1 being the slowest and 4 being the fastest. Varying intensity is achieved by altering the gap between pulses rather than changing the power level.
Gradually increasing and decreasing intensity on all channels at different rates. Varying intensity is achieved by altering the gap between pulses rather than changing the power level.
Can NOT be used with shock collars.

### Menu options
* Speed - controls how fast the intensity increases/decreases. 10-1000, higher is faster.
* Frequency - controls how fast the intensity increases/decreases. 1-64, lower is faster.

### Extra hardware required?:
No.

------------------------------------------------------------

## Orgasm
Pleasurable increasing and decreasing sensation, achieved by varying both pulse width and frequency. A full cycles takes a few minutes.

### Menu options
* Frequency - controls how fast the intensity increases/decreases. 15-255, lower is faster.

### Extra hardware required?:
No.

------------------------------------------------------------

## Climb
Gradually increasing intensity.

### Menu options
* Frequency - controls how fast the intensity increases/decreases. 15-255, lower is faster.

### Extra hardware required?:
No.
Expand Down Expand Up @@ -58,22 +80,6 @@ No.

------------------------------------------------------------

## Climb
Outputs single (150us) pules, with the gap between each pulse continuously decreasing until the output is near constant.
Can NOT be used with shock collars. Note that this achieves increasing intensity by varying the gap between pulses rather than altering the power level.

### Menu options
* Duration - Approximately how long, in seconds, it takes to reach full power
* Reset after climb
- Yes - once full power is reached, start over
- No - once full power is reached, stay there
* Soft button "Reset" - Reset to beginning of pattern

### Extra hardware required?:
No.

------------------------------------------------------------

## Triggered Climb
Power increases on channels 1+2 until a remote button is pressed, then a pulse is delivered on channels 3+4, and 1+2 is reset. Every time the button is pressed the shock delivered on channels 3+4 is increased.
Suggest bipolar nipple clamps for channels 3+4, 1-2 anywhere else, then handing the button to the person connected.
Expand Down Expand Up @@ -111,7 +117,7 @@ Only available if audio enabled.
If the volume goes above the level set, triggers the output on all 4 channels. Intended to be used with a microphone.

### Menu options
* Audio trigger - Shows mini spectrum analyser, with a horizontal red line that can be moved up/down using the adjust dial. Whenever the volume is loud enough to cross the line, the output is triggered.
* Audio trigger - Shows mini spectrum analyzer, with a horizontal red line that can be moved up/down using the adjust dial. Whenever the volume is loud enough to cross the line, the output is triggered.

![Audio Threshold]

Expand Down Expand Up @@ -201,7 +207,7 @@ No.
## Predicament
Designed for predicament bondage scenarios. I'm finding this difficult to explain, so hopefully some examples will help.
Requires at least one input, ideally two, connected to the Trigger1 & Trigger2 ports. Based on the settings, delivers a shock
based on which inputs have been trigged.
based on which inputs have been triggered.
For example, if two foot pedals are used, it can deliver a shock unless both are kept pressed (this is the default settings).

### Menu options
Expand All @@ -217,7 +223,7 @@ For example, if two foot pedals are used, it can deliver a shock unless both are
| No | No | And | Yes | Default. Shock if either button not pressed
| No | No | And | No | Shock if both both buttons pressed
| No | No | Or | No | Shock if either button pressed
| Yes | Yes | Or | No | Shock if either button not pressed (different way achieving the default behaviour)
| Yes | Yes | Or | No | Shock if either button not pressed (different way achieving the default behavior)
| No | No | Or | Yes | If only one button connected to trigger1, shock when it is not pressed


Expand Down
7 changes: 5 additions & 2 deletions remote_access/lib/ZcMessages.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,11 @@ def GetResponse(self, expectedMsgId, expectedType):

if "Result" not in result or result["Result"] != "OK":
if "Error" in result:
print("Got error message: ")
print(" " + result["Error"])
if result["Error"] == None:
print("Unknown error.")
else:
print("Got error message: ")
print(" " + result["Error"])
else:
print("Result not OK")

Expand Down
4 changes: 2 additions & 2 deletions remote_access/lua_manage.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
connection_group.add_argument('--serial', action='store', help='Serial port to use')

parser.add_argument('--list', action='store_true', help='List scripts stored on ZC95')
parser.add_argument('--delete', action='store', type=int, choices=range(1, 6), help='Delete script at slot on ZC95')
parser.add_argument('--delete', action='store', type=int, choices=range(0, 5), help='Delete script at slot on ZC95')

args = parser.parse_args()

Expand All @@ -39,7 +39,7 @@
for script in scripts:
print(str(script["Index"]) + " - " + script["Name"])

elif args.delete:
elif args.delete != None:
if zc_messages.SendDeleteLuaScript(args.delete) == None:
print("Failed!")
else:
Expand Down
2 changes: 1 addition & 1 deletion remote_access/lua_upload.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def OutputErrorMessagesReceived(rcv_queue):
connection_group.add_argument('--ip', action='store', help='IP address of ZC95')
connection_group.add_argument('--serial', action='store', help='Serial port to use')

parser.add_argument('--index', action='store', required=True, type=int, choices=range(1, 6), help='Slot/index on ZC95 to upload script to')
parser.add_argument('--index', action='store', required=True, type=int, choices=range(0, 5), help='Slot/index on ZC95 to upload script to')
parser.add_argument('--script', action='store', required=True, help='Lua script to upload')
args = parser.parse_args()

Expand Down
2 changes: 2 additions & 0 deletions source/zc624/CI2cSlave.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ class CI2cSlave
VerStrStart = 0x20,
VerStrEnd = 0x34, // 20 character string

TestVal = 0x40,

// Read/write
// starting at 0x80
ChannelIsolation = 0x80 // Default = true. If false, multiple channels can pulse at the same time (triphase effects)
Expand Down
2 changes: 1 addition & 1 deletion source/zc624/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ add_executable(OutputZc
CI2cSlave.cpp
)

pico_generate_pio_header(OutputZc ${CMAKE_CURRENT_LIST_DIR}/pulse_gen.pio OUTPUT_DIR ${CMAKE_CURRENT_LIST_DIR}/generated)
pico_generate_pio_header(OutputZc ${CMAKE_CURRENT_LIST_DIR}/pulse_gen.pio OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/generated)


pico_set_program_name(OutputZc "OutputZc")
Expand Down
24 changes: 16 additions & 8 deletions source/zc624/CMessageProcess.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
#include "hardware/spi.h"
#include <stdio.h>

CMessageProcess::CMessageProcess(COutput *output)
CMessageProcess::CMessageProcess(COutput *output, CI2cSlave *i2c_slave)
{
printf("CMessageProcess()\n");
_output = output;
_i2c_slave = i2c_slave;
}

CMessageProcess::~CMessageProcess()
Expand All @@ -18,10 +19,8 @@ void CMessageProcess::init()
{
// SPI initialisation
spi_init(SPI_PORT, SPI_BAUD_RATE);


// spi_set_format(SPI_PORT, 8, SPI_CPOL_1, SPI_CPHA_1,SPI_MSB_FIRST);
spi_set_format(spi0, 8, SPI_CPOL_1, SPI_CPHA_1, SPI_MSB_FIRST);
spi_set_format(spi0, 8, SPI_CPOL_1, SPI_CPHA_1, SPI_MSB_FIRST);

spi_set_slave(SPI_PORT, true);

Expand All @@ -35,8 +34,8 @@ void CMessageProcess::loop()
{
message msg;

spi_read_blocking(SPI_PORT, (uint8_t)0x00, (uint8_t*)&msg, 4);
//printf("command = %d, arg 0=%d, 1=%d, 2=%d\n", msg.command, msg.arg0, msg.arg1, msg.arg2);
spi_read_blocking(SPI_PORT, _output->get_channel_led_state(), (uint8_t*)&msg, 4);
// printf("command = %d, arg 0=%d, 1=%d, 2=%d\n", msg.command, msg.arg0, msg.arg1, msg.arg2);
switch ((command)msg.command)
{
case command::Pulse:
Expand All @@ -55,7 +54,7 @@ void CMessageProcess::loop()
set_freq(msg);
break;

case command::SetPulseWitdh:
case command::SetPulseWidth:
set_pulse_width(msg);
break;

Expand All @@ -67,6 +66,15 @@ void CMessageProcess::loop()
off(msg);
break;

case command::NoOp:
// Sent so we can send LED states
break;

case command::SetTestVal:
// Used so zc95 can test that SPI comms are working (at least to some extent) on startup. It'll use i2c to read this value back.
_i2c_slave->set_value((uint8_t)CI2cSlave::reg::TestVal, msg.arg0);
break;

default:
printf("ERR: command = %d, arg 0=%d, 1=%d, 2=%d\n", msg.command, msg.arg0, msg.arg1, msg.arg2);
break;
Expand Down Expand Up @@ -114,7 +122,7 @@ void CMessageProcess::set_freq(message msg)
_output->set_freq(msg.arg0, freq);
}

// SetPulseWitdh - set pulse width generated if SwitchOn used
// SetPulseWidth - set pulse width generated if SwitchOn used
// Args:
// 0 = channel (0-3)
// 1 = pos pulse width (us)
Expand Down
15 changes: 10 additions & 5 deletions source/zc624/CMessageProcess.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include "config.h"
#include "COutput.h"
#include "CI2cSlave.h"

class CMessageProcess
{
Expand All @@ -18,18 +19,20 @@ class CMessageProcess

enum class command
{
Pulse = 0,
SetPower = 1,
Poll = 2,
PowerDown = 3,

SetFreq = 4,
SetPulseWitdh = 5,
SetPulseWidth = 5,
SwitchOn = 6,
SwitchOff = 7
SwitchOff = 7,
NoOp = 8,
SetTestVal = 9,
Pulse = 10
};

CMessageProcess(COutput *output);
CMessageProcess(COutput *output, CI2cSlave *i2c_slave);
~CMessageProcess();
void init();

Expand All @@ -44,6 +47,8 @@ class CMessageProcess
void on(message msg);
void off(message msg);
void power_down(message msg);

CI2cSlave *_i2c_slave;
};

#endif
22 changes: 19 additions & 3 deletions source/zc624/COutput.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ COutput::COutput(PIO pio, CI2cSlave *i2c_slave)
_channel[3] = new COutputChannel(PIN_CHAN4_GATE_A, _pio, 3, _pio_program_offset, 1, &_dac, CDac::dac_channel::D, _pulse_queue);

gpio_put(PIN_9V_ENABLE, 1);
sleep_ms(100); // wait for 9v suppy to stabalise
sleep_ms(100); // wait for 9v supply to stabilize

for (int chan=0; chan < 4; chan++)
_channel[chan]->calibrate();
Expand Down Expand Up @@ -59,7 +59,6 @@ COutput::COutput(PIO pio, CI2cSlave *i2c_slave)
printf("One or more chanel failed calibration, not enabling power.\n");
_i2c_slave->set_value((uint8_t)CI2cSlave::reg::OverallStatus, CI2cSlave::status::Fault);
gpio_put(PIN_9V_ENABLE, 0);

}
}

Expand Down Expand Up @@ -145,12 +144,29 @@ void COutput::loop()
}
}

uint8_t COutput::get_channel_led_state()
{
uint8_t state = 0;

for (uint8_t chan = 0; chan < CHANNEL_COUNT; chan++)
{
if (_channel[chan]->get_channel_led())
{
state |= (1 << chan);
}
}

state |= (1 << 5);

return state;
}

void COutput::power_down()
{
printf("COutput::power_down()\n");
gpio_put(PIN_9V_ENABLE, 0);

// Could probably do with an extra status. But as there's currenly no return
// Could probably do with an extra status. But as there's currently no return
// from PowerDown, it's good enough for now
_i2c_slave->set_value((uint8_t)CI2cSlave::reg::OverallStatus, CI2cSlave::status::Fault);

Expand Down
1 change: 1 addition & 0 deletions source/zc624/COutput.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class COutput
void off(uint8_t channel);
void loop();
void power_down();
uint8_t get_channel_led_state();

private:
bool is_channel_valid(uint8_t channel);
Expand Down
15 changes: 13 additions & 2 deletions source/zc624/COutputChannel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -239,20 +239,31 @@ void COutputChannel::do_pulse(uint8_t pos_us, uint8_t neg_us)
if (!pio_sm_is_tx_fifo_full(_pio, _sm))
{
pio_sm_put_blocking(_pio, _sm, val);
channel_led_on();
}
else
{
printf("Full queue (%d)\n", _sm);
}
}

void COutputChannel::channel_led_on()
{
_channel_led_off_time_us = time_us_64() + (10 * 1000); // 10ms
}

COutputChannel::status COutputChannel::get_status()
{
return _status;
}

// test the PFET, by starting from the highist DAC setting which should turn the PFET fully off, and
// runing through to 0, which should be fully on. Output the ADC reading for each DAC setting in a format
bool COutputChannel::get_channel_led()
{
return _channel_led_off_time_us > time_us_64();
}

// test the PFET, by starting from the highest DAC setting which should turn the PFET fully off, and
// running through to 0, which should be fully on. Output the ADC reading for each DAC setting in a format
// that can be easily imported into a spreadsheet for graphing. It should be a nice smooth curve starting
// at around dac=3200 and leveling off around dac=1400
void COutputChannel::diag_run_dac_sweep()
Expand Down
Loading

0 comments on commit bc8a0e9

Please sign in to comment.